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
c1871a62
Unverified
Commit
c1871a62
authored
Mar 06, 2018
by
Lucio Maciel
Committed by
GitHub
Mar 06, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #796 from RocketChat/release/2.0.0-beta7
[RELEASE] 2.0.0 beta 7
parents
fd036b3c
a639df30
Changes
65
Show whitespace changes
Inline
Side-by-side
Showing
65 changed files
with
1537 additions
and
356 deletions
+1537
-356
build.gradle
app/build.gradle
+4
-2
LoginPresenter.kt
...droid/authentication/login/presentation/LoginPresenter.kt
+28
-12
AudioAttachmentViewHolder.kt
...ket/android/chatroom/adapter/AudioAttachmentViewHolder.kt
+5
-1
BaseViewHolder.kt
...va/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
+50
-1
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+33
-5
ImageAttachmentViewHolder.kt
...ket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
+10
-1
MessageViewHolder.kt
...chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
+5
-54
UrlPreviewViewHolder.kt
...t/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
+9
-1
VideoAttachmentViewHolder.kt
...ket/android/chatroom/adapter/VideoAttachmentViewHolder.kt
+5
-1
ChatRoomFragmentModule.kt
...chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
+5
-0
ChatRoomNavigator.kt
...rocket/android/chatroom/presentation/ChatRoomNavigator.kt
+15
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+72
-58
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+3
-0
PinnedMessagesPresenter.kt
.../android/chatroom/presentation/PinnedMessagesPresenter.kt
+2
-3
ChatRoomActivity.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
+26
-10
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+52
-4
AudioAttachmentViewModel.kt
...et/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
+2
-0
BaseViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
+2
-0
ImageAttachmentViewModel.kt
...et/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
+2
-0
MessageViewModel.kt
...hat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
+2
-2
UrlPreviewViewModel.kt
.../rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
+2
-0
VideoAttachmentViewModel.kt
...et/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
+2
-0
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+49
-29
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+102
-85
ChatRoomsView.kt
...at/rocket/android/chatrooms/presentation/ChatRoomsView.kt
+3
-0
ChatRoomsAdapter.kt
...java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
+13
-10
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+51
-8
ActivityBuilder.kt
...java/chat/rocket/android/dagger/module/ActivityBuilder.kt
+2
-1
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+19
-1
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+12
-1
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+8
-0
MemberBottomSheetFragment.kt
...hat/rocket/android/member/ui/MemberBottomSheetFragment.kt
+80
-0
MembersAdapter.kt
...ava/chat/rocket/android/members/adapter/MembersAdapter.kt
+42
-0
MembersFragmentModule.kt
...a/chat/rocket/android/members/di/MembersFragmentModule.kt
+35
-0
MembersFragmentProvider.kt
...chat/rocket/android/members/di/MembersFragmentProvider.kt
+12
-0
MembersNavigator.kt
...t/rocket/android/members/presentation/MembersNavigator.kt
+14
-0
MembersPresenter.kt
...t/rocket/android/members/presentation/MembersPresenter.kt
+53
-0
MembersView.kt
...a/chat/rocket/android/members/presentation/MembersView.kt
+16
-0
MembersFragment.kt
...in/java/chat/rocket/android/members/ui/MembersFragment.kt
+108
-0
MemberViewModel.kt
.../chat/rocket/android/members/viewmodel/MemberViewModel.kt
+46
-0
MemberViewModelMapper.kt
...rocket/android/members/viewmodel/MemberViewModelMapper.kt
+17
-0
SettingsRepository.kt
...a/chat/rocket/android/server/domain/SettingsRepository.kt
+19
-18
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+155
-0
ConnectionManagerFactory.kt
...ndroid/server/infraestructure/ConnectionManagerFactory.kt
+22
-0
RocketChatClientFactory.kt
...android/server/infraestructure/RocketChatClientFactory.kt
+1
-1
SharedPreferencesSettingsRepository.kt
...er/infraestructure/SharedPreferencesSettingsRepository.kt
+3
-3
Animation.kt
...ain/java/chat/rocket/android/util/extensions/Animation.kt
+12
-2
Text.kt
...src/main/java/chat/rocket/android/util/extensions/Text.kt
+3
-0
Ui.kt
app/src/main/java/chat/rocket/android/util/extensions/Ui.kt
+12
-1
ic_gitlab.xml
app/src/main/res/drawable/ic_gitlab.xml
+72
-32
emoji_popup_layout.xml
app/src/main/res/layout/emoji_popup_layout.xml
+1
-1
fragment_authentication_server.xml
app/src/main/res/layout/fragment_authentication_server.xml
+2
-2
fragment_chat_room.xml
app/src/main/res/layout/fragment_chat_room.xml
+17
-1
fragment_chat_rooms.xml
app/src/main/res/layout/fragment_chat_rooms.xml
+15
-0
fragment_member_bottom_sheet.xml
app/src/main/res/layout/fragment_member_bottom_sheet.xml
+88
-0
fragment_members.xml
app/src/main/res/layout/fragment_members.xml
+24
-0
fragment_profile.xml
app/src/main/res/layout/fragment_profile.xml
+1
-1
item_member.xml
app/src/main/res/layout/item_member.xml
+31
-0
message_attachment.xml
app/src/main/res/layout/message_attachment.xml
+2
-2
message_url_preview.xml
app/src/main/res/layout/message_url_preview.xml
+2
-2
chatroom_actions.xml
app/src/main/res/menu/chatroom_actions.xml
+5
-0
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+13
-0
colors.xml
app/src/main/res/values/colors.xml
+2
-0
strings.xml
app/src/main/res/values/strings.xml
+14
-0
dependencies.gradle
dependencies.gradle
+3
-0
No files found.
app/build.gradle
View file @
c1871a62
...
@@ -12,8 +12,8 @@ android {
...
@@ -12,8 +12,8 @@ android {
applicationId
"chat.rocket.android"
applicationId
"chat.rocket.android"
minSdkVersion
21
minSdkVersion
21
targetSdkVersion
versions
.
targetSdk
targetSdkVersion
versions
.
targetSdk
versionCode
10
09
versionCode
10
11
versionName
"2.0.0-dev
7
"
versionName
"2.0.0-dev
9
"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
multiDexEnabled
true
}
}
...
@@ -60,6 +60,8 @@ dependencies {
...
@@ -60,6 +60,8 @@ dependencies {
implementation
libraries
.
constraintLayout
implementation
libraries
.
constraintLayout
implementation
libraries
.
cardView
implementation
libraries
.
cardView
implementation
libraries
.
androidKtx
implementation
libraries
.
dagger
implementation
libraries
.
dagger
implementation
libraries
.
daggerSupport
implementation
libraries
.
daggerSupport
kapt
libraries
.
daggerProcessor
kapt
libraries
.
daggerProcessor
...
...
app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt
View file @
c1871a62
...
@@ -7,14 +7,14 @@ import chat.rocket.android.helper.NetworkHelper
...
@@ -7,14 +7,14 @@ import chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.isEmailValid
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatTwoFactorException
import
chat.rocket.common.RocketChatTwoFactorException
import
chat.rocket.common.model.Token
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.login
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.registerPushToken
import
javax.inject.Inject
import
javax.inject.Inject
class
LoginPresenter
@Inject
constructor
(
private
val
view
:
LoginView
,
class
LoginPresenter
@Inject
constructor
(
private
val
view
:
LoginView
,
...
@@ -93,15 +93,31 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
...
@@ -93,15 +93,31 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view
.
showLoading
()
view
.
showLoading
()
try
{
try
{
val
token
=
client
.
login
(
usernameOrEmail
,
password
)
var
token
:
Token
?
=
null
if
(
usernameOrEmail
.
isEmailValid
())
{
token
=
client
.
loginWithEmail
(
usernameOrEmail
,
password
)
}
else
{
val
settings
=
settingsInteractor
.
get
(
server
)
if
(
settings
!=
null
)
{
token
=
if
(
settings
.
ldapEnabled
())
{
client
.
loginWithLdap
(
usernameOrEmail
,
password
)
}
else
{
client
.
login
(
usernameOrEmail
,
password
)
}
}
else
{
navigator
.
toServerScreen
()
}
}
if
(
token
!=
null
)
{
val
me
=
client
.
me
()
val
me
=
client
.
me
()
multiServerRepository
.
save
(
multiServerRepository
.
save
(
server
,
TokenModel
(
token
.
userId
,
token
.
authToken
))
server
,
TokenModel
(
token
.
userId
,
token
.
authToken
)
)
localRepository
.
save
(
LocalRepository
.
USERNAME_KEY
,
me
.
username
)
localRepository
.
save
(
LocalRepository
.
USERNAME_KEY
,
me
.
username
)
registerPushToken
()
registerPushToken
()
navigator
.
toChatList
()
navigator
.
toChatList
()
}
else
{
view
.
showGenericErrorMessage
()
}
}
catch
(
exception
:
RocketChatException
)
{
}
catch
(
exception
:
RocketChatException
)
{
when
(
exception
)
{
when
(
exception
)
{
is
RocketChatTwoFactorException
->
{
is
RocketChatTwoFactorException
->
{
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/AudioAttachmentViewHolder.kt
View file @
c1871a62
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
AudioAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
AudioAttachmentViewModel
>(
itemView
)
{
class
AudioAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
AudioAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
init
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setVisible
(
false
)
image_attachment
.
setVisible
(
false
)
audio_video_attachment
.
setVisible
(
true
)
audio_video_attachment
.
setVisible
(
true
)
setupActionMenu
(
attachment_container
)
setupActionMenu
(
audio_video_attachment
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.support.v7.widget.RecyclerView
import
android.support.v7.widget.RecyclerView
import
android.view.MenuItem
import
android.view.View
import
android.view.View
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import
chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.isSystemMessage
import
ru.whalemare.sheetmenu.extension.inflate
import
ru.whalemare.sheetmenu.extension.toList
abstract
class
BaseViewHolder
<
T
>(
itemView
:
View
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
abstract
class
BaseViewHolder
<
T
:
BaseViewModel
<*>>(
itemView
:
View
,
private
val
listener
:
ActionsListener
)
:
RecyclerView
.
ViewHolder
(
itemView
),
MenuItem
.
OnMenuItemClickListener
{
var
data
:
T
?
=
null
var
data
:
T
?
=
null
init
{
setupActionMenu
(
itemView
)
}
fun
bind
(
data
:
T
)
{
fun
bind
(
data
:
T
)
{
this
.
data
=
data
this
.
data
=
data
bindViews
(
data
)
bindViews
(
data
)
}
}
abstract
fun
bindViews
(
data
:
T
)
abstract
fun
bindViews
(
data
:
T
)
interface
ActionsListener
{
fun
isActionsEnabled
():
Boolean
fun
onActionSelected
(
item
:
MenuItem
,
message
:
Message
)
}
val
longClickListener
=
{
view
:
View
->
if
(
data
?.
message
?.
isSystemMessage
()
==
false
)
{
val
menuItems
=
view
.
context
.
inflate
(
R
.
menu
.
message_actions
).
toList
()
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_menu_msg_pin_unpin
}
?.
apply
{
val
isPinned
=
data
?.
message
?.
pinned
?:
false
setTitle
(
if
(
isPinned
)
R
.
string
.
action_msg_unpin
else
R
.
string
.
action_msg_pin
)
isChecked
=
isPinned
}
val
adapter
=
ActionListAdapter
(
menuItems
,
this
@BaseViewHolder
)
BottomSheetMenu
(
adapter
).
show
(
view
.
context
)
}
true
}
internal
fun
setupActionMenu
(
view
:
View
)
{
if
(
listener
.
isActionsEnabled
())
{
view
.
setOnLongClickListener
(
longClickListener
)
}
}
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
{
data
?.
let
{
listener
.
onActionSelected
(
item
,
it
.
message
)
}
return
true
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.support.v7.widget.RecyclerView
import
android.support.v7.widget.RecyclerView
import
android.view.MenuItem
import
android.view.ViewGroup
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.viewmodel.*
import
chat.rocket.android.chatroom.viewmodel.*
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.core.model.Message
import
timber.log.Timber
import
timber.log.Timber
import
java.security.InvalidParameterException
import
java.security.InvalidParameterException
...
@@ -26,23 +28,23 @@ class ChatRoomAdapter(
...
@@ -26,23 +28,23 @@ class ChatRoomAdapter(
return
when
(
viewType
.
toViewType
())
{
return
when
(
viewType
.
toViewType
())
{
BaseViewModel
.
ViewType
.
MESSAGE
->
{
BaseViewModel
.
ViewType
.
MESSAGE
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message
)
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message
)
MessageViewHolder
(
view
,
roomName
,
roomType
,
presenter
,
enableActions
)
MessageViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
ImageAttachmentViewHolder
(
view
)
ImageAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
AudioAttachmentViewHolder
(
view
)
AudioAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
->
{
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_attachment
)
VideoAttachmentViewHolder
(
view
)
VideoAttachmentViewHolder
(
view
,
actionsListener
)
}
}
BaseViewModel
.
ViewType
.
URL_PREVIEW
->
{
BaseViewModel
.
ViewType
.
URL_PREVIEW
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
UrlPreviewViewHolder
(
view
)
UrlPreviewViewHolder
(
view
,
actionsListener
)
}
}
else
->
{
else
->
{
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
...
@@ -108,4 +110,30 @@ class ChatRoomAdapter(
...
@@ -108,4 +110,30 @@ class ChatRoomAdapter(
notifyItemRangeRemoved
(
index
,
oldSize
-
newSize
)
notifyItemRangeRemoved
(
index
,
oldSize
-
newSize
)
}
}
}
}
val
actionsListener
=
object
:
BaseViewHolder
.
ActionsListener
{
override
fun
isActionsEnabled
():
Boolean
=
enableActions
override
fun
onActionSelected
(
item
:
MenuItem
,
message
:
Message
)
{
message
.
apply
{
when
(
item
.
itemId
)
{
R
.
id
.
action_menu_msg_delete
->
presenter
?.
deleteMessage
(
roomId
,
id
)
R
.
id
.
action_menu_msg_quote
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
false
)
R
.
id
.
action_menu_msg_reply
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
true
)
R
.
id
.
action_menu_msg_copy
->
presenter
?.
copyMessage
(
id
)
R
.
id
.
action_menu_msg_edit
->
presenter
?.
editMessage
(
roomId
,
id
,
message
.
message
)
R
.
id
.
action_menu_msg_pin_unpin
->
{
with
(
item
)
{
if
(!
isChecked
)
{
presenter
?.
pinMessage
(
id
)
}
else
{
presenter
?.
unpinMessage
(
id
)
}
}
}
else
->
TODO
(
"Not implemented"
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
View file @
c1871a62
...
@@ -5,7 +5,16 @@ import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
...
@@ -5,7 +5,16 @@ import chat.rocket.android.chatroom.viewmodel.ImageAttachmentViewModel
import
com.stfalcon.frescoimageviewer.ImageViewer
import
com.stfalcon.frescoimageviewer.ImageViewer
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
ImageAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
)
{
class
ImageAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
ImageAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
with
(
itemView
)
{
setupActionMenu
(
attachment_container
)
setupActionMenu
(
image_attachment
)
}
}
override
fun
bindViews
(
data
:
ImageAttachmentViewModel
)
{
override
fun
bindViews
(
data
:
ImageAttachmentViewModel
)
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setImageURI
(
data
.
attachmentUrl
)
image_attachment
.
setImageURI
(
data
.
attachmentUrl
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.adapter
package
chat.rocket.android.chatroom.adapter
import
android.text.method.LinkMovementMethod
import
android.text.method.LinkMovementMethod
import
android.view.MenuItem
import
android.view.View
import
android.view.View
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.ui.bottomsheet.BottomSheetMenu
import
chat.rocket.android.chatroom.ui.bottomsheet.adapter.ActionListAdapter
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
import
kotlinx.android.synthetic.main.item_message.view.*
import
ru.whalemare.sheetmenu.extension.inflate
import
ru.whalemare.sheetmenu.extension.toList
class
MessageViewHolder
(
class
MessageViewHolder
(
itemView
:
View
,
itemView
:
View
,
private
val
roomType
:
String
,
listener
:
ActionsListener
private
val
roomName
:
String
,
)
:
BaseViewHolder
<
MessageViewModel
>(
itemView
,
listener
)
{
private
val
presenter
:
ChatRoomPresenter
?,
enableActions
:
Boolean
)
:
BaseViewHolder
<
MessageViewModel
>(
itemView
),
MenuItem
.
OnMenuItemClickListener
{
init
{
init
{
itemView
.
text_content
.
movementMethod
=
LinkMovementMethod
()
with
(
itemView
)
{
text_content
.
movementMethod
=
LinkMovementMethod
()
if
(
enableActions
)
{
setupActionMenu
(
text_content
)
itemView
.
setOnLongClickListener
{
if
(
data
?.
isSystemMessage
==
false
)
{
val
menuItems
=
it
.
context
.
inflate
(
R
.
menu
.
message_actions
).
toList
()
menuItems
.
find
{
it
.
itemId
==
R
.
id
.
action_menu_msg_pin_unpin
}
?.
apply
{
val
isPinned
=
data
?.
isPinned
?:
false
setTitle
(
if
(
isPinned
)
R
.
string
.
action_msg_unpin
else
R
.
string
.
action_msg_pin
)
isChecked
=
isPinned
}
val
adapter
=
ActionListAdapter
(
menuItems
,
this
@MessageViewHolder
)
BottomSheetMenu
(
adapter
).
apply
{
}.
show
(
it
.
context
)
}
true
}
}
}
}
}
...
@@ -52,27 +26,4 @@ class MessageViewHolder(
...
@@ -52,27 +26,4 @@ class MessageViewHolder(
image_avatar
.
setImageURI
(
data
.
avatar
)
image_avatar
.
setImageURI
(
data
.
avatar
)
}
}
}
}
override
fun
onMenuItemClick
(
item
:
MenuItem
):
Boolean
{
data
?.
rawData
?.
apply
{
when
(
item
.
itemId
)
{
R
.
id
.
action_menu_msg_delete
->
presenter
?.
deleteMessage
(
roomId
,
id
)
R
.
id
.
action_menu_msg_quote
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
false
)
R
.
id
.
action_menu_msg_reply
->
presenter
?.
citeMessage
(
roomType
,
roomName
,
id
,
true
)
R
.
id
.
action_menu_msg_copy
->
presenter
?.
copyMessage
(
id
)
R
.
id
.
action_menu_msg_edit
->
presenter
?.
editMessage
(
roomId
,
id
,
message
)
R
.
id
.
action_menu_msg_pin_unpin
->
{
with
(
item
)
{
if
(!
isChecked
)
{
presenter
?.
pinMessage
(
id
)
}
else
{
presenter
?.
unpinMessage
(
id
)
}
}
}
else
->
TODO
(
"Not implemented"
)
}
}
return
true
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
View file @
c1871a62
...
@@ -8,7 +8,15 @@ import chat.rocket.android.util.extensions.content
...
@@ -8,7 +8,15 @@ import chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_url_preview.view.*
import
kotlinx.android.synthetic.main.message_url_preview.view.*
class
UrlPreviewViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
UrlPreviewViewModel
>(
itemView
)
{
class
UrlPreviewViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
UrlPreviewViewModel
>(
itemView
,
listener
)
{
init
{
with
(
itemView
)
{
setupActionMenu
(
url_preview_layout
)
}
}
override
fun
bindViews
(
data
:
UrlPreviewViewModel
)
{
override
fun
bindViews
(
data
:
UrlPreviewViewModel
)
{
with
(
itemView
)
{
with
(
itemView
)
{
if
(
data
.
thumbUrl
.
isNullOrEmpty
())
{
if
(
data
.
thumbUrl
.
isNullOrEmpty
())
{
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/VideoAttachmentViewHolder.kt
View file @
c1871a62
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
...
@@ -6,11 +6,15 @@ import chat.rocket.android.player.PlayerActivity
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
import
kotlinx.android.synthetic.main.message_attachment.view.*
import
kotlinx.android.synthetic.main.message_attachment.view.*
class
VideoAttachmentViewHolder
(
itemView
:
View
)
:
BaseViewHolder
<
VideoAttachmentViewModel
>(
itemView
)
{
class
VideoAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
)
:
BaseViewHolder
<
VideoAttachmentViewModel
>(
itemView
,
listener
)
{
init
{
init
{
with
(
itemView
)
{
with
(
itemView
)
{
image_attachment
.
setVisible
(
false
)
image_attachment
.
setVisible
(
false
)
audio_video_attachment
.
setVisible
(
true
)
audio_video_attachment
.
setVisible
(
true
)
setupActionMenu
(
attachment_container
)
setupActionMenu
(
audio_video_attachment
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.di
package
chat.rocket.android.chatroom.di
import
android.arch.lifecycle.LifecycleOwner
import
android.arch.lifecycle.LifecycleOwner
import
chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import
chat.rocket.android.chatroom.presentation.ChatRoomView
import
chat.rocket.android.chatroom.presentation.ChatRoomView
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.chatroom.ui.ChatRoomFragment
import
chat.rocket.android.chatroom.ui.ChatRoomFragment
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.dagger.scope.PerFragment
...
@@ -13,6 +15,9 @@ import kotlinx.coroutines.experimental.Job
...
@@ -13,6 +15,9 @@ import kotlinx.coroutines.experimental.Job
@PerFragment
@PerFragment
class
ChatRoomFragmentModule
{
class
ChatRoomFragmentModule
{
@Provides
fun
provideChatRoomNavigator
(
activity
:
ChatRoomActivity
)
=
ChatRoomNavigator
(
activity
)
@Provides
@Provides
fun
chatRoomView
(
frag
:
ChatRoomFragment
):
ChatRoomView
{
fun
chatRoomView
(
frag
:
ChatRoomFragment
):
ChatRoomView
{
return
frag
return
frag
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomNavigator.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.chatroom.presentation
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.members.ui.newInstance
import
chat.rocket.android.util.extensions.addFragmentBackStack
class
ChatRoomNavigator
(
internal
val
activity
:
ChatRoomActivity
)
{
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
{
activity
.
addFragmentBackStack
(
"MembersFragment"
,
R
.
id
.
fragment_container
)
{
newInstance
(
chatRoomId
,
chatRoomType
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
c1871a62
...
@@ -6,41 +6,50 @@ import chat.rocket.android.chatroom.domain.UriInteractor
...
@@ -6,41 +6,50 @@ import chat.rocket.android.chatroom.domain.UriInteractor
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.realtime.connect
import
chat.rocket.core.internal.realtime.subscribeRoomMessages
import
chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.launch
import
org.threeten.bp.Instant
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
class
ChatRoomPresenter
@Inject
constructor
(
private
val
view
:
ChatRoomView
,
class
ChatRoomPresenter
@Inject
constructor
(
private
val
view
:
ChatRoomView
,
private
val
navigator
:
ChatRoomNavigator
,
private
val
strategy
:
CancelStrategy
,
private
val
strategy
:
CancelStrategy
,
getSettingsInteractor
:
GetSettingsInteractor
,
getSettingsInteractor
:
GetSettingsInteractor
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
permissions
:
GetPermissionsInteractor
,
private
val
permissions
:
GetPermissionsInteractor
,
private
val
uriInteractor
:
UriInteractor
,
private
val
uriInteractor
:
UriInteractor
,
private
val
messagesRepository
:
MessagesRepository
,
private
val
messagesRepository
:
MessagesRepository
,
factory
:
RocketChatClient
Factory
,
factory
:
ConnectionManager
Factory
,
private
val
mapper
:
ViewModelMapper
)
{
private
val
mapper
:
ViewModelMapper
)
{
private
val
client
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
var
subId
:
String
?
=
null
private
val
manager
=
factory
.
create
(
currentServer
)
private
val
client
=
manager
.
client
private
var
settings
:
Map
<
String
,
Value
<
Any
>>
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
!!
private
var
settings
:
Map
<
String
,
Value
<
Any
>>
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
!!
private
val
messagesChannel
=
Channel
<
Message
>()
private
var
chatRoomId
:
String
?
=
null
private
var
chatRoomType
:
String
?
=
null
private
val
stateChannel
=
Channel
<
State
>()
private
val
stateChannel
=
Channel
<
State
>()
private
var
lastState
=
manager
.
state
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
this
.
chatRoomId
=
chatRoomId
this
.
chatRoomType
=
chatRoomType
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
view
.
showLoading
()
view
.
showLoading
()
try
{
try
{
...
@@ -55,10 +64,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -55,10 +64,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val
messagesViewModels
=
mapper
.
map
(
messages
)
val
messagesViewModels
=
mapper
.
map
(
messages
)
view
.
showMessages
(
messagesViewModels
)
view
.
showMessages
(
messagesViewModels
)
// Subscribe after getting the first page of messages from REST
if
(
offset
==
0L
)
{
subscribeMessages
(
chatRoomId
)
subscribeMessages
(
chatRoomId
)
}
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
ex
.
printStackTrace
()
ex
.
printStackTrace
()
ex
.
message
?.
let
{
ex
.
message
?.
let
{
...
@@ -69,6 +75,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -69,6 +75,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
finally
{
}
finally
{
view
.
hideLoading
()
view
.
hideLoading
()
}
}
if
(
offset
==
0L
)
{
subscribeState
()
}
}
}
}
}
...
@@ -131,7 +141,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -131,7 +141,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
}
fun
markRoomAsRead
(
roomId
:
String
)
{
private
fun
markRoomAsRead
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
try
{
try
{
client
.
markAsRead
(
roomId
)
client
.
markAsRead
(
roomId
)
...
@@ -142,55 +152,68 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -142,55 +152,68 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
}
private
fun
subscribeMessages
(
roomId
:
String
)
{
private
fun
subscribeState
()
{
client
.
addStateChannel
(
stateChannel
)
Timber
.
d
(
"Subscribing to Status changes"
)
lastState
=
manager
.
state
manager
.
addStatusChannel
(
stateChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
status
in
stateChannel
)
{
for
(
state
in
stateChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
Timber
.
d
(
"Got new state: $state - last: $lastState"
)
when
(
status
)
{
if
(
state
!=
lastState
)
{
is
State
.
Authenticating
->
Timber
.
d
(
"Authenticating"
)
launch
(
UI
)
{
is
State
.
Connected
->
{
view
.
showConnectionState
(
state
)
Timber
.
d
(
"Connected"
)
}
subId
=
client
.
subscribeRoomMessages
(
roomId
)
{
success
,
_
->
Timber
.
d
(
"subscribe messages for $roomId: $success"
)
if
(
state
is
State
.
Connected
)
{
loadMissingMessages
()
}
}
}
}
lastState
=
state
}
}
}
}
Timber
.
d
(
"Done on statusChannel"
)
}
}
when
(
client
.
state
)
{
private
fun
subscribeMessages
(
roomId
:
String
)
{
is
State
.
Connected
->
{
manager
.
subscribeRoomMessages
(
roomId
,
messagesChannel
)
Timber
.
d
(
"Already connected"
)
subId
=
client
.
subscribeRoomMessages
(
roomId
)
{
success
,
_
->
launch
(
CommonPool
+
strategy
.
jobs
)
{
Timber
.
d
(
"subscribe messages for $roomId: $success"
)
for
(
message
in
messagesChannel
)
{
Timber
.
d
(
"New message for room ${message.roomId}"
)
updateMessage
(
message
)
}
}
}
}
else
->
client
.
connect
()
}
}
private
fun
loadMissingMessages
()
{
launch
(
parent
=
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
chatRoomType
!=
null
)
{
val
roomType
=
roomTypeOf
(
chatRoomType
!!
)
val
lastMessage
=
messagesRepository
.
getByRoomId
(
chatRoomId
!!
).
sortedByDescending
{
it
.
timestamp
}.
first
()
val
instant
=
Instant
.
ofEpochMilli
(
lastMessage
.
timestamp
)
val
messages
=
client
.
history
(
chatRoomId
!!
,
roomType
,
count
=
50
,
oldest
=
instant
.
toString
())
Timber
.
d
(
"History: $messages"
)
if
(
messages
.
result
.
isNotEmpty
())
{
val
models
=
mapper
.
map
(
messages
.
result
)
messagesRepository
.
saveAll
(
messages
.
result
)
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
listenMessages
(
roomId
)
view
.
showNewMessage
(
models
)
}
}
// TODO - when we have a proper service, we won't need to take care of connection, just
if
(
messages
.
result
.
size
==
50
)
{
// subscribe and listen...
// we loade at least count messages, try one more to fetch more messages
/*launchUI(strategy) {
loadMissingMessages
()
subId = client.subscribeRoomMessages(roomId) {
}
Timber.d("subscribe messages for $roomId: $it")
}
}
listenMessages(roomId)
}*/
}
}
fun
unsubscribeMessages
()
{
launch
(
CommonPool
)
{
client
.
removeStateChannel
(
stateChannel
)
subId
?.
let
{
subscriptionId
->
client
.
unsubscribe
(
subscriptionId
)
}
}
}
}
fun
unsubscribeMessages
(
chatRoomId
:
String
)
{
manager
.
removeStatusChannel
(
stateChannel
)
manager
.
unsubscribeRoomMessages
(
chatRoomId
)
}
}
/**
/**
...
@@ -317,16 +340,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
...
@@ -317,16 +340,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
}
}
}
}
private
suspend
fun
listenMessages
(
roomId
:
String
)
{
fun
toMembersList
(
chatRoomId
:
String
,
chatRoomType
:
String
)
=
navigator
.
toMembersList
(
chatRoomId
,
chatRoomType
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
messagesChannel
)
{
if
(
message
.
roomId
!=
roomId
)
{
Timber
.
d
(
"Ignoring message for room ${message.roomId}, expecting $roomId"
)
}
updateMessage
(
message
)
}
}
}
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
c1871a62
...
@@ -4,6 +4,7 @@ import android.net.Uri
...
@@ -4,6 +4,7 @@ import android.net.Uri
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.State
interface
ChatRoomView
:
LoadingView
,
MessageView
{
interface
ChatRoomView
:
LoadingView
,
MessageView
{
...
@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView {
...
@@ -97,4 +98,6 @@ interface ChatRoomView : LoadingView, MessageView {
fun
clearMessageComposition
()
fun
clearMessageComposition
()
fun
showInvalidFileSize
(
fileSize
:
Int
,
maxFileSize
:
Int
)
fun
showInvalidFileSize
(
fileSize
:
Int
,
maxFileSize
:
Int
)
fun
showConnectionState
(
state
:
State
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/presentation/PinnedMessagesPresenter.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.presentation
package
chat.rocket.android.chatroom.presentation
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.GetChatRoomsInteractor
import
chat.rocket.android.server.domain.GetChatRoomsInteractor
...
@@ -12,6 +11,7 @@ import chat.rocket.common.RocketChatException
...
@@ -12,6 +11,7 @@ import chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.rest.getRoomPinnedMessages
import
chat.rocket.core.internal.rest.getRoomPinnedMessages
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.isSystemMessage
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -42,8 +42,7 @@ class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessag
...
@@ -42,8 +42,7 @@ class PinnedMessagesPresenter @Inject constructor(private val view: PinnedMessag
val
pinnedMessages
=
val
pinnedMessages
=
client
.
getRoomPinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
client
.
getRoomPinnedMessages
(
roomId
,
room
.
type
,
pinnedMessagesListOffset
)
pinnedMessagesListOffset
=
pinnedMessages
.
offset
.
toInt
()
pinnedMessagesListOffset
=
pinnedMessages
.
offset
.
toInt
()
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
)
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
.
filterNot
{
it
.
isSystemMessage
()
})
.
filter
{
it
is
MessageViewModel
}.
filterNot
{
(
it
as
MessageViewModel
).
isSystemMessage
}
view
.
showPinnedMessages
(
messageList
)
view
.
showPinnedMessages
(
messageList
)
view
.
hideLoading
()
view
.
hideLoading
()
}.
ifNull
{
}.
ifNull
{
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomActivity.kt
View file @
c1871a62
...
@@ -8,9 +8,12 @@ import android.os.Bundle
...
@@ -8,9 +8,12 @@ import android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.util.extensions.addFragment
import
chat.rocket.android.util.extensions.addFragment
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjector
import
dagger.android.AndroidInjector
import
dagger.android.DispatchingAndroidInjector
import
dagger.android.DispatchingAndroidInjector
...
@@ -35,6 +38,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
...
@@ -35,6 +38,11 @@ private const val INTENT_IS_CHAT_ROOM_READ_ONLY = "is_chat_room_read_only"
class
ChatRoomActivity
:
AppCompatActivity
(),
HasSupportFragmentInjector
{
class
ChatRoomActivity
:
AppCompatActivity
(),
HasSupportFragmentInjector
{
@Inject
lateinit
var
fragmentDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Fragment
>
@Inject
lateinit
var
fragmentDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Fragment
>
// TODO - workaround for now... We will move to a single activity
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
managerFactory
:
ConnectionManagerFactory
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomName
:
String
private
lateinit
var
chatRoomType
:
String
private
lateinit
var
chatRoomType
:
String
...
@@ -45,6 +53,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
...
@@ -45,6 +53,9 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_chat_room
)
setContentView
(
R
.
layout
.
activity_chat_room
)
// Workaround for when we are coming to the app via the recents app and the app was killed.
managerFactory
.
create
(
serverInteractor
.
get
()
!!
).
connect
()
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
requireNotNull
(
chatRoomId
)
{
"no chat_room_id provided in Intent extras"
}
requireNotNull
(
chatRoomId
)
{
"no chat_room_id provided in Intent extras"
}
...
@@ -57,7 +68,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
...
@@ -57,7 +68,7 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
isChatRoomReadOnly
=
intent
.
getBooleanExtra
(
INTENT_IS_CHAT_ROOM_READ_ONLY
,
true
)
isChatRoomReadOnly
=
intent
.
getBooleanExtra
(
INTENT_IS_CHAT_ROOM_READ_ONLY
,
true
)
requireNotNull
(
chatRoomType
)
{
"no is_chat_room_read_only provided in Intent extras"
}
requireNotNull
(
chatRoomType
)
{
"no is_chat_room_read_only provided in Intent extras"
}
setupToolbar
(
chatRoomName
)
setupToolbar
()
addFragment
(
"ChatRoomFragment"
,
R
.
id
.
fragment_container
)
{
addFragment
(
"ChatRoomFragment"
,
R
.
id
.
fragment_container
)
{
newInstance
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
isChatRoomReadOnly
)
newInstance
(
chatRoomId
,
chatRoomName
,
chatRoomType
,
isChatRoomReadOnly
)
...
@@ -72,22 +83,23 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
...
@@ -72,22 +83,23 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
return
fragmentDispatchingAndroidInjector
return
fragmentDispatchingAndroidInjector
}
}
private
fun
setupToolbar
(
chatRoomName
:
String
)
{
private
fun
setupToolbar
()
{
setSupportActionBar
(
toolbar
)
setSupportActionBar
(
toolbar
)
supportActionBar
?.
setDisplayShowTitleEnabled
(
false
)
supportActionBar
?.
setDisplayShowTitleEnabled
(
false
)
text_room_name
.
textContent
=
chatRoomName
text_room_name
.
textContent
=
chatRoomName
va
r
drawable
:
Drawable
?
=
null
va
l
roomType
=
roomTypeOf
(
chatRoomType
)
when
(
chatR
oomType
)
{
val
drawable
=
when
(
r
oomType
)
{
RoomType
.
CHANNEL
.
toString
()
->
{
is
RoomType
.
Channel
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_channel
,
this
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_channel
,
this
)
}
}
RoomType
.
PRIVATE_GROUP
.
toString
()
->
{
is
RoomType
.
PrivateGroup
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_lock
,
this
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_lock
,
this
)
}
}
RoomType
.
DIRECT_MESSAGE
.
toString
()
->
{
is
RoomType
.
DirectMessage
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_dm
,
this
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_dm
,
this
)
}
}
else
->
null
}
}
drawable
?.
let
{
drawable
?.
let
{
...
@@ -102,6 +114,10 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
...
@@ -102,6 +114,10 @@ class ChatRoomActivity : AppCompatActivity(), HasSupportFragmentInjector {
}
}
}
}
fun
setupToolbarTitle
(
toolbarTitle
:
String
)
{
text_room_name
.
textContent
=
toolbarTitle
}
private
fun
finishActivity
()
{
private
fun
finishActivity
()
{
super
.
onBackPressed
()
super
.
onBackPressed
()
overridePendingTransition
(
R
.
anim
.
close_enter
,
R
.
anim
.
close_exit
)
overridePendingTransition
(
R
.
anim
.
close_enter
,
R
.
anim
.
close_exit
)
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
c1871a62
...
@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText
...
@@ -27,6 +27,7 @@ import chat.rocket.android.widget.emoji.ComposerEditText
import
chat.rocket.android.widget.emoji.Emoji
import
chat.rocket.android.widget.emoji.Emoji
import
chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import
chat.rocket.android.widget.emoji.EmojiKeyboardPopup
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.core.internal.realtime.State
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.CompositeDisposable
import
kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.fragment_chat_room.*
...
@@ -98,6 +99,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -98,6 +99,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
super
.
onViewCreated
(
view
,
savedInstanceState
)
setupToolbar
(
chatRoomName
)
presenter
.
loadMessages
(
chatRoomId
,
chatRoomType
)
presenter
.
loadMessages
(
chatRoomId
,
chatRoomType
)
setupRecyclerView
()
setupRecyclerView
()
...
@@ -112,7 +115,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -112,7 +115,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
}
}
override
fun
onDestroyView
()
{
override
fun
onDestroyView
()
{
presenter
.
unsubscribeMessages
()
presenter
.
unsubscribeMessages
(
chatRoomId
)
handler
.
removeCallbacksAndMessages
(
null
)
handler
.
removeCallbacksAndMessages
(
null
)
unsubscribeTextMessage
()
unsubscribeTextMessage
()
super
.
onDestroyView
()
super
.
onDestroyView
()
...
@@ -133,6 +136,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -133,6 +136,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
override
fun
onOptionsItemSelected
(
item
:
MenuItem
):
Boolean
{
when
(
item
.
itemId
)
{
when
(
item
.
itemId
)
{
R
.
id
.
action_members_list
->
{
presenter
.
toMembersList
(
chatRoomId
,
chatRoomType
)
}
R
.
id
.
action_pinned_messages
->
{
R
.
id
.
action_pinned_messages
->
{
val
intent
=
Intent
(
activity
,
PinnedMessagesActivity
::
class
.
java
).
apply
{
val
intent
=
Intent
(
activity
,
PinnedMessagesActivity
::
class
.
java
).
apply
{
putExtra
(
BUNDLE_CHAT_ROOM_ID
,
chatRoomId
)
putExtra
(
BUNDLE_CHAT_ROOM_ID
,
chatRoomId
)
...
@@ -220,6 +226,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -220,6 +226,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
.
title
=
username
actionSnackbar
.
title
=
username
actionSnackbar
.
text
=
quotedMessage
actionSnackbar
.
text
=
quotedMessage
actionSnackbar
.
show
()
actionSnackbar
.
show
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
if
(!
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
}
}
}
}
}
}
...
@@ -247,6 +259,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -247,6 +259,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
.
show
()
actionSnackbar
.
show
()
text_message
.
textContent
=
text
text_message
.
textContent
=
text
editingMessageId
=
messageId
editingMessageId
=
messageId
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
}
}
}
}
...
@@ -283,6 +296,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -283,6 +296,28 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
showMessage
(
getString
(
R
.
string
.
max_file_size_exceeded
,
fileSize
,
maxFileSize
))
showMessage
(
getString
(
R
.
string
.
max_file_size_exceeded
,
fileSize
,
maxFileSize
))
}
}
override
fun
showConnectionState
(
state
:
State
)
{
activity
?.
apply
{
connection_status_text
.
fadeIn
()
handler
.
removeCallbacks
(
dismissStatus
)
when
(
state
)
{
is
State
.
Connected
->
{
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connected
)
handler
.
postDelayed
(
dismissStatus
,
2000
)
}
is
State
.
Disconnected
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
}
}
}
private
val
dismissStatus
=
{
connection_status_text
.
fadeOut
()
}
private
fun
setupRecyclerView
()
{
private
fun
setupRecyclerView
()
{
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
...
@@ -310,11 +345,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -310,11 +345,21 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
text_room_is_read_only
.
setVisible
(
true
)
text_room_is_read_only
.
setVisible
(
true
)
input_container
.
setVisible
(
false
)
input_container
.
setVisible
(
false
)
}
else
{
}
else
{
button_send
.
alpha
=
0f
button_send
.
setVisible
(
false
)
button_show_attachment_options
.
alpha
=
1f
button_show_attachment_options
.
setVisible
(
true
)
subscribeTextMessage
()
subscribeTextMessage
()
emojiKeyboardPopup
=
EmojiKeyboardPopup
(
activity
!!
,
activity
!!
.
findViewById
(
R
.
id
.
fragment_container
))
emojiKeyboardPopup
=
EmojiKeyboardPopup
(
activity
!!
,
activity
!!
.
findViewById
(
R
.
id
.
fragment_container
))
emojiKeyboardPopup
.
listener
=
this
emojiKeyboardPopup
.
listener
=
this
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
override
fun
onKeyboardOpened
()
{
override
fun
onKeyboardOpened
()
{
if
(
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
}
}
}
}
override
fun
onKeyboardClosed
()
{
override
fun
onKeyboardClosed
()
{
...
@@ -390,6 +435,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -390,6 +435,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
actionSnackbar
=
ActionSnackbar
.
make
(
message_list_container
,
parser
=
parser
)
actionSnackbar
=
ActionSnackbar
.
make
(
message_list_container
,
parser
=
parser
)
actionSnackbar
.
cancelView
.
setOnClickListener
({
actionSnackbar
.
cancelView
.
setOnClickListener
({
clearMessageComposition
()
clearMessageComposition
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
})
})
}
}
...
@@ -401,9 +447,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -401,9 +447,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
}
}
private
fun
unsubscribeTextMessage
()
{
private
fun
unsubscribeTextMessage
()
{
if
(!
compositeDisposable
.
isDisposed
)
{
compositeDisposable
.
clear
()
compositeDisposable
.
dispose
()
}
}
}
private
fun
setupComposeMessageButtons
(
charSequence
:
CharSequence
)
{
private
fun
setupComposeMessageButtons
(
charSequence
:
CharSequence
)
{
...
@@ -435,4 +479,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
...
@@ -435,4 +479,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardPopup.Listener {
view_dim
.
setVisible
(
false
)
view_dim
.
setVisible
(
false
)
}
}
private
fun
setupToolbar
(
toolbarTitle
:
String
)
{
(
activity
as
ChatRoomActivity
).
setupToolbarTitle
(
toolbarTitle
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.AudioAttachment
import
chat.rocket.core.model.attachment.AudioAttachment
data class
AudioAttachmentViewModel
(
data class
AudioAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
AudioAttachment
,
override
val
rawData
:
AudioAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.core.model.Message
import
java.security.InvalidParameterException
import
java.security.InvalidParameterException
interface
BaseViewModel
<
out
T
>
{
interface
BaseViewModel
<
out
T
>
{
val
message
:
Message
val
rawData
:
T
val
rawData
:
T
val
messageId
:
String
val
messageId
:
String
val
viewType
:
Int
val
viewType
:
Int
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.ImageAttachment
import
chat.rocket.core.model.attachment.ImageAttachment
data class
ImageAttachmentViewModel
(
data class
ImageAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
ImageAttachment
,
override
val
rawData
:
ImageAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
View file @
c1871a62
...
@@ -4,14 +4,14 @@ import chat.rocket.android.R
...
@@ -4,14 +4,14 @@ import chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
data class
MessageViewModel
(
data class
MessageViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
Message
,
override
val
rawData
:
Message
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
avatar
:
String
,
override
val
avatar
:
String
,
override
val
time
:
CharSequence
,
override
val
time
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
isPinned
:
Boolean
,
override
val
isPinned
:
Boolean
val
isSystemMessage
:
Boolean
)
:
BaseMessageViewModel
<
Message
>
{
)
:
BaseMessageViewModel
<
Message
>
{
override
val
viewType
:
Int
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.url.Url
import
chat.rocket.core.model.url.Url
data class
UrlPreviewViewModel
(
data class
UrlPreviewViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
Url
,
override
val
rawData
:
Url
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
val
title
:
CharSequence
?,
val
title
:
CharSequence
?,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
View file @
c1871a62
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.attachment.VideoAttachment
import
chat.rocket.core.model.attachment.VideoAttachment
data class
VideoAttachmentViewModel
(
data class
VideoAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
VideoAttachment
,
override
val
rawData
:
VideoAttachment
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
attachmentUrl
:
String
,
override
val
attachmentUrl
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
c1871a62
...
@@ -19,11 +19,13 @@ import chat.rocket.core.model.Message
...
@@ -19,11 +19,13 @@ import chat.rocket.core.model.Message
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.Value
import
chat.rocket.core.model.attachment.*
import
chat.rocket.core.model.attachment.*
import
chat.rocket.core.model.isSystemMessage
import
chat.rocket.core.model.url.Url
import
chat.rocket.core.model.url.Url
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.experimental.withContext
import
okhttp3.HttpUrl
import
okhttp3.HttpUrl
import
timber.log.Timber
import
timber.log.Timber
import
java.security.InvalidParameterException
import
javax.inject.Inject
import
javax.inject.Inject
class
ViewModelMapper
@Inject
constructor
(
private
val
context
:
Context
,
class
ViewModelMapper
@Inject
constructor
(
private
val
context
:
Context
,
...
@@ -81,7 +83,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -81,7 +83,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val
title
=
url
.
meta
?.
title
val
title
=
url
.
meta
?.
title
val
description
=
url
.
meta
?.
description
val
description
=
url
.
meta
?.
description
return
UrlPreviewViewModel
(
url
,
message
.
id
,
title
,
hostname
,
description
,
thumb
)
return
UrlPreviewViewModel
(
message
,
url
,
message
.
id
,
title
,
hostname
,
description
,
thumb
)
}
}
private
fun
mapAttachment
(
message
:
Message
,
attachment
:
Attachment
):
BaseViewModel
<
*
>?
{
private
fun
mapAttachment
(
message
:
Message
,
attachment
:
Attachment
):
BaseViewModel
<
*
>?
{
...
@@ -96,12 +98,12 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -96,12 +98,12 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val
attachmentTitle
=
attachment
.
title
val
attachmentTitle
=
attachment
.
title
val
id
=
"${message.id}_${attachment.titleLink}"
.
hashCode
().
toLong
()
val
id
=
"${message.id}_${attachment.titleLink}"
.
hashCode
().
toLong
()
return
when
(
attachment
)
{
return
when
(
attachment
)
{
is
ImageAttachment
->
ImageAttachmentViewModel
(
attachment
,
message
.
id
,
attachmentUrl
,
is
ImageAttachment
->
ImageAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentTitle
?:
""
,
id
)
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
VideoAttachment
->
VideoAttachmentViewModel
(
attachment
,
message
.
id
,
is
VideoAttachment
->
VideoAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
AudioAttachment
->
AudioAttachmentViewModel
(
message
,
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
is
AudioAttachment
->
AudioAttachmentViewModel
(
attachment
,
message
.
id
,
attachmentUrl
,
attachmentTitle
?:
""
,
id
)
else
->
null
else
->
null
}
}
}
}
...
@@ -143,13 +145,16 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -143,13 +145,16 @@ class ViewModelMapper @Inject constructor(private val context: Context,
}
}
val
content
=
getContent
(
context
,
message
,
quote
)
val
content
=
getContent
(
context
,
message
,
quote
)
MessageViewModel
(
rawData
=
message
,
messageId
=
message
.
id
,
MessageViewModel
(
message
=
message
,
rawData
=
message
,
messageId
=
message
.
id
,
avatar
=
avatar
!!
,
time
=
time
,
senderName
=
sender
,
avatar
=
avatar
!!
,
time
=
time
,
senderName
=
sender
,
content
=
content
.
first
,
isPinned
=
message
.
pinned
,
content
=
content
,
isPinned
=
message
.
pinned
)
isSystemMessage
=
content
.
second
)
}
}
private
fun
getSenderName
(
message
:
Message
):
CharSequence
{
private
fun
getSenderName
(
message
:
Message
):
CharSequence
{
if
(!
message
.
senderAlias
.
isNullOrEmpty
())
{
return
message
.
senderAlias
!!
}
val
username
=
message
.
sender
?.
username
val
username
=
message
.
sender
?.
username
val
realName
=
message
.
sender
?.
name
val
realName
=
message
.
sender
?.
name
val
senderName
=
if
(
settings
.
useRealName
())
realName
else
username
val
senderName
=
if
(
settings
.
useRealName
())
realName
else
username
...
@@ -157,6 +162,10 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -157,6 +162,10 @@ class ViewModelMapper @Inject constructor(private val context: Context,
}
}
private
fun
getUserAvatar
(
message
:
Message
):
String
?
{
private
fun
getUserAvatar
(
message
:
Message
):
String
?
{
message
.
avatar
?.
let
{
return
it
// Always give preference for overridden avatar from message
}
val
username
=
message
.
sender
?.
username
?:
"?"
val
username
=
message
.
sender
?.
username
?:
"?"
return
baseUrl
?.
let
{
return
baseUrl
?.
let
{
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
...
@@ -171,28 +180,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -171,28 +180,14 @@ class ViewModelMapper @Inject constructor(private val context: Context,
Timber
.
d
(
"Will quote message Id: $msgIdToQuote"
)
Timber
.
d
(
"Will quote message Id: $msgIdToQuote"
)
return
if
(
msgIdToQuote
!=
null
)
messagesRepository
.
getById
(
msgIdToQuote
)
else
null
return
if
(
msgIdToQuote
!=
null
)
messagesRepository
.
getById
(
msgIdToQuote
)
else
null
}
}
return
null
return
null
}
}
private
suspend
fun
getContent
(
context
:
Context
,
message
:
Message
,
quote
:
Message
?):
Pair
<
CharSequence
,
Boolean
>
{
private
suspend
fun
getContent
(
context
:
Context
,
message
:
Message
,
quote
:
Message
?):
CharSequence
{
var
systemMessage
=
true
return
when
(
message
.
isSystemMessage
())
{
val
content
=
when
(
message
.
type
)
{
true
->
getSystemMessage
(
message
,
context
)
//TODO: Add implementation for Welcome type.
false
->
getNormalMessage
(
message
,
quote
)
is
MessageType
.
MessageRemoved
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_removed
))
is
MessageType
.
UserJoined
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_joined_channel
))
is
MessageType
.
UserLeft
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_left
))
is
MessageType
.
UserAdded
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_added_by
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
RoomNameChanged
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_room_name_changed
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
UserRemoved
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_user_removed_by
,
message
.
message
,
message
.
sender
?.
username
))
is
MessageType
.
MessagePinned
->
getSystemMessage
(
context
.
getString
(
R
.
string
.
message_pinned
))
else
->
{
systemMessage
=
false
getNormalMessage
(
message
,
quote
)
}
}
}
return
Pair
(
content
,
systemMessage
)
}
}
private
suspend
fun
getNormalMessage
(
message
:
Message
,
quote
:
Message
?):
CharSequence
{
private
suspend
fun
getNormalMessage
(
message
:
Message
,
quote
:
Message
?):
CharSequence
{
...
@@ -204,7 +199,32 @@ class ViewModelMapper @Inject constructor(private val context: Context,
...
@@ -204,7 +199,32 @@ class ViewModelMapper @Inject constructor(private val context: Context,
return
parser
.
renderMarkdown
(
message
.
message
,
quoteViewModel
,
currentUsername
)
return
parser
.
renderMarkdown
(
message
.
message
,
quoteViewModel
,
currentUsername
)
}
}
private
fun
getSystemMessage
(
content
:
String
):
CharSequence
{
private
fun
getSystemMessage
(
message
:
Message
,
context
:
Context
):
CharSequence
{
val
content
=
when
(
message
.
type
)
{
//TODO: Add implementation for Welcome type.
is
MessageType
.
MessageRemoved
->
context
.
getString
(
R
.
string
.
message_removed
)
is
MessageType
.
UserJoined
->
context
.
getString
(
R
.
string
.
message_user_joined_channel
)
is
MessageType
.
UserLeft
->
context
.
getString
(
R
.
string
.
message_user_left
)
is
MessageType
.
UserAdded
->
context
.
getString
(
R
.
string
.
message_user_added_by
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
RoomNameChanged
->
context
.
getString
(
R
.
string
.
message_room_name_changed
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
UserRemoved
->
context
.
getString
(
R
.
string
.
message_user_removed_by
,
message
.
message
,
message
.
sender
?.
username
)
is
MessageType
.
MessagePinned
->
{
val
attachment
=
message
.
attachments
?.
get
(
0
)
val
pinnedSystemMessage
=
context
.
getString
(
R
.
string
.
message_pinned
)
if
(
attachment
!=
null
&&
attachment
is
MessageAttachment
)
{
return
SpannableStringBuilder
(
pinnedSystemMessage
)
.
apply
{
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
length
,
0
)
setSpan
(
ForegroundColorSpan
(
Color
.
GRAY
),
0
,
length
,
0
)
}
.
append
(
quoteMessage
(
attachment
.
author
!!
,
attachment
.
text
!!
,
attachment
.
timestamp
!!
))
}
return
pinnedSystemMessage
}
else
->
{
throw
InvalidParameterException
(
"Invalid message type: ${message.type}"
)
}
}
//isSystemMessage = true
//isSystemMessage = true
val
spannableMsg
=
SpannableStringBuilder
(
content
)
val
spannableMsg
=
SpannableStringBuilder
(
content
)
spannableMsg
.
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
spannableMsg
.
length
,
spannableMsg
.
setSpan
(
StyleSpan
(
Typeface
.
ITALIC
),
0
,
spannableMsg
.
length
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
c1871a62
package
chat.rocket.android.chatrooms.presentation
package
chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.
GetChatRoomsInteractor
import
chat.rocket.android.server.domain.
*
import
chat.rocket.android.server.
domain.GetCurrentServerInteracto
r
import
chat.rocket.android.server.
infraestructure.ConnectionManage
r
import
chat.rocket.android.server.
domain.RefreshSettingsInteractor
import
chat.rocket.android.server.
infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.
domain.SaveChatRoomsInteractor
import
chat.rocket.android.server.
infraestructure.chatRooms
import
chat.rocket.android.server.infraestructure.
RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.
state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.RoomType
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.realtime.*
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.internal.realtime.StreamMessage
import
chat.rocket.core.internal.realtime.Type
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.Room
import
chat.rocket.core.model.Room
import
kotlinx.coroutines.experimental.*
import
kotlinx.coroutines.experimental.*
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.channels.Channel
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -26,31 +29,47 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -26,31 +29,47 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
factory
:
RocketChatClientFactory
)
{
settingsRepository
:
SettingsRepository
,
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
factory
:
ConnectionManagerFactory
)
{
private
val
manager
:
ConnectionManager
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
var
reloadJob
:
Deferred
<
List
<
ChatRoom
>>?
=
null
private
var
reloadJob
:
Deferred
<
List
<
ChatRoom
>>?
=
null
private
val
settings
=
settingsRepository
.
get
(
currentServer
)
!!
private
val
subscriptionsChannel
=
Channel
<
StreamMessage
<
BaseRoom
>>()
private
val
stateChannel
=
Channel
<
State
>()
private
val
stateChannel
=
Channel
<
State
>()
private
var
lastState
=
manager
.
state
fun
loadChatRooms
()
{
fun
loadChatRooms
()
{
refreshSettingsInteractor
.
refreshAsync
(
currentServer
)
refreshSettingsInteractor
.
refreshAsync
(
currentServer
)
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
view
.
showLoading
()
view
.
showLoading
()
subscribeStatusChange
()
try
{
try
{
view
.
updateChatRooms
(
loadRooms
())
view
.
updateChatRooms
(
loadRooms
())
subscribeRoomUpdates
()
}
catch
(
e
:
RocketChatException
)
{
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
Timber
.
e
(
e
)
view
.
showMessage
(
e
.
message
!!
)
view
.
showMessage
(
e
.
message
!!
)
}
finally
{
}
finally
{
view
.
hideLoading
()
view
.
hideLoading
()
}
}
subscribeRoomUpdates
()
}
}
}
}
fun
loadChatRoom
(
chatRoom
:
ChatRoom
)
=
navigator
.
toChatRoom
(
chatRoom
.
id
,
chatRoom
.
name
,
fun
loadChatRoom
(
chatRoom
:
ChatRoom
)
{
val
roomName
=
if
(
chatRoom
.
type
is
RoomType
.
DirectMessage
&&
chatRoom
.
fullName
!=
null
&&
settings
.
useRealName
())
{
chatRoom
.
fullName
!!
}
else
{
chatRoom
.
name
}
navigator
.
toChatRoom
(
chatRoom
.
id
,
roomName
,
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
)
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
)
}
/**
/**
* Gets a [ChatRoom] list from local repository.
* Gets a [ChatRoom] list from local repository.
...
@@ -65,8 +84,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -65,8 +84,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private
suspend
fun
loadRooms
():
List
<
ChatRoom
>
{
private
suspend
fun
loadRooms
():
List
<
ChatRoom
>
{
val
chatRooms
=
client
.
chatRooms
().
update
val
chatRooms
=
manager
.
chatRooms
().
update
val
sortedRooms
=
sortRooms
(
chatRooms
)
val
sortedRooms
=
sortRooms
(
chatRooms
)
Timber
.
d
(
"Loaded rooms: ${sortedRooms.size}"
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
return
sortedRooms
return
sortedRooms
}
}
...
@@ -77,6 +97,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -77,6 +97,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
private
fun
updateRooms
()
{
private
fun
updateRooms
()
{
Timber
.
d
(
"Updating Rooms"
)
launch
{
launch
{
view
.
updateChatRooms
(
getChatRoomsInteractor
.
get
(
currentServer
))
view
.
updateChatRooms
(
getChatRoomsInteractor
.
get
(
currentServer
))
}
}
...
@@ -92,52 +113,43 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -92,52 +113,43 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
}
}
// TODO - Temporary stuff, remove when adding DB support
private
suspend
fun
subscribeStatusChange
()
{
private
suspend
fun
subscribeRoomUpdates
()
{
lastState
=
manager
.
state
client
.
addStateChannel
(
stateChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
status
in
stateChannel
)
{
for
(
state
in
stateChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
Timber
.
d
(
"Got new state: $state - last: $lastState"
)
when
(
status
)
{
if
(
state
!=
lastState
)
{
is
State
.
Authenticating
->
Timber
.
d
(
"Authenticating"
)
launch
(
UI
)
{
is
State
.
Connected
->
{
view
.
showConnectionState
(
state
)
Timber
.
d
(
"Connected"
)
client
.
subscribeSubscriptions
{
success
,
_
->
Timber
.
d
(
"subscriptions: $success"
)
}
client
.
subscribeRooms
{
success
,
_
->
Timber
.
d
(
"rooms: $success"
)
}
}
}
if
(
state
is
State
.
Connected
)
{
reloadRooms
()
updateRooms
()
}
}
}
}
Timber
.
d
(
"Done on statusChannel"
)
lastState
=
state
}
}
when
(
client
.
state
)
{
is
State
.
Connected
->
{
Timber
.
d
(
"Already connected"
)
}
}
else
->
client
.
connect
()
}
}
// TODO - Temporary stuff, remove when adding DB support
private
suspend
fun
subscribeRoomUpdates
()
{
manager
.
addStatusChannel
(
stateChannel
)
manager
.
addRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
room
sChannel
)
{
for
(
message
in
subscription
sChannel
)
{
Timber
.
d
(
"Got message: $message"
)
Timber
.
d
(
"Got message: $message"
)
updateRoom
(
message
)
when
(
message
.
data
)
{
}
is
Room
->
updateRoom
(
message
as
StreamMessage
<
Room
>)
is
Subscription
->
updateSubscription
(
message
as
StreamMessage
<
Subscription
>)
}
}
launch
(
CommonPool
+
strategy
.
jobs
)
{
for
(
message
in
client
.
subscriptionsChannel
)
{
Timber
.
d
(
"Got message: $message"
)
updateSubscription
(
message
)
}
}
}
}
}
}
private
fun
updateRoom
(
message
:
StreamMessage
<
Room
>)
{
private
suspend
fun
updateRoom
(
message
:
StreamMessage
<
Room
>)
{
launchUI
(
strategy
)
{
Timber
.
d
(
"Update Room: ${message.type} - ${message.data.id} - ${message.data.name}"
)
when
(
message
.
type
)
{
when
(
message
.
type
)
{
Type
.
Removed
->
{
Type
.
Removed
->
{
removeRoom
(
message
.
data
.
id
)
removeRoom
(
message
.
data
.
id
)
...
@@ -154,10 +166,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -154,10 +166,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
updateRooms
()
updateRooms
()
}
}
}
private
fun
updateSubscription
(
message
:
StreamMessage
<
Subscription
>)
{
private
suspend
fun
updateSubscription
(
message
:
StreamMessage
<
Subscription
>)
{
launchUI
(
strategy
)
{
Timber
.
d
(
"Update Subscription: ${message.type} - ${message.data.id} - ${message.data.name}"
)
when
(
message
.
type
)
{
when
(
message
.
type
)
{
Type
.
Removed
->
{
Type
.
Removed
->
{
removeRoom
(
message
.
data
.
roomId
)
removeRoom
(
message
.
data
.
roomId
)
...
@@ -174,22 +185,26 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -174,22 +185,26 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
updateRooms
()
updateRooms
()
}
}
}
private
suspend
fun
reloadRooms
()
{
private
suspend
fun
reloadRooms
()
{
Timber
.
d
(
"realoadRooms()"
)
Timber
.
d
(
"realoadRooms()"
)
reloadJob
?.
cancel
()
reloadJob
?.
cancel
()
try
{
reloadJob
=
async
(
CommonPool
+
strategy
.
jobs
)
{
reloadJob
=
async
(
CommonPool
+
strategy
.
jobs
)
{
delay
(
1000
)
delay
(
1000
)
Timber
.
d
(
"reloading rooms after wait"
)
Timber
.
d
(
"reloading rooms after wait"
)
loadRooms
()
loadRooms
()
}
}
reloadJob
?.
await
()
reloadJob
?.
await
()
}
catch
(
ex
:
Exception
)
{
ex
.
printStackTrace
()
}
}
}
// Update a ChatRoom with a Room information
// Update a ChatRoom with a Room information
private
fun
updateRoom
(
room
:
Room
)
{
private
fun
updateRoom
(
room
:
Room
)
{
Timber
.
d
(
"Updating Room: ${room.id} - ${room.name}"
)
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
room
.
id
}
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
room
.
id
}
chatRoom
?.
apply
{
chatRoom
?.
apply
{
...
@@ -220,6 +235,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -220,6 +235,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Subscription information
// Update a ChatRoom with a Subscription information
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
Timber
.
d
(
"Updating subscrition: ${subscription.id} - ${subscription.name}"
)
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
chatRoom
?.
apply
{
chatRoom
?.
apply
{
...
@@ -251,6 +267,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -251,6 +267,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
fun
removeRoom
(
id
:
String
,
private
fun
removeRoom
(
id
:
String
,
chatRooms
:
MutableList
<
ChatRoom
>
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
())
{
chatRooms
:
MutableList
<
ChatRoom
>
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
())
{
Timber
.
d
(
"Removing ROOM: $id"
)
synchronized
(
this
)
{
synchronized
(
this
)
{
chatRooms
.
removeAll
{
chatRoom
->
chatRoom
.
id
==
id
}
chatRooms
.
removeAll
{
chatRoom
->
chatRoom
.
id
==
id
}
}
}
...
@@ -258,7 +275,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
...
@@ -258,7 +275,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
}
fun
disconnect
()
{
fun
disconnect
()
{
client
.
removeState
Channel
(
stateChannel
)
manager
.
removeStatus
Channel
(
stateChannel
)
client
.
disconnect
(
)
manager
.
removeRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsView.kt
View file @
c1871a62
...
@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
...
@@ -2,6 +2,7 @@ package chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
interface
ChatRoomsView
:
LoadingView
,
MessageView
{
interface
ChatRoomsView
:
LoadingView
,
MessageView
{
...
@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView {
...
@@ -17,4 +18,6 @@ interface ChatRoomsView : LoadingView, MessageView {
* Shows no chat rooms to display.
* Shows no chat rooms to display.
*/
*/
fun
showNoChatRoomsToDisplay
()
fun
showNoChatRoomsToDisplay
()
fun
showConnectionState
(
state
:
State
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
View file @
c1871a62
...
@@ -11,6 +11,8 @@ import android.view.ViewGroup
...
@@ -11,6 +11,8 @@ import android.view.ViewGroup
import
android.widget.TextView
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.setVisible
...
@@ -23,6 +25,7 @@ import kotlinx.android.synthetic.main.item_chat.view.*
...
@@ -23,6 +25,7 @@ import kotlinx.android.synthetic.main.item_chat.view.*
import
kotlinx.android.synthetic.main.unread_messages_badge.view.*
import
kotlinx.android.synthetic.main.unread_messages_badge.view.*
class
ChatRoomsAdapter
(
private
val
context
:
Context
,
class
ChatRoomsAdapter
(
private
val
context
:
Context
,
private
val
settings
:
PublicSettings
,
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
...
@@ -73,27 +76,27 @@ class ChatRoomsAdapter(private val context: Context,
...
@@ -73,27 +76,27 @@ class ChatRoomsAdapter(private val context: Context,
private
fun
bindName
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
private
fun
bindName
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
textView
.
textContent
=
chatRoom
.
name
textView
.
textContent
=
chatRoom
.
name
var
drawable
:
Drawable
?
=
null
var
drawable
=
when
(
chatRoom
.
type
)
{
when
(
chatRoom
.
type
)
{
is
RoomType
.
Channel
->
{
is
RoomType
.
Channel
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_channel
,
context
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_channel
,
context
)
}
}
is
RoomType
.
PrivateGroup
->
{
is
RoomType
.
PrivateGroup
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_lock
,
context
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_lock
,
context
)
}
}
is
RoomType
.
DirectMessage
->
{
is
RoomType
.
DirectMessage
->
{
drawable
=
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_dm
,
context
)
DrawableHelper
.
getDrawableFromId
(
R
.
drawable
.
ic_room_dm
,
context
)
}
}
else
->
null
}
}
drawable
?.
let
{
drawable
?.
let
{
val
wrappedDrawable
=
DrawableHelper
.
wrapDrawable
(
it
)
val
wrappedDrawable
=
DrawableHelper
.
wrapDrawable
(
it
)
val
mutableDrawable
=
wrappedDrawable
.
mutate
()
val
mutableDrawable
=
wrappedDrawable
.
mutate
()
DrawableHelper
.
tintDrawable
(
mutableDrawable
,
context
,
val
color
=
when
(
chatRoom
.
alert
||
chatRoom
.
unread
>
0
)
{
when
(
chatRoom
.
alert
||
chatRoom
.
unread
>
0
)
{
true
->
R
.
color
.
colorPrimaryText
true
->
R
.
color
.
colorPrimaryText
false
->
R
.
color
.
colorSecondaryText
false
->
R
.
color
.
colorSecondaryText
})
}
DrawableHelper
.
tintDrawable
(
mutableDrawable
,
context
,
color
)
DrawableHelper
.
compoundDrawable
(
textView
,
mutableDrawable
)
DrawableHelper
.
compoundDrawable
(
textView
,
mutableDrawable
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
c1871a62
package
chat.rocket.android.chatrooms.ui
package
chat.rocket.android.chatrooms.ui
import
android.os.Bundle
import
android.os.Bundle
import
android.os.Handler
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.util.DiffUtil
import
android.support.v7.util.DiffUtil
...
@@ -11,14 +12,16 @@ import android.view.*
...
@@ -11,14 +12,16 @@ import android.view.*
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
import
chat.rocket.android.
util.extensions.inflate
import
chat.rocket.android.
server.domain.GetCurrentServerInteractor
import
chat.rocket.android.
util.extensions.setVisible
import
chat.rocket.android.
server.domain.SettingsRepository
import
chat.rocket.android.util.extensions.
showToast
import
chat.rocket.android.util.extensions.
*
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_chat_rooms.*
import
kotlinx.android.synthetic.main.fragment_chat_rooms.*
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.async
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.launch
...
@@ -26,7 +29,12 @@ import javax.inject.Inject
...
@@ -26,7 +29,12 @@ import javax.inject.Inject
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
settingsRepository
:
SettingsRepository
private
var
searchView
:
SearchView
?
=
null
private
var
searchView
:
SearchView
?
=
null
private
val
handler
=
Handler
()
private
var
listJob
:
Job
?
=
null
companion
object
{
companion
object
{
fun
newInstance
()
=
ChatRoomsFragment
()
fun
newInstance
()
=
ChatRoomsFragment
()
...
@@ -39,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -39,6 +47,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
}
override
fun
onDestroy
()
{
override
fun
onDestroy
()
{
handler
.
removeCallbacks
(
dismissStatus
)
presenter
.
disconnect
()
presenter
.
disconnect
()
super
.
onDestroy
()
super
.
onDestroy
()
}
}
...
@@ -53,6 +62,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -53,6 +62,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
presenter
.
loadChatRooms
()
presenter
.
loadChatRooms
()
}
}
override
fun
onDestroyView
()
{
listJob
?.
cancel
()
super
.
onDestroyView
()
}
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
override
fun
onCreateOptionsMenu
(
menu
:
Menu
,
inflater
:
MenuInflater
)
{
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
super
.
onCreateOptionsMenu
(
menu
,
inflater
)
inflater
.
inflate
(
R
.
menu
.
chatrooms
,
menu
)
inflater
.
inflate
(
R
.
menu
.
chatrooms
,
menu
)
...
@@ -71,18 +85,23 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -71,18 +85,23 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
}
override
suspend
fun
updateChatRooms
(
newDataSet
:
List
<
ChatRoom
>)
{
override
suspend
fun
updateChatRooms
(
newDataSet
:
List
<
ChatRoom
>)
{
activity
.
apply
{
activity
?.
apply
{
launch
(
UI
)
{
listJob
?.
cancel
()
listJob
=
launch
(
UI
)
{
val
adapter
=
recycler_view
.
adapter
as
ChatRoomsAdapter
val
adapter
=
recycler_view
.
adapter
as
ChatRoomsAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android.dev/issues/5a90d4718cb3c2fa63b3f557?time=last-seven-days
// TODO - fix this bug to reenable DiffUtil
val
diff
=
async
(
CommonPool
)
{
val
diff
=
async
(
CommonPool
)
{
DiffUtil
.
calculateDiff
(
RoomsDiffCallback
(
adapter
.
dataSet
,
newDataSet
))
DiffUtil
.
calculateDiff
(
RoomsDiffCallback
(
adapter
.
dataSet
,
newDataSet
))
}.
await
()
}.
await
()
if
(
isActive
)
{
adapter
.
updateRooms
(
newDataSet
)
adapter
.
updateRooms
(
newDataSet
)
diff
.
dispatchUpdatesTo
(
adapter
)
diff
.
dispatchUpdatesTo
(
adapter
)
}
}
}
}
}
}
}
override
fun
showNoChatRoomsToDisplay
()
=
text_no_data_to_display
.
setVisible
(
true
)
override
fun
showNoChatRoomsToDisplay
()
=
text_no_data_to_display
.
setVisible
(
true
)
...
@@ -96,6 +115,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -96,6 +115,28 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showConnectionState
(
state
:
State
)
{
activity
?.
apply
{
connection_status_text
.
fadeIn
()
handler
.
removeCallbacks
(
dismissStatus
)
when
(
state
)
{
is
State
.
Connected
->
{
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connected
)
handler
.
postDelayed
(
dismissStatus
,
2000
)
}
is
State
.
Disconnected
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
}
}
}
private
val
dismissStatus
=
{
connection_status_text
.
fadeOut
()
}
private
fun
setupToolbar
()
{
private
fun
setupToolbar
()
{
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
getString
(
R
.
string
.
title_chats
)
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
getString
(
R
.
string
.
title_chats
)
}
}
...
@@ -107,7 +148,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -107,7 +148,9 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_start
),
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_start
),
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_end
)))
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_end
)))
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
)
{
chatRoom
->
// TODO - use a ViewModel Mapper instead of using settings on the adapter
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
,
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
)
!!
)
{
chatRoom
->
presenter
.
loadChatRoom
(
chatRoom
)
presenter
.
loadChatRoom
(
chatRoom
)
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/dagger/module/ActivityBuilder.kt
View file @
c1871a62
...
@@ -16,6 +16,7 @@ import chat.rocket.android.dagger.scope.PerActivity
...
@@ -16,6 +16,7 @@ import chat.rocket.android.dagger.scope.PerActivity
import
chat.rocket.android.main.di.MainActivityProvider
import
chat.rocket.android.main.di.MainActivityProvider
import
chat.rocket.android.main.di.MainModule
import
chat.rocket.android.main.di.MainModule
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.members.di.MembersFragmentProvider
import
chat.rocket.android.profile.di.ProfileFragmentProvider
import
chat.rocket.android.profile.di.ProfileFragmentProvider
import
chat.rocket.android.settings.password.di.PasswordFragmentProvider
import
chat.rocket.android.settings.password.di.PasswordFragmentProvider
import
chat.rocket.android.settings.password.ui.PasswordActivity
import
chat.rocket.android.settings.password.ui.PasswordActivity
...
@@ -44,7 +45,7 @@ abstract class ActivityBuilder {
...
@@ -44,7 +45,7 @@ abstract class ActivityBuilder {
abstract
fun
bindMainActivity
():
MainActivity
abstract
fun
bindMainActivity
():
MainActivity
@PerActivity
@PerActivity
@ContributesAndroidInjector
(
modules
=
[
ChatRoomFragmentProvider
::
class
])
@ContributesAndroidInjector
(
modules
=
[
ChatRoomFragmentProvider
::
class
,
MembersFragmentProvider
::
class
])
abstract
fun
bindChatRoomActivity
():
ChatRoomActivity
abstract
fun
bindChatRoomActivity
():
ChatRoomActivity
@PerActivity
@PerActivity
...
...
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
View file @
c1871a62
...
@@ -13,11 +13,15 @@ import android.support.v4.content.res.ResourcesCompat
...
@@ -13,11 +13,15 @@ import android.support.v4.content.res.ResourcesCompat
import
android.text.Layout
import
android.text.Layout
import
android.text.Spannable
import
android.text.Spannable
import
android.text.Spanned
import
android.text.Spanned
import
android.text.TextUtils
import
android.text.style.*
import
android.text.style.*
import
android.util.Patterns
import
android.util.Patterns
import
android.view.View
import
android.view.View
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiRepository
import
chat.rocket.android.widget.emoji.EmojiTypefaceSpan
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.BlockQuote
import
org.commonmark.node.BlockQuote
import
org.commonmark.node.Text
import
org.commonmark.node.Text
...
@@ -47,7 +51,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -47,7 +51,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
*/
*/
fun
renderMarkdown
(
text
:
String
,
quote
:
MessageViewModel
?
=
null
,
selfUsername
:
String
?
=
null
):
CharSequence
{
fun
renderMarkdown
(
text
:
String
,
quote
:
MessageViewModel
?
=
null
,
selfUsername
:
String
?
=
null
):
CharSequence
{
val
builder
=
SpannableBuilder
()
val
builder
=
SpannableBuilder
()
val
content
=
text
val
content
=
EmojiRepository
.
shortnameToUnicode
(
text
,
true
)
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
parentNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
parentNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
quote
?.
apply
{
quote
?.
apply
{
...
@@ -58,6 +62,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -58,6 +62,7 @@ class MessageParser @Inject constructor(val context: Application, private val co
quoteNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
quoteNode
.
accept
(
QuoteMessageBodyVisitor
(
context
,
configuration
,
builder
))
}
}
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
EmojiVisitor
(
builder
))
val
result
=
builder
.
text
()
val
result
=
builder
.
text
()
applySpans
(
result
,
selfUsername
)
applySpans
(
result
,
selfUsername
)
...
@@ -139,6 +144,19 @@ class MessageParser @Inject constructor(val context: Application, private val co
...
@@ -139,6 +144,19 @@ class MessageParser @Inject constructor(val context: Application, private val co
}
}
}
}
class
EmojiVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
override
fun
visit
(
text
:
Text
)
{
val
spannable
=
EmojiParser
.
parse
(
text
.
literal
)
if
(
spannable
is
Spanned
)
{
val
spans
=
spannable
.
getSpans
(
0
,
spannable
.
length
,
EmojiTypefaceSpan
::
class
.
java
)
spans
.
forEach
{
builder
.
setSpan
(
it
,
spannable
.
getSpanStart
(
it
),
spannable
.
getSpanEnd
(
it
),
0
)
}
}
visitChildren
(
text
)
}
}
class
LinkVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
class
LinkVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
override
fun
visit
(
text
:
Text
)
{
override
fun
visit
(
text
:
Text
)
{
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
c1871a62
...
@@ -2,6 +2,7 @@ package chat.rocket.android.main.presentation
...
@@ -2,6 +2,7 @@ package chat.rocket.android.main.presentation
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.RocketChatClient
...
@@ -13,9 +14,11 @@ import javax.inject.Inject
...
@@ -13,9 +14,11 @@ import javax.inject.Inject
class
MainPresenter
@Inject
constructor
(
private
val
navigator
:
MainNavigator
,
class
MainPresenter
@Inject
constructor
(
private
val
navigator
:
MainNavigator
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
localRepository
:
LocalRepository
,
private
val
localRepository
:
LocalRepository
,
managerFactory
:
ConnectionManagerFactory
,
factory
:
RocketChatClientFactory
)
{
factory
:
RocketChatClientFactory
)
{
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
manager
=
managerFactory
.
create
(
currentServer
)
private
val
client
:
RocketChatClient
=
factory
.
create
(
currentServer
)
fun
toChatList
()
=
navigator
.
toChatList
()
fun
toChatList
()
=
navigator
.
toChatList
()
...
@@ -51,4 +54,12 @@ class MainPresenter @Inject constructor(private val navigator: MainNavigator,
...
@@ -51,4 +54,12 @@ class MainPresenter @Inject constructor(private val navigator: MainNavigator,
}
}
localRepository
.
clearAllFromServer
(
currentServer
)
localRepository
.
clearAllFromServer
(
currentServer
)
}
}
fun
connect
()
{
manager
.
connect
()
}
fun
disconnect
()
{
manager
.
disconnect
()
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
View file @
c1871a62
...
@@ -35,6 +35,7 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
...
@@ -35,6 +35,7 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
setContentView
(
R
.
layout
.
activity_main
)
presenter
.
connect
()
setupToolbar
()
setupToolbar
()
setupNavigationView
()
setupNavigationView
()
}
}
...
@@ -47,6 +48,13 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
...
@@ -47,6 +48,13 @@ class MainActivity : AppCompatActivity(), MainView, HasSupportFragmentInjector {
}
}
}
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
if
(
isFinishing
)
{
presenter
.
disconnect
()
}
}
override
fun
onLogout
()
{
override
fun
onLogout
()
{
finish
()
finish
()
val
intent
=
Intent
(
this
,
AuthenticationActivity
::
class
.
java
)
val
intent
=
Intent
(
this
,
AuthenticationActivity
::
class
.
java
)
...
...
app/src/main/java/chat/rocket/android/member/ui/MemberBottomSheetFragment.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.member.ui
import
android.os.Bundle
import
android.support.design.widget.BottomSheetDialogFragment
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.textContent
import
kotlinx.android.synthetic.main.fragment_member_bottom_sheet.*
fun
newInstance
(
avatarUri
:
String
,
realName
:
String
,
username
:
String
,
email
:
String
,
utcOffset
:
String
):
BottomSheetDialogFragment
{
return
MemberBottomSheetFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
putString
(
BUNDLE_AVATAR_URI
,
avatarUri
)
putString
(
BUNDLE_REAL_NAME
,
realName
)
putString
(
BUNDLE_USERNAME
,
username
)
putString
(
BUNDLE_EMAIL
,
email
)
putString
(
BUNDLE_UTC_OFFSET
,
utcOffset
)
}
}
}
private
const
val
BUNDLE_AVATAR_URI
=
"avatar_uri"
private
const
val
BUNDLE_REAL_NAME
=
"real_name"
private
const
val
BUNDLE_USERNAME
=
"username"
private
const
val
BUNDLE_EMAIL
=
"email"
private
const
val
BUNDLE_UTC_OFFSET
=
"utc_offset"
class
MemberBottomSheetFragment
:
BottomSheetDialogFragment
()
{
private
lateinit
var
avatarUri
:
String
private
lateinit
var
realName
:
String
private
lateinit
var
username
:
String
private
lateinit
var
email
:
String
private
lateinit
var
utcOffset
:
String
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
val
bundle
=
arguments
if
(
bundle
!=
null
)
{
avatarUri
=
bundle
.
getString
(
BUNDLE_AVATAR_URI
)
realName
=
bundle
.
getString
(
BUNDLE_REAL_NAME
)
username
=
bundle
.
getString
(
BUNDLE_USERNAME
)
email
=
bundle
.
getString
(
BUNDLE_EMAIL
)
utcOffset
=
bundle
.
getString
(
BUNDLE_UTC_OFFSET
)
}
else
{
requireNotNull
(
bundle
)
{
"no arguments supplied when the fragment was instantiated"
}
}
}
override
fun
onCreateView
(
inflater
:
LayoutInflater
,
container
:
ViewGroup
?,
savedInstanceState
:
Bundle
?):
View
?
=
inflater
.
inflate
(
R
.
layout
.
fragment_member_bottom_sheet
,
container
,
false
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
showMemberDetails
()
}
private
fun
showMemberDetails
()
{
image_bottom_sheet_avatar
.
setImageURI
(
avatarUri
)
text_bottom_sheet_member_name
.
content
=
realName
text_bottom_sheet_member_username
.
content
=
username
if
(
email
.
isNotEmpty
())
{
text_member_email_address
.
textContent
=
email
}
else
{
text_email_address
.
setVisible
(
false
)
text_member_email_address
.
setVisible
(
false
)
}
if
(
utcOffset
.
isNotEmpty
()){
text_member_utc
.
content
=
utcOffset
}
else
{
text_utc
.
setVisible
(
false
)
text_member_utc
.
setVisible
(
false
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/adapter/MembersAdapter.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.adapter
import
android.support.v7.widget.RecyclerView
import
android.view.View
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.members.viewmodel.MemberViewModel
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.inflate
import
kotlinx.android.synthetic.main.avatar.view.*
import
kotlinx.android.synthetic.main.item_member.view.*
class
MembersAdapter
(
private
val
listener
:
(
MemberViewModel
)
->
Unit
)
:
RecyclerView
.
Adapter
<
MembersAdapter
.
ViewHolder
>()
{
private
var
dataSet
:
List
<
MemberViewModel
>
=
ArrayList
()
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
MembersAdapter
.
ViewHolder
=
ViewHolder
(
parent
.
inflate
(
R
.
layout
.
item_member
))
override
fun
onBindViewHolder
(
holder
:
MembersAdapter
.
ViewHolder
,
position
:
Int
)
=
holder
.
bind
(
dataSet
[
position
],
listener
)
override
fun
getItemCount
():
Int
=
dataSet
.
size
fun
prependData
(
dataSet
:
List
<
MemberViewModel
>)
{
this
.
dataSet
=
dataSet
notifyItemRangeInserted
(
0
,
dataSet
.
size
)
}
fun
appendData
(
dataSet
:
List
<
MemberViewModel
>)
{
val
previousDataSetSize
=
this
.
dataSet
.
size
this
.
dataSet
+=
dataSet
notifyItemRangeInserted
(
previousDataSetSize
,
dataSet
.
size
)
}
class
ViewHolder
(
itemView
:
View
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
fun
bind
(
memberViewModel
:
MemberViewModel
,
listener
:
(
MemberViewModel
)
->
Unit
)
=
with
(
itemView
)
{
image_avatar
.
setImageURI
(
memberViewModel
.
avatarUri
)
text_member
.
content
=
memberViewModel
.
displayName
setOnClickListener
{
listener
(
memberViewModel
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/di/MembersFragmentModule.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.di
import
android.arch.lifecycle.LifecycleOwner
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.members.presentation.MembersNavigator
import
chat.rocket.android.members.presentation.MembersView
import
chat.rocket.android.members.ui.MembersFragment
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.experimental.Job
@Module
@PerFragment
class
MembersFragmentModule
{
@Provides
fun
provideChatRoomNavigator
(
activity
:
ChatRoomActivity
)
=
MembersNavigator
(
activity
)
@Provides
fun
membersView
(
frag
:
MembersFragment
):
MembersView
{
return
frag
}
@Provides
fun
provideLifecycleOwner
(
frag
:
MembersFragment
):
LifecycleOwner
{
return
frag
}
@Provides
fun
provideCancelStrategy
(
owner
:
LifecycleOwner
,
jobs
:
Job
):
CancelStrategy
{
return
CancelStrategy
(
owner
,
jobs
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/di/MembersFragmentProvider.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.di
import
chat.rocket.android.members.ui.MembersFragment
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
@Module
abstract
class
MembersFragmentProvider
{
@ContributesAndroidInjector
(
modules
=
[
MembersFragmentModule
::
class
])
abstract
fun
provideMembersFragment
():
MembersFragment
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/presentation/MembersNavigator.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.presentation
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.member.ui.newInstance
class
MembersNavigator
(
internal
val
activity
:
ChatRoomActivity
)
{
fun
toMemberDetails
(
avatarUri
:
String
,
realName
:
String
,
username
:
String
,
email
:
String
,
utcOffset
:
String
)
{
activity
.
apply
{
newInstance
(
avatarUri
,
realName
,
username
,
email
,
utcOffset
)
.
show
(
supportFragmentManager
,
"MemberBottomSheetFragment"
)
}
}
}
app/src/main/java/chat/rocket/android/members/presentation/MembersPresenter.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.presentation
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.members.viewmodel.MemberViewModel
import
chat.rocket.android.members.viewmodel.MemberViewModelMapper
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.model.roomTypeOf
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.getMembers
import
javax.inject.Inject
class
MembersPresenter
@Inject
constructor
(
private
val
view
:
MembersView
,
private
val
navigator
:
MembersNavigator
,
private
val
strategy
:
CancelStrategy
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
factory
:
RocketChatClientFactory
,
private
val
mapper
:
MemberViewModelMapper
)
{
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
fun
loadChatRoomsMembers
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
launchUI
(
strategy
)
{
try
{
view
.
showLoading
()
val
members
=
client
.
getMembers
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
60
)
val
memberViewModels
=
mapper
.
mapToViewModelList
(
members
.
result
)
view
.
showMembers
(
memberViewModels
,
members
.
total
)
}
catch
(
ex
:
RocketChatException
)
{
ex
.
message
?.
let
{
view
.
showMessage
(
it
)
}.
ifNull
{
view
.
showGenericErrorMessage
()
}
}
finally
{
view
.
hideLoading
()
}
}
}
fun
toMemberDetails
(
memberViewModel
:
MemberViewModel
)
{
val
avatarUri
=
memberViewModel
.
avatarUri
.
toString
()
val
realName
=
memberViewModel
.
realName
.
toString
()
val
username
=
"@${memberViewModel.username}"
val
email
=
memberViewModel
.
email
?:
""
val
utcOffset
=
memberViewModel
.
utcOffset
.
toString
()
navigator
.
toMemberDetails
(
avatarUri
,
realName
,
username
,
email
,
utcOffset
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/presentation/MembersView.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.presentation
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.members.viewmodel.MemberViewModel
interface
MembersView
:
LoadingView
,
MessageView
{
/**
* Shows a list of members of a room.
*
* @param dataSet The data set to show.
* @param total The total number of members.
*/
fun
showMembers
(
dataSet
:
List
<
MemberViewModel
>,
total
:
Long
)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/ui/MembersFragment.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.ui
import
android.os.Bundle
import
android.support.v4.app.Fragment
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.View
import
android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.members.adapter.MembersAdapter
import
chat.rocket.android.members.presentation.MembersPresenter
import
chat.rocket.android.members.presentation.MembersView
import
chat.rocket.android.members.viewmodel.MemberViewModel
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.widget.DividerItemDecoration
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_members.*
import
javax.inject.Inject
fun
newInstance
(
chatRoomId
:
String
,
chatRoomType
:
String
):
Fragment
{
return
MembersFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
putString
(
BUNDLE_CHAT_ROOM_ID
,
chatRoomId
)
putString
(
BUNDLE_CHAT_ROOM_TYPE
,
chatRoomType
)
}
}
}
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
)
private
lateinit
var
chatRoomId
:
String
private
lateinit
var
chatRoomType
:
String
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
val
bundle
=
arguments
if
(
bundle
!=
null
)
{
chatRoomId
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_ID
)
chatRoomType
=
bundle
.
getString
(
BUNDLE_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_members
)
override
fun
onViewCreated
(
view
:
View
,
savedInstanceState
:
Bundle
?)
{
super
.
onViewCreated
(
view
,
savedInstanceState
)
(
activity
as
AppCompatActivity
).
supportActionBar
?.
title
=
""
setupRecyclerView
()
presenter
.
loadChatRoomsMembers
(
chatRoomId
,
chatRoomType
)
}
override
fun
showMembers
(
dataSet
:
List
<
MemberViewModel
>,
total
:
Long
)
{
activity
?.
apply
{
setupToolbar
(
total
)
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
?)
{
presenter
.
loadChatRoomsMembers
(
chatRoomId
,
chatRoomType
,
page
*
60L
)
}
})
}
}
else
{
adapter
.
appendData
(
dataSet
)
}
}
}
override
fun
showLoading
()
=
view_loading
.
setVisible
(
true
)
override
fun
hideLoading
()
=
view_loading
.
setVisible
(
false
)
override
fun
showMessage
(
resId
:
Int
)
=
showToast
(
resId
)
override
fun
showMessage
(
message
:
String
)
=
showToast
(
message
)
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
private
fun
setupRecyclerView
()
{
activity
?.
apply
{
recycler_view
.
layoutManager
=
linearLayoutManager
recycler_view
.
addItemDecoration
(
DividerItemDecoration
(
this
))
recycler_view
.
adapter
=
adapter
}
}
private
fun
setupToolbar
(
totalMembers
:
Long
)
{
(
activity
as
ChatRoomActivity
).
setupToolbarTitle
(
getString
(
R
.
string
.
title_members
,
totalMembers
))
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/viewmodel/MemberViewModel.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.viewmodel
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.common.model.User
import
chat.rocket.core.model.Value
class
MemberViewModel
(
private
val
member
:
User
,
private
val
settings
:
Map
<
String
,
Value
<
Any
>>,
private
val
baseUrl
:
String
?)
{
val
avatarUri
:
String
?
val
displayName
:
String
val
realName
:
String
?
val
username
:
String
?
val
email
:
String
?
val
utcOffset
:
Float
?
init
{
avatarUri
=
getUserAvatar
()
displayName
=
getUserDisplayName
()
realName
=
getUserRealName
()
username
=
getUserUsername
()
email
=
getUserEmail
()
utcOffset
=
getUserUtcOffset
()
}
private
fun
getUserAvatar
():
String
?
{
val
username
=
member
.
username
?:
"?"
return
baseUrl
?.
let
{
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
}
}
private
fun
getUserDisplayName
():
String
{
val
username
=
member
.
username
val
realName
=
member
.
name
val
senderName
=
if
(
settings
.
useRealName
())
realName
else
username
return
senderName
?:
username
.
toString
()
}
private
fun
getUserRealName
():
String
?
=
member
.
name
private
fun
getUserUsername
():
String
?
=
member
.
username
private
fun
getUserEmail
():
String
?
=
member
.
emails
?.
get
(
0
)
?.
address
private
fun
getUserUtcOffset
():
Float
?
=
member
.
utcOffset
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/members/viewmodel/MemberViewModelMapper.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.members.viewmodel
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.baseUrl
import
chat.rocket.common.model.User
import
chat.rocket.core.model.Value
import
javax.inject.Inject
class
MemberViewModelMapper
@Inject
constructor
(
serverInteractor
:
GetCurrentServerInteractor
,
getSettingsInteractor
:
GetSettingsInteractor
)
{
private
var
settings
:
Map
<
String
,
Value
<
Any
>>
=
getSettingsInteractor
.
get
(
serverInteractor
.
get
()
!!
)
!!
private
val
baseUrl
=
settings
.
baseUrl
()
fun
mapToViewModelList
(
memberList
:
List
<
User
>):
List
<
MemberViewModel
>
{
return
memberList
.
map
{
MemberViewModel
(
it
,
settings
,
baseUrl
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/domain/SettingsRepository.kt
View file @
c1871a62
...
@@ -48,30 +48,31 @@ const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
...
@@ -48,30 +48,31 @@ const val ALLOW_MESSAGE_PINNING = "Message_AllowPinning"
* If you need to access a Setting, add a const val key above, add it to the filter on
* If you need to access a Setting, add a const val key above, add it to the filter on
* ServerPresenter.kt and a extension function to access it
* ServerPresenter.kt and a extension function to access it
*/
*/
fun
Map
<
String
,
Value
<
Any
>>
.
googleEnabled
():
Boolean
=
this
[
ACCOUNT_GOOGLE
]
?.
value
==
true
fun
PublicSettings
.
googleEnabled
():
Boolean
=
this
[
ACCOUNT_GOOGLE
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
facebookEnabled
():
Boolean
=
this
[
ACCOUNT_FACEBOOK
]
?.
value
==
true
fun
PublicSettings
.
facebookEnabled
():
Boolean
=
this
[
ACCOUNT_FACEBOOK
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
githubEnabled
():
Boolean
=
this
[
ACCOUNT_GITHUB
]
?.
value
==
true
fun
PublicSettings
.
githubEnabled
():
Boolean
=
this
[
ACCOUNT_GITHUB
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
linkedinEnabled
():
Boolean
=
this
[
ACCOUNT_LINKEDIN
]
?.
value
==
true
fun
PublicSettings
.
linkedinEnabled
():
Boolean
=
this
[
ACCOUNT_LINKEDIN
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
meteorEnabled
():
Boolean
=
this
[
ACCOUNT_METEOR
]
?.
value
==
true
fun
PublicSettings
.
meteorEnabled
():
Boolean
=
this
[
ACCOUNT_METEOR
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
twitterEnabled
():
Boolean
=
this
[
ACCOUNT_TWITTER
]
?.
value
==
true
fun
PublicSettings
.
twitterEnabled
():
Boolean
=
this
[
ACCOUNT_TWITTER
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
gitlabEnabled
():
Boolean
=
this
[
ACCOUNT_GITLAB
]
?.
value
==
true
fun
PublicSettings
.
gitlabEnabled
():
Boolean
=
this
[
ACCOUNT_GITLAB
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
wordpressEnabled
():
Boolean
=
this
[
ACCOUNT_WORDPRESS
]
?.
value
==
true
fun
PublicSettings
.
wordpressEnabled
():
Boolean
=
this
[
ACCOUNT_WORDPRESS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>.
useRealName
():
Boolean
=
this
[
USE_REALNAME
]
?.
value
==
true
fun
PublicSettings
.
useRealName
():
Boolean
=
this
[
USE_REALNAME
]
?.
value
==
true
fun
PublicSettings
.
ldapEnabled
():
Boolean
=
this
[
LDAP_ENABLE
]
?.
value
==
true
// Message settings
// Message settings
fun
Map
<
String
,
Value
<
Any
>>
.
showDeletedStatus
():
Boolean
=
this
[
SHOW_DELETED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
showDeletedStatus
():
Boolean
=
this
[
SHOW_DELETED_STATUS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
showEditedStatus
():
Boolean
=
this
[
SHOW_EDITED_STATUS
]
?.
value
==
true
fun
PublicSettings
.
showEditedStatus
():
Boolean
=
this
[
SHOW_EDITED_STATUS
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessagePinning
():
Boolean
=
this
[
ALLOW_MESSAGE_PINNING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessagePinning
():
Boolean
=
this
[
ALLOW_MESSAGE_PINNING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessageEditing
():
Boolean
=
this
[
ALLOW_MESSAGE_EDITING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageEditing
():
Boolean
=
this
[
ALLOW_MESSAGE_EDITING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
allowedMessageDeleting
():
Boolean
=
this
[
ALLOW_MESSAGE_DELETING
]
?.
value
==
true
fun
PublicSettings
.
allowedMessageDeleting
():
Boolean
=
this
[
ALLOW_MESSAGE_DELETING
]
?.
value
==
true
fun
Map
<
String
,
Value
<
Any
>>
.
registrationEnabled
():
Boolean
{
fun
PublicSettings
.
registrationEnabled
():
Boolean
{
val
value
=
this
[
ACCOUNT_REGISTRATION
]
val
value
=
this
[
ACCOUNT_REGISTRATION
]
return
value
?.
value
==
"Public"
return
value
?.
value
==
"Public"
}
}
fun
Map
<
String
,
Value
<
Any
>>
.
uploadMimeTypeFilter
():
Array
<
String
>
{
fun
PublicSettings
.
uploadMimeTypeFilter
():
Array
<
String
>
{
val
values
=
this
[
UPLOAD_WHITELIST_MIMETYPES
]
?.
value
val
values
=
this
[
UPLOAD_WHITELIST_MIMETYPES
]
?.
value
values
?.
let
{
it
as
String
}
?.
split
(
","
)
?.
let
{
values
?.
let
{
it
as
String
}
?.
split
(
","
)
?.
let
{
return
it
.
mapToTypedArray
{
it
.
trim
()
}
return
it
.
mapToTypedArray
{
it
.
trim
()
}
...
@@ -80,8 +81,8 @@ fun Map<String, Value<Any>>.uploadMimeTypeFilter(): Array<String> {
...
@@ -80,8 +81,8 @@ fun Map<String, Value<Any>>.uploadMimeTypeFilter(): Array<String> {
return
arrayOf
(
"*/*"
)
return
arrayOf
(
"*/*"
)
}
}
fun
Map
<
String
,
Value
<
Any
>>
.
uploadMaxFileSize
():
Int
{
fun
PublicSettings
.
uploadMaxFileSize
():
Int
{
return
this
[
UPLOAD_MAX_FILE_SIZE
]
?.
value
?.
let
{
it
as
Int
}
?:
Int
.
MAX_VALUE
return
this
[
UPLOAD_MAX_FILE_SIZE
]
?.
value
?.
let
{
it
as
Int
}
?:
Int
.
MAX_VALUE
}
}
fun
Map
<
String
,
Value
<
Any
>>.
baseUrl
()
:
String
?
=
this
[
SITE_URL
]
?.
value
as
String
fun
PublicSettings
.
baseUrl
():
String
?
=
this
[
SITE_URL
]
?.
value
as
String
\ No newline at end of file
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.server.infraestructure
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.realtime.*
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.model.Message
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
timber.log.Timber
class
ConnectionManager
(
internal
val
client
:
RocketChatClient
)
{
private
val
statusChannelList
=
ArrayList
<
Channel
<
State
>>()
private
val
statusChannel
=
Channel
<
State
>()
private
var
connectJob
:
Job
?
=
null
private
val
roomAndSubscriptionChannels
=
ArrayList
<
Channel
<
StreamMessage
<
BaseRoom
>>>()
private
val
roomMessagesChannels
=
LinkedHashMap
<
String
,
Channel
<
Message
>>()
private
val
subscriptionIdMap
=
HashMap
<
String
,
String
>()
private
var
subscriptionId
:
String
?
=
null
private
var
roomsId
:
String
?
=
null
fun
connect
()
{
if
(
connectJob
?.
isActive
==
true
&&
(
state
!
is
State
.
Disconnected
))
{
Timber
.
d
(
"Already connected, just returning..."
)
return
}
// cleanup first
client
.
removeStateChannel
(
statusChannel
)
client
.
disconnect
()
connectJob
?.
cancel
()
// Connect and setup
client
.
addStateChannel
(
statusChannel
)
connectJob
=
launch
{
for
(
status
in
statusChannel
)
{
Timber
.
d
(
"Changing status to: $status"
)
when
(
status
)
{
is
State
.
Connected
->
{
client
.
subscribeSubscriptions
{
_
,
id
->
Timber
.
d
(
"Subscribed to subscriptions: $id"
)
subscriptionId
=
id
}
client
.
subscribeRooms
{
_
,
id
->
Timber
.
d
(
"Subscribed to rooms: $id"
)
roomsId
=
id
}
resubscribeRooms
()
}
is
State
.
Waiting
->
{
Timber
.
d
(
"Connection in: ${status.seconds}"
)
}
}
for
(
channel
in
statusChannelList
)
{
channel
.
send
(
status
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
room
in
client
.
roomsChannel
)
{
Timber
.
d
(
"GOT Room streamed"
)
for
(
channel
in
roomAndSubscriptionChannels
)
{
channel
.
send
(
room
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
subscription
in
client
.
subscriptionsChannel
)
{
Timber
.
d
(
"GOT Subscription streamed"
)
for
(
channel
in
roomAndSubscriptionChannels
)
{
channel
.
send
(
subscription
)
}
}
}
launch
(
parent
=
connectJob
)
{
for
(
message
in
client
.
messagesChannel
)
{
Timber
.
d
(
"Received new Message for room ${message.roomId}"
)
val
channel
=
roomMessagesChannels
[
message
.
roomId
]
channel
?.
send
(
message
)
}
}
client
.
connect
()
// Broadcast initial state...
val
state
=
client
.
state
for
(
channel
in
statusChannelList
)
{
channel
.
offer
(
state
)
}
}
private
fun
resubscribeRooms
()
{
roomMessagesChannels
.
toList
().
map
{
(
roomId
,
channel
)
->
client
.
subscribeRoomMessages
(
roomId
)
{
_
,
id
->
Timber
.
d
(
"Subscribed to $roomId: $id"
)
subscriptionIdMap
[
roomId
]
=
id
}
}
}
fun
disconnect
()
{
Timber
.
d
(
"ConnectionManager DISCONNECT"
)
client
.
removeStateChannel
(
statusChannel
)
client
.
disconnect
()
connectJob
?.
cancel
()
}
fun
addStatusChannel
(
channel
:
Channel
<
State
>)
=
statusChannelList
.
add
(
channel
)
fun
removeStatusChannel
(
channel
:
Channel
<
State
>)
=
statusChannelList
.
remove
(
channel
)
fun
addRoomsAndSubscriptionsChannel
(
channel
:
Channel
<
StreamMessage
<
BaseRoom
>>)
=
roomAndSubscriptionChannels
.
add
(
channel
)
fun
removeRoomsAndSubscriptionsChannel
(
channel
:
Channel
<
StreamMessage
<
BaseRoom
>>)
=
roomAndSubscriptionChannels
.
remove
(
channel
)
fun
subscribeRoomMessages
(
roomId
:
String
,
channel
:
Channel
<
Message
>)
{
val
oldSub
=
roomMessagesChannels
.
put
(
roomId
,
channel
)
if
(
oldSub
!=
null
)
{
Timber
.
d
(
"Room $roomId already subscribed..."
)
return
}
if
(
client
.
state
is
State
.
Connected
)
{
client
.
subscribeRoomMessages
(
roomId
)
{
_
,
id
->
Timber
.
d
(
"Subscribed to $roomId: $id"
)
subscriptionIdMap
[
roomId
]
=
id
}
}
}
fun
unsubscribeRoomMessages
(
roomId
:
String
)
{
val
sub
=
roomMessagesChannels
.
remove
(
roomId
)
if
(
sub
!=
null
)
{
val
id
=
subscriptionIdMap
.
remove
(
roomId
)
id
?.
let
{
client
.
unsubscribe
(
it
)
}
}
}
}
suspend
fun
ConnectionManager
.
chatRooms
(
timestamp
:
Long
=
0
,
filterCustom
:
Boolean
=
true
)
=
client
.
chatRooms
(
timestamp
,
filterCustom
)
val
ConnectionManager
.
state
:
State
get
()
=
client
.
state
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManagerFactory.kt
0 → 100644
View file @
c1871a62
package
chat.rocket.android.server.infraestructure
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Singleton
@Singleton
class
ConnectionManagerFactory
@Inject
constructor
(
private
val
factory
:
RocketChatClientFactory
)
{
private
val
cache
=
HashMap
<
String
,
ConnectionManager
>()
fun
create
(
url
:
String
):
ConnectionManager
{
cache
[
url
]
?.
let
{
Timber
.
d
(
"Returning CACHED Manager for: $url"
)
return
it
}
Timber
.
d
(
"Returning FRESH Manager for: $url"
)
val
manager
=
ConnectionManager
(
factory
.
create
(
url
))
cache
[
url
]
=
manager
return
manager
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/RocketChatClientFactory.kt
View file @
c1871a62
...
@@ -28,7 +28,7 @@ class RocketChatClientFactory @Inject constructor(val okHttpClient: OkHttpClient
...
@@ -28,7 +28,7 @@ class RocketChatClientFactory @Inject constructor(val okHttpClient: OkHttpClient
}
}
Timber
.
d
(
"Returning NEW client for: $url"
)
Timber
.
d
(
"Returning NEW client for: $url"
)
cache
.
put
(
url
,
client
)
cache
[
url
]
=
client
return
client
return
client
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/SharedPreferencesSettingsRepository.kt
View file @
c1871a62
...
@@ -2,19 +2,19 @@ package chat.rocket.android.server.infraestructure
...
@@ -2,19 +2,19 @@ package chat.rocket.android.server.infraestructure
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
import
chat.rocket.android.infrastructure.LocalRepository.Companion.SETTINGS_KEY
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.core.internal.SettingsAdapter
import
chat.rocket.core.internal.SettingsAdapter
import
chat.rocket.core.model.Value
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
class
SharedPreferencesSettingsRepository
(
private
val
localRepository
:
LocalRepository
)
:
SettingsRepository
{
private
val
adapter
=
SettingsAdapter
().
lenient
()
private
val
adapter
=
SettingsAdapter
().
lenient
()
override
fun
save
(
url
:
String
,
settings
:
Map
<
String
,
Value
<
Any
>>
)
{
override
fun
save
(
url
:
String
,
settings
:
PublicSettings
)
{
localRepository
.
save
(
"$SETTINGS_KEY$url"
,
adapter
.
toJson
(
settings
))
localRepository
.
save
(
"$SETTINGS_KEY$url"
,
adapter
.
toJson
(
settings
))
}
}
override
fun
get
(
url
:
String
):
Map
<
String
,
Value
<
Any
>>
?
{
override
fun
get
(
url
:
String
):
PublicSettings
?
{
val
settings
=
localRepository
.
get
(
"$SETTINGS_KEY$url"
)
val
settings
=
localRepository
.
get
(
"$SETTINGS_KEY$url"
)
settings
?.
let
{
settings
?.
let
{
return
adapter
.
fromJson
(
it
)
return
adapter
.
fromJson
(
it
)
...
...
app/src/main/java/chat/rocket/android/util/extensions/Animation.kt
View file @
c1871a62
...
@@ -12,7 +12,12 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
...
@@ -12,7 +12,12 @@ fun View.rotateBy(value: Float, duration: Long = 100) {
.
start
()
.
start
()
}
}
fun
View
.
fadeIn
(
startValue
:
Float
,
finishValue
:
Float
,
duration
:
Long
=
200
)
{
fun
View
.
fadeIn
(
startValue
:
Float
=
0f
,
finishValue
:
Float
=
1f
,
duration
:
Long
=
200
)
{
if
(
alpha
==
finishValue
)
{
setVisible
(
true
)
return
}
animate
()
animate
()
.
alpha
(
startValue
)
.
alpha
(
startValue
)
.
setDuration
(
duration
)
.
setDuration
(
duration
)
...
@@ -27,7 +32,12 @@ fun View.fadeIn(startValue: Float, finishValue: Float, duration: Long = 200) {
...
@@ -27,7 +32,12 @@ fun View.fadeIn(startValue: Float, finishValue: Float, duration: Long = 200) {
setVisible
(
true
)
setVisible
(
true
)
}
}
fun
View
.
fadeOut
(
startValue
:
Float
,
finishValue
:
Float
,
duration
:
Long
=
200
)
{
fun
View
.
fadeOut
(
startValue
:
Float
=
1f
,
finishValue
:
Float
=
0f
,
duration
:
Long
=
200
)
{
if
(
alpha
==
finishValue
)
{
setVisible
(
false
)
return
}
animate
()
animate
()
.
alpha
(
startValue
)
.
alpha
(
startValue
)
.
setDuration
(
duration
)
.
setDuration
(
duration
)
...
...
app/src/main/java/chat/rocket/android/util/extensions/Text.kt
View file @
c1871a62
...
@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions
...
@@ -3,6 +3,7 @@ package chat.rocket.android.util.extensions
import
android.text.Spannable
import
android.text.Spannable
import
android.text.Spanned
import
android.text.Spanned
import
android.text.TextUtils
import
android.text.TextUtils
import
android.util.Patterns
import
android.widget.EditText
import
android.widget.EditText
import
android.widget.TextView
import
android.widget.TextView
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.android.widget.emoji.EmojiParser
...
@@ -31,6 +32,8 @@ fun EditText.erase() {
...
@@ -31,6 +32,8 @@ fun EditText.erase() {
}
}
}
}
fun
String
.
isEmailValid
():
Boolean
=
Patterns
.
EMAIL_ADDRESS
.
matcher
(
this
).
matches
()
var
TextView
.
textContent
:
String
var
TextView
.
textContent
:
String
get
()
=
text
.
toString
()
get
()
=
text
.
toString
()
set
(
value
)
{
set
(
value
)
{
...
...
app/src/main/java/chat/rocket/android/util/extensions/Ui.kt
View file @
c1871a62
...
@@ -6,6 +6,8 @@ import android.support.annotation.LayoutRes
...
@@ -6,6 +6,8 @@ import android.support.annotation.LayoutRes
import
android.support.annotation.StringRes
import
android.support.annotation.StringRes
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
...
@@ -55,3 +57,12 @@ fun Activity.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) = To
...
@@ -55,3 +57,12 @@ fun Activity.showToast(message: String, duration: Int = Toast.LENGTH_SHORT) = To
fun
Fragment
.
showToast
(
@StringRes
resource
:
Int
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
showToast
(
getString
(
resource
),
duration
)
fun
Fragment
.
showToast
(
@StringRes
resource
:
Int
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
showToast
(
getString
(
resource
),
duration
)
fun
Fragment
.
showToast
(
message
:
String
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
activity
!!
.
showToast
(
message
,
duration
)
fun
Fragment
.
showToast
(
message
:
String
,
duration
:
Int
=
Toast
.
LENGTH_SHORT
)
=
activity
!!
.
showToast
(
message
,
duration
)
fun
RecyclerView
.
isAtBottom
():
Boolean
{
val
manager
:
RecyclerView
.
LayoutManager
?
=
layoutManager
if
(
manager
is
LinearLayoutManager
)
{
return
manager
.
findFirstVisibleItemPosition
()
==
0
}
return
false
// or true??? we can't determine the first visible item.
}
\ No newline at end of file
app/src/main/res/drawable/ic_gitlab.xml
View file @
c1871a62
...
@@ -19,58 +19,98 @@
...
@@ -19,58 +19,98 @@
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M121.13,18.01L119.97,21.57L117.68,28.64C117.56,29 117.04,29 116.92,28.64L114.63,21.57L107,21.57L104.71,28.64C104.59,29 104.08,29 103.96,28.64L101.66,21.57L100.5,18.01C100.4,17.68 100.51,17.33 100.79,17.12L110.82,9.84L120.84,17.12C121.12,17.33 121.23,17.68 121.13,18.01"
android:pathData=
"M122.13,22.99L120.97,19.43C119.6,15.19 118.83,12.83 118.68,12.36C118.56,12 118.04,12 117.92,12.36C117.77,12.83 117,15.19 115.63,19.43L108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16C117.83,26.79 121.17,24.37 121.84,23.88C122.12,23.67 122.23,23.32 122.13,22.99"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M122.13,22.99L120.97,19.43C119.6,15.19 118.83,12.83 118.68,12.36C118.56,12 118.04,12 117.92,12.36C117.77,12.83 117,15.19 115.63,19.43L108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16C117.83,26.79 121.17,24.37 121.84,23.88C122.12,23.67 122.23,23.32 122.13,22.99"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M110.82,9.84l0,0l3.81,11.73l-7.62,0z"
android:pathData=
"M111.82,31.16L111.82,31.16L115.63,19.43L108.01,19.43L111.82,31.16Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M111.82,31.16L111.82,31.16L115.63,19.43L108.01,19.43L111.82,31.16Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M110.82,9.84l-3.81,11.73l-5.34,0z"
android:pathData=
"M111.82,31.16L108.01,19.43L102.67,19.43L111.82,31.16Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M111.82,31.16L108.01,19.43L102.67,19.43L111.82,31.16Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M101.66,21.57L101.66,21.57L100.5,18.01C100.4,17.68 100.51,17.33 100.79,17.12L110.82,9.84L101.66,21.57L101.66,21.57Z"
android:pathData=
"M102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16L102.66,19.43L102.66,19.43L102.66,19.43Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M102.66,19.43C101.96,21.57 101.58,22.75 101.5,22.99C101.4,23.32 101.51,23.67 101.79,23.88C102.46,24.37 105.8,26.79 111.82,31.16L102.66,19.43L102.66,19.43L102.66,19.43Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M101.66,21.57L107,21.57L104.71,28.64C104.59,29 104.08,29 103.96,28.64L101.66,21.57L101.66,21.57Z"
android:pathData=
"M108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43L102.66,19.43L108,19.43Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M108,19.43C106.63,15.19 105.86,12.83 105.71,12.36C105.59,12 105.08,12 104.96,12.36C104.81,12.83 104.04,15.19 102.66,19.43L102.66,19.43L108,19.43Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M110.82,9.84l3.81,11.73l5.34,0z"
android:pathData=
"M111.82,31.16L115.63,19.43L120.97,19.43L111.82,31.16Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M111.82,31.16L115.63,19.43L120.97,19.43L111.82,31.16Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M119.97,21.57L119.97,21.57L121.13,18.01C121.24,17.68 121.12,17.33 120.84,17.12L110.82,9.84L119.97,21.57L119.97,21.57Z"
android:pathData=
"M120.97,19.43C121.67,21.57 122.05,22.75 122.13,22.99C122.24,23.32 122.12,23.67 121.84,23.88C121.17,24.37 117.83,26.79 111.82,31.16L120.97,19.43L120.97,19.43L120.97,19.43Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M120.97,19.43C121.67,21.57 122.05,22.75 122.13,22.99C122.24,23.32 122.12,23.67 121.84,23.88C121.17,24.37 117.83,26.79 111.82,31.16L120.97,19.43L120.97,19.43L120.97,19.43Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
<path
<path
android:fillColor=
"#FFFFFF"
android:fillAlpha=
"1"
android:fillType=
"nonZero"
android:fillColor=
"#ffffff"
android:pathData=
"M119.97,21.57L114.63,21.57L116.92,28.64C117.04,29 117.56,29 117.68,28.64L119.97,21.57L119.97,21.57Z"
android:pathData=
"M115.63,19.43C117,15.19 117.77,12.83 117.92,12.36C118.04,12 118.56,12 118.68,12.36C118.83,12.83 119.6,15.19 120.97,19.43L120.97,19.43L115.63,19.43Z"
/>
android:strokeColor=
"#00000000"
<path
android:fillAlpha=
"0"
android:fillColor=
"#FF000000"
android:pathData=
"M115.63,19.43C117,15.19 117.77,12.83 117.92,12.36C118.04,12 118.56,12 118.68,12.36C118.83,12.83 119.6,15.19 120.97,19.43L120.97,19.43L115.63,19.43Z"
android:strokeAlpha=
"0"
android:strokeColor=
"#000000"
android:strokeWidth=
"1"
/>
android:strokeWidth=
"1"
/>
</vector>
</vector>
\ No newline at end of file
app/src/main/res/layout/emoji_popup_layout.xml
View file @
c1871a62
...
@@ -60,6 +60,7 @@
...
@@ -60,6 +60,7 @@
android:clickable=
"true"
android:clickable=
"true"
android:focusable=
"true"
android:focusable=
"true"
android:padding=
"8dp"
android:padding=
"8dp"
android:visibility=
"invisible"
android:src=
"@drawable/ic_search_gray_24px"
/>
android:src=
"@drawable/ic_search_gray_24px"
/>
<ImageView
<ImageView
...
@@ -75,5 +76,4 @@
...
@@ -75,5 +76,4 @@
android:src=
"@drawable/ic_backspace_gray_24dp"
/>
android:src=
"@drawable/ic_backspace_gray_24dp"
/>
</RelativeLayout>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_authentication_server.xml
View file @
c1871a62
...
@@ -32,7 +32,7 @@
...
@@ -32,7 +32,7 @@
android:cursorVisible=
"false"
android:cursorVisible=
"false"
android:hint=
"@string/default_server"
android:hint=
"@string/default_server"
android:imeOptions=
"actionDone"
android:imeOptions=
"actionDone"
android:digits=
"0123456789abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
.-/:"
android:digits=
"0123456789abcdefghijklmnopqrstuvwxyz.-/:"
android:inputType=
"textUri"
android:inputType=
"textUri"
android:paddingEnd=
"0dp"
android:paddingEnd=
"0dp"
android:paddingStart=
"0dp"
/>
android:paddingStart=
"0dp"
/>
...
...
app/src/main/res/layout/fragment_chat_room.xml
View file @
c1871a62
...
@@ -4,7 +4,8 @@
...
@@ -4,7 +4,8 @@
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/root_layout"
android:id=
"@+id/root_layout"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
android:layout_height=
"match_parent"
tools:context=
".chatroom.ui.ChatRoomFragment"
>
<com.wang.avi.AVLoadingIndicatorView
<com.wang.avi.AVLoadingIndicatorView
android:id=
"@+id/view_loading"
android:id=
"@+id/view_loading"
...
@@ -53,4 +54,19 @@
...
@@ -53,4 +54,19 @@
android:layout_margin=
"5dp"
android:layout_margin=
"5dp"
android:visibility=
"gone"
/>
android:visibility=
"gone"
/>
<TextView
android:id=
"@+id/connection_status_text"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:background=
"@color/colorPrimary"
android:elevation=
"4dp"
android:textColor=
"@color/white"
android:gravity=
"center"
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:visibility=
"gone"
android:alpha=
"0"
tools:alpha=
"1"
tools:visibility=
"visible"
tools:text=
"connected"
/>
</RelativeLayout>
</RelativeLayout>
app/src/main/res/layout/fragment_chat_rooms.xml
View file @
c1871a62
...
@@ -29,4 +29,19 @@
...
@@ -29,4 +29,19 @@
android:visibility=
"gone"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/connection_status_text"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:background=
"@color/colorPrimary"
android:elevation=
"4dp"
android:textColor=
"@color/white"
android:gravity=
"center"
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:visibility=
"gone"
android:alpha=
"0"
tools:alpha=
"1"
tools:visibility=
"visible"
tools:text=
"connected"
/>
</RelativeLayout>
</RelativeLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_member_bottom_sheet.xml
0 → 100644
View file @
c1871a62
<?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:id=
"@+id/member_bottom_sheet"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:paddingBottom=
"16dp"
app:layout_behavior=
"android.support.design.widget.BottomSheetBehavior"
>
<com.facebook.drawee.view.SimpleDraweeView
android:id=
"@+id/image_bottom_sheet_avatar"
android:layout_width=
"match_parent"
android:layout_height=
"200dp"
/>
<LinearLayout
android:id=
"@+id/name_and_username_container"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"@color/colorBackgroundMemberContainer"
android:orientation=
"vertical"
android:paddingBottom=
"10dp"
android:paddingStart=
"16dp"
android:paddingTop=
"10dp"
app:layout_constraintBottom_toBottomOf=
"@+id/image_bottom_sheet_avatar"
app:layout_constraintLeft_toLeftOf=
"parent"
>
<TextView
android:id=
"@+id/text_bottom_sheet_member_name"
style=
"@style/TextAppearance.AppCompat.Title"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:textColor=
"@color/white"
tools:text=
"Ronald Perkins"
/>
<TextView
android:id=
"@+id/text_bottom_sheet_member_username"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"5dp"
android:textColor=
"@color/white"
tools:text=
"\@ronaldPerkins"
/>
</LinearLayout>
<TextView
android:id=
"@+id/text_email_address"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"16dp"
android:text=
"@string/msg_email_address"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/image_bottom_sheet_avatar"
/>
<TextView
android:id=
"@+id/text_member_email_address"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"10dp"
android:textColor=
"@color/colorPrimaryText"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_email_address"
tools:text=
"ronald@perkins.com"
/>
<TextView
android:id=
"@+id/text_utc"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"16dp"
android:text=
"@string/msg_utc_offset"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_member_email_address"
/>
<TextView
android:id=
"@+id/text_member_utc"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"10dp"
android:textColor=
"@color/colorPrimaryText"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_utc"
tools:text=
"+01:00"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_members.xml
0 → 100644
View file @
c1871a62
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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=
".members.ui.MembersFragment"
>
<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:layout_gravity=
"center"
app:indicatorColor=
"@color/black"
app:indicatorName=
"BallPulseIndicator"
/>
</android.support.design.widget.CoordinatorLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_profile.xml
View file @
c1871a62
...
@@ -49,7 +49,7 @@
...
@@ -49,7 +49,7 @@
<EditText
<EditText
android:id=
"@+id/text_avatar_url"
android:id=
"@+id/text_avatar_url"
style=
"@style/
EditText.Profile
"
style=
"@style/
Profile.EditText
"
android:layout_marginTop=
"16dp"
android:layout_marginTop=
"16dp"
android:drawableStart=
"@drawable/ic_link_black_24dp"
android:drawableStart=
"@drawable/ic_link_black_24dp"
android:hint=
"@string/msg_avatar_url"
android:hint=
"@string/msg_avatar_url"
...
...
app/src/main/res/layout/item_member.xml
0 → 100644
View file @
c1871a62
<?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=
"wrap_content"
android:layout_marginBottom=
"6dp"
android:layout_marginEnd=
"@dimen/screen_edge_left_and_right_margins"
android:layout_marginStart=
"@dimen/screen_edge_left_and_right_margins"
android:layout_marginTop=
"6dp"
>
<include
android:id=
"@+id/layout_avatar"
layout=
"@layout/avatar"
android:layout_width=
"40dp"
android:layout_height=
"40dp"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/text_member"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
app:layout_constraintBottom_toBottomOf=
"@+id/layout_avatar"
app:layout_constraintLeft_toRightOf=
"@+id/layout_avatar"
app:layout_constraintTop_toTopOf=
"@+id/layout_avatar"
tools:text=
"Ronald Perkins"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/message_attachment.xml
View file @
c1871a62
...
@@ -5,8 +5,8 @@
...
@@ -5,8 +5,8 @@
android:id=
"@+id/attachment_container"
android:id=
"@+id/attachment_container"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:
layout_margin
Start=
"72dp"
android:
padding
Start=
"72dp"
android:
layout_margin
End=
"@dimen/screen_edge_left_and_right_margins"
android:
padding
End=
"@dimen/screen_edge_left_and_right_margins"
android:orientation=
"vertical"
>
android:orientation=
"vertical"
>
<com.facebook.drawee.view.SimpleDraweeView
<com.facebook.drawee.view.SimpleDraweeView
...
...
app/src/main/res/layout/message_url_preview.xml
View file @
c1871a62
...
@@ -6,8 +6,8 @@
...
@@ -6,8 +6,8 @@
android:id=
"@+id/url_preview_layout"
android:id=
"@+id/url_preview_layout"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:
layout_margin
Start=
"72dp"
android:
padding
Start=
"72dp"
android:
layout_margin
End=
"24dp"
>
android:
padding
End=
"24dp"
>
<com.facebook.drawee.view.SimpleDraweeView
<com.facebook.drawee.view.SimpleDraweeView
android:id=
"@+id/image_preview"
android:id=
"@+id/image_preview"
...
...
app/src/main/res/menu/chatroom_actions.xml
View file @
c1871a62
...
@@ -2,6 +2,11 @@
...
@@ -2,6 +2,11 @@
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
<menu
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
xmlns:app=
"http://schemas.android.com/apk/res-auto"
>
<item
android:id=
"@+id/action_members_list"
android:title=
"@string/title_members_list"
app:showAsAction=
"never"
/>
<item
<item
android:id=
"@+id/action_pinned_messages"
android:id=
"@+id/action_pinned_messages"
android:title=
"@string/title_pinned_messages"
android:title=
"@string/title_pinned_messages"
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
c1871a62
...
@@ -7,6 +7,7 @@
...
@@ -7,6 +7,7 @@
<string
name=
"title_legal_terms"
>
Termos Legais
</string>
<string
name=
"title_legal_terms"
>
Termos Legais
</string>
<string
name=
"title_chats"
>
Chats
</string>
<string
name=
"title_chats"
>
Chats
</string>
<string
name=
"title_profile"
>
Perfil
</string>
<string
name=
"title_profile"
>
Perfil
</string>
<string
name=
"title_members"
>
Membros (%d)
</string>
<string
name=
"title_settings"
>
Configurações
</string>
<string
name=
"title_settings"
>
Configurações
</string>
<string
name=
"title_password"
>
Alterar senha
</string>
<string
name=
"title_password"
>
Alterar senha
</string>
<string
name=
"title_update_profile"
>
Editar perfil
</string>
<string
name=
"title_update_profile"
>
Editar perfil
</string>
...
@@ -59,6 +60,8 @@
...
@@ -59,6 +60,8 @@
<string
name=
"msg_content_description_show_attachment_options"
>
Mostrar opções de anexo
</string>
<string
name=
"msg_content_description_show_attachment_options"
>
Mostrar opções de anexo
</string>
<string
name=
"msg_you"
>
Você
</string>
<string
name=
"msg_you"
>
Você
</string>
<string
name=
"msg_unknown"
>
Desconhecido
</string>
<string
name=
"msg_unknown"
>
Desconhecido
</string>
<string
name=
"msg_email_address"
>
Endereço de e-mail
</string>
<string
name=
"msg_utc_offset"
>
Deslocamento de UTC
</string>
<string
name=
"msg_new_password"
>
Informe a nova senha
</string>
<string
name=
"msg_new_password"
>
Informe a nova senha
</string>
<string
name=
"msg_confirm_password"
>
Confirme a nova senha
</string>
<string
name=
"msg_confirm_password"
>
Confirme a nova senha
</string>
...
@@ -89,10 +92,20 @@
...
@@ -89,10 +92,20 @@
<string
name=
"permission_deleting_not_allowed"
>
Remoção não permitida
</string>
<string
name=
"permission_deleting_not_allowed"
>
Remoção não permitida
</string>
<string
name=
"permission_pinning_not_allowed"
>
Fixar não permitido
</string>
<string
name=
"permission_pinning_not_allowed"
>
Fixar não permitido
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Lista de Membros
</string>
<!-- Pinned Messages -->
<!-- Pinned Messages -->
<string
name=
"title_pinned_messages"
>
Mensagens Pinadas
</string>
<string
name=
"title_pinned_messages"
>
Mensagens Pinadas
</string>
<!-- Upload Messages -->
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)
</string>
<string
name=
"max_file_size_exceeded"
>
Tamanho de arquivo (%1$d bytes) excedeu tamanho máximo de upload (%2$d bytes)
</string>
<!-- Socket status -->
<string
name=
"status_connected"
>
conectado
</string>
<string
name=
"status_disconnected"
>
desconetado
</string>
<string
name=
"status_connecting"
>
conectando
</string>
<string
name=
"status_authenticating"
>
autenticando
</string>
<string
name=
"status_disconnecting"
>
desconectando
</string>
<string
name=
"status_waiting"
>
conectando em %d segundos
</string>
</resources>
</resources>
\ No newline at end of file
app/src/main/res/values/colors.xml
View file @
c1871a62
...
@@ -25,6 +25,8 @@
...
@@ -25,6 +25,8 @@
<color
name=
"colorDim"
>
#99000000
</color>
<color
name=
"colorDim"
>
#99000000
</color>
<color
name=
"colorBackgroundMemberContainer"
>
#4D000000
</color>
<color
name=
"white"
>
#FFFFFFFF
</color>
<color
name=
"white"
>
#FFFFFFFF
</color>
<color
name=
"black"
>
#FF000000
</color>
<color
name=
"black"
>
#FF000000
</color>
<color
name=
"red"
>
#FFFF0000
</color>
<color
name=
"red"
>
#FFFF0000
</color>
...
...
app/src/main/res/values/strings.xml
View file @
c1871a62
...
@@ -8,6 +8,7 @@
...
@@ -8,6 +8,7 @@
<string
name=
"title_legal_terms"
>
Legal Terms
</string>
<string
name=
"title_legal_terms"
>
Legal Terms
</string>
<string
name=
"title_chats"
>
Chats
</string>
<string
name=
"title_chats"
>
Chats
</string>
<string
name=
"title_profile"
>
Profile
</string>
<string
name=
"title_profile"
>
Profile
</string>
<string
name=
"title_members"
>
Members (%d)
</string>
<string
name=
"title_settings"
>
Settings
</string>
<string
name=
"title_settings"
>
Settings
</string>
<string
name=
"title_password"
>
Change Password
</string>
<string
name=
"title_password"
>
Change Password
</string>
<string
name=
"title_update_profile"
>
Update profile
</string>
<string
name=
"title_update_profile"
>
Update profile
</string>
...
@@ -61,6 +62,8 @@
...
@@ -61,6 +62,8 @@
<string
name=
"msg_content_description_show_attachment_options"
>
Show attachment options
</string>
<string
name=
"msg_content_description_show_attachment_options"
>
Show attachment options
</string>
<string
name=
"msg_you"
>
You
</string>
<string
name=
"msg_you"
>
You
</string>
<string
name=
"msg_unknown"
>
Unknown
</string>
<string
name=
"msg_unknown"
>
Unknown
</string>
<string
name=
"msg_email_address"
>
E-mail address
</string>
<string
name=
"msg_utc_offset"
>
UTC offset
</string>
<string
name=
"msg_new_password"
>
Enter New Password
</string>
<string
name=
"msg_new_password"
>
Enter New Password
</string>
<string
name=
"msg_confirm_password"
>
Confirm New Password
</string>
<string
name=
"msg_confirm_password"
>
Confirm New Password
</string>
...
@@ -91,10 +94,21 @@
...
@@ -91,10 +94,21 @@
<string
name=
"permission_deleting_not_allowed"
>
Deleting 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_pinning_not_allowed"
>
Pinning is not allowed
</string>
<!-- Members List -->
<string
name=
"title_members_list"
>
Members List
</string>
<!-- Pinned Messages -->
<!-- Pinned Messages -->
<string
name=
"title_pinned_messages"
>
Pinned Messages
</string>
<string
name=
"title_pinned_messages"
>
Pinned Messages
</string>
<!-- Upload Messages -->
<!-- Upload Messages -->
<string
name=
"max_file_size_exceeded"
>
File size %1$d bytes exceeded max upload size of %2$d bytes
</string>
<string
name=
"max_file_size_exceeded"
>
File size %1$d bytes exceeded max upload size of %2$d bytes
</string>
<!-- Socket status -->
<string
name=
"status_connected"
>
connected
</string>
<string
name=
"status_disconnected"
>
disconnected
</string>
<string
name=
"status_connecting"
>
connecting
</string>
<string
name=
"status_authenticating"
>
authenticating
</string>
<string
name=
"status_disconnecting"
>
disconnecting
</string>
<string
name=
"status_waiting"
>
connecting in %d seconds
</string>
</resources>
</resources>
\ No newline at end of file
dependencies.gradle
View file @
c1871a62
...
@@ -11,6 +11,7 @@ ext {
...
@@ -11,6 +11,7 @@ ext {
// Main dependencies
// Main dependencies
support
:
'27.0.2'
,
support
:
'27.0.2'
,
constraintLayout
:
'1.0.2'
,
constraintLayout
:
'1.0.2'
,
androidKtx
:
'0.1'
,
dagger
:
'2.14.1'
,
dagger
:
'2.14.1'
,
exoPlayer
:
'2.6.0'
,
exoPlayer
:
'2.6.0'
,
playServices
:
'11.8.0'
,
playServices
:
'11.8.0'
,
...
@@ -49,6 +50,8 @@ ext {
...
@@ -49,6 +50,8 @@ ext {
constraintLayout
:
"com.android.support.constraint:constraint-layout:${versions.constraintLayout}"
,
constraintLayout
:
"com.android.support.constraint:constraint-layout:${versions.constraintLayout}"
,
cardView
:
"com.android.support:cardview-v7:${versions.support}"
,
cardView
:
"com.android.support:cardview-v7:${versions.support}"
,
androidKtx
:
"androidx.core:core-ktx:${versions.androidKtx}"
,
dagger
:
"com.google.dagger:dagger:${versions.dagger}"
,
dagger
:
"com.google.dagger:dagger:${versions.dagger}"
,
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment