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
e3989237
Unverified
Commit
e3989237
authored
Mar 30, 2018
by
divyanshu bhargava
Committed by
GitHub
Mar 30, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop-2.x' into lang-hindi
parents
5f4cce90
2176c0ce
Changes
38
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
38 changed files
with
538 additions
and
379 deletions
+538
-379
RocketChatApplication.kt
...ain/java/chat/rocket/android/app/RocketChatApplication.kt
+0
-1
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+16
-8
CommandSuggestionsAdapter.kt
...ket/android/chatroom/adapter/CommandSuggestionsAdapter.kt
+1
-1
MessageAttachmentViewHolder.kt
...t/android/chatroom/adapter/MessageAttachmentViewHolder.kt
+29
-0
PeopleSuggestionsAdapter.kt
...cket/android/chatroom/adapter/PeopleSuggestionsAdapter.kt
+33
-6
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+1
-2
ActionSnackbar.kt
...in/java/chat/rocket/android/chatroom/ui/ActionSnackbar.kt
+8
-12
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+49
-7
AudioAttachmentViewModel.kt
...et/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
+3
-2
BaseViewModel.kt
...a/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
+1
-0
ImageAttachmentViewModel.kt
...et/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
+2
-1
MessageAttachmentViewModel.kt
.../android/chatroom/viewmodel/MessageAttachmentViewModel.kt
+24
-0
MessageViewModel.kt
...hat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
+1
-0
UrlPreviewViewModel.kt
.../rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
+2
-1
VideoAttachmentViewModel.kt
...et/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
+2
-1
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+50
-108
PeopleSuggestionViewModel.kt
...hatroom/viewmodel/suggestion/PeopleSuggestionViewModel.kt
+1
-1
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+21
-6
ChatRoomsAdapter.kt
...java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
+23
-9
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+12
-5
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+58
-159
LocalRepository.kt
...ava/chat/rocket/android/infrastructure/LocalRepository.kt
+3
-1
ProfileFragment.kt
...in/java/chat/rocket/android/profile/ui/ProfileFragment.kt
+5
-3
CompletionStrategy.kt
...roid/widget/autocompletion/strategy/CompletionStrategy.kt
+1
-0
StringMatchingCompletionStrategy.kt
...letion/strategy/regex/StringMatchingCompletionStrategy.kt
+19
-4
TrieCompletionStrategy.kt
...et/autocompletion/strategy/trie/TrieCompletionStrategy.kt
+4
-0
SuggestionsAdapter.kt
...et/android/widget/autocompletion/ui/SuggestionsAdapter.kt
+14
-3
SuggestionsView.kt
...ocket/android/widget/autocompletion/ui/SuggestionsView.kt
+17
-16
EmojiParser.kt
...main/java/chat/rocket/android/widget/emoji/EmojiParser.kt
+19
-6
EmojiRepository.kt
.../java/chat/rocket/android/widget/emoji/EmojiRepository.kt
+6
-7
EmojiTypefaceSpan.kt
...ava/chat/rocket/android/widget/emoji/EmojiTypefaceSpan.kt
+5
-5
quote_vertical_bar.xml
app/src/main/res/drawable/quote_vertical_bar.xml
+3
-1
item_message_attachment.xml
app/src/main/res/layout/item_message_attachment.xml
+70
-0
message_action_bar.xml
app/src/main/res/layout/message_action_bar.xml
+11
-0
suggestion_member_item.xml
app/src/main/res/layout/suggestion_member_item.xml
+4
-3
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+8
-0
strings.xml
app/src/main/res/values/strings.xml
+8
-0
styles.xml
app/src/main/res/values/styles.xml
+4
-0
No files found.
app/src/main/java/chat/rocket/android/app/RocketChatApplication.kt
View file @
e3989237
...
...
@@ -24,7 +24,6 @@ import chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.widget.emoji.EmojiRepository
import
chat.rocket.common.model.Token
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.model.Value
import
com.crashlytics.android.Crashlytics
import
com.crashlytics.android.core.CrashlyticsCore
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
e3989237
...
...
@@ -49,6 +49,10 @@ class ChatRoomAdapter(
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
UrlPreviewViewHolder
(
view
,
actionsListener
,
reactionListener
)
}
BaseViewModel
.
ViewType
.
MESSAGE_ATTACHMENT
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
item_message_attachment
)
MessageAttachmentViewHolder
(
view
,
actionsListener
,
reactionListener
)
}
else
->
{
throw
InvalidParameterException
(
"TODO - implement for ${viewType.toViewType()}"
)
}
...
...
@@ -87,6 +91,7 @@ class ChatRoomAdapter(
is
AudioAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
AudioAttachmentViewModel
)
is
VideoAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
VideoAttachmentViewModel
)
is
UrlPreviewViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
UrlPreviewViewModel
)
is
MessageAttachmentViewHolder
->
holder
.
bind
(
dataSet
[
position
]
as
MessageAttachmentViewModel
)
}
}
...
...
@@ -117,19 +122,22 @@ class ChatRoomAdapter(
fun
updateItem
(
message
:
BaseViewModel
<
*
>)
{
var
index
=
dataSet
.
indexOfLast
{
it
.
messageId
==
message
.
messageId
}
val
indexOf
Firs
t
=
dataSet
.
indexOfFirst
{
it
.
messageId
==
message
.
messageId
}
val
indexOf
Nex
t
=
dataSet
.
indexOfFirst
{
it
.
messageId
==
message
.
messageId
}
Timber
.
d
(
"index: $index"
)
if
(
index
>
-
1
)
{
dataSet
[
index
]
=
message
notifyItemChanged
(
index
)
while
(
dataSet
[
index
].
nextDownStreamMessage
!=
null
)
{
dataSet
[
index
].
nextDownStreamMessage
!!
.
reactions
=
message
.
reactions
notifyItemChanged
(--
index
)
dataSet
.
forEachIndexed
{
index
,
viewModel
->
if
(
viewModel
.
messageId
==
message
.
messageId
)
{
if
(
viewModel
.
nextDownStreamMessage
==
null
)
{
viewModel
.
reactions
=
message
.
reactions
}
notifyItemChanged
(
index
)
}
}
// Delete message only if current is a system message update, i.e.: Message Removed
if
(
message
.
message
.
isSystemMessage
()
&&
indexOf
First
>
-
1
&&
indexOfFirs
t
!=
index
)
{
dataSet
.
removeAt
(
indexOf
Firs
t
)
notifyItemRemoved
(
indexOf
Firs
t
)
if
(
message
.
message
.
isSystemMessage
()
&&
indexOf
Next
>
-
1
&&
indexOfNex
t
!=
index
)
{
dataSet
.
removeAt
(
indexOf
Nex
t
)
notifyItemRemoved
(
indexOf
Nex
t
)
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/CommandSuggestionsAdapter.kt
View file @
e3989237
...
...
@@ -12,7 +12,7 @@ import chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
class
CommandSuggestionsAdapter
:
SuggestionsAdapter
<
CommandSuggestionsViewHolder
>(
token
=
"/"
,
constraint
=
CONSTRAINT_BOUND_TO_START
,
threshold
=
UNLIMITED_RESULT_COUNT
)
{
constraint
=
CONSTRAINT_BOUND_TO_START
,
threshold
=
RESULT_COUNT_UNLIMITED
)
{
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
CommandSuggestionsViewHolder
{
val
view
=
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
suggestion_command_item
,
parent
,
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageAttachmentViewHolder.kt
0 → 100644
View file @
e3989237
package
chat.rocket.android.chatroom.adapter
import
android.text.method.LinkMovementMethod
import
android.view.View
import
chat.rocket.android.chatroom.viewmodel.MessageAttachmentViewModel
import
chat.rocket.android.widget.emoji.EmojiReactionListener
import
kotlinx.android.synthetic.main.item_message.view.*
class
MessageAttachmentViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
MessageAttachmentViewModel
>(
itemView
,
listener
,
reactionListener
)
{
init
{
with
(
itemView
)
{
text_content
.
movementMethod
=
LinkMovementMethod
()
setupActionMenu
(
text_content
)
}
}
override
fun
bindViews
(
data
:
MessageAttachmentViewModel
)
{
with
(
itemView
)
{
text_message_time
.
text
=
data
.
time
text_sender
.
text
=
data
.
senderName
text_content
.
text
=
data
.
content
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/PeopleSuggestionsAdapter.kt
View file @
e3989237
package
chat.rocket.android.chatroom.adapter
import
DrawableHelper
import
android.content.Context
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
...
...
@@ -13,10 +14,32 @@ import chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
import
chat.rocket.common.model.UserStatus
import
com.facebook.drawee.view.SimpleDraweeView
class
PeopleSuggestionsAdapter
:
SuggestionsAdapter
<
PeopleSuggestionViewHolder
>(
"@"
)
{
class
PeopleSuggestionsAdapter
(
context
:
Context
)
:
SuggestionsAdapter
<
PeopleSuggestionViewHolder
>(
"@"
)
{
init
{
val
allDescription
=
context
.
getString
(
R
.
string
.
suggest_all_description
)
val
hereDescription
=
context
.
getString
(
R
.
string
.
suggest_here_description
)
val
pinnedList
=
listOf
(
PeopleSuggestionViewModel
(
imageUri
=
null
,
text
=
"all"
,
username
=
"all"
,
name
=
allDescription
,
status
=
null
,
pinned
=
false
,
searchList
=
listOf
(
"all"
)),
PeopleSuggestionViewModel
(
imageUri
=
null
,
text
=
"here"
,
username
=
"here"
,
name
=
hereDescription
,
status
=
null
,
pinned
=
false
,
searchList
=
listOf
(
"here"
))
)
setPinnedSuggestions
(
pinnedList
)
}
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
PeopleSuggestionViewHolder
{
val
view
=
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
suggestion_member_item
,
parent
,
false
)
...
...
@@ -34,15 +57,19 @@ class PeopleSuggestionsAdapter : SuggestionsAdapter<PeopleSuggestionViewHolder>(
val
statusView
=
itemView
.
findViewById
<
ImageView
>(
R
.
id
.
image_status
)
username
.
text
=
item
.
username
name
.
text
=
item
.
name
if
(
item
.
imageUri
.
isEmpty
()
)
{
if
(
item
.
imageUri
?.
isEmpty
()
!=
false
)
{
avatar
.
setVisible
(
false
)
}
else
{
avatar
.
setVisible
(
true
)
avatar
.
setImageURI
(
item
.
imageUri
)
}
val
status
=
item
.
status
?:
UserStatus
.
Offline
()
val
statusDrawable
=
DrawableHelper
.
getUserStatusDrawable
(
status
,
itemView
.
context
)
statusView
.
setImageDrawable
(
statusDrawable
)
val
status
=
item
.
status
if
(
status
!=
null
)
{
val
statusDrawable
=
DrawableHelper
.
getUserStatusDrawable
(
status
,
itemView
.
context
)
statusView
.
setImageDrawable
(
statusDrawable
)
}
else
{
statusView
.
setVisible
(
false
)
}
setOnClickListener
{
itemClickListener
?.
onClick
(
item
)
}
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
e3989237
...
...
@@ -291,7 +291,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view
.
showReplyingAction
(
username
=
user
,
replyMarkdown
=
"[ ]($serverUrl/$room/$roomName?msg=$id) $mention "
,
quotedMessage
=
m
.
message
quotedMessage
=
m
apper
.
map
(
message
).
last
().
preview
?.
message
?:
""
)
}
}
...
...
@@ -499,7 +499,6 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
//TODO: cache the commands
val
commands
=
client
.
commands
(
0
,
100
).
result
view
.
populateCommandSuggestions
(
commands
.
map
{
println
(
"${it.command} - ${it.description}"
)
CommandSuggestionViewModel
(
it
.
command
,
it
.
description
?:
""
,
listOf
(
it
.
command
))
})
}
catch
(
ex
:
RocketChatException
)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ActionSnackbar.kt
View file @
e3989237
package
chat.rocket.android.chatroom.ui
import
android.graphics.drawable.Drawable
import
android.support.design.widget.BaseTransientBottomBar
import
android.support.v4.view.ViewCompat
import
android.text.Spannable
import
android.text.SpannableStringBuilder
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
...
...
@@ -27,7 +27,6 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
actionSnackbar
.
cancelView
=
view
.
findViewById
(
R
.
id
.
image_view_action_cancel_quote
)
as
ImageView
actionSnackbar
.
duration
=
BaseTransientBottomBar
.
LENGTH_INDEFINITE
val
spannable
=
Markwon
.
markdown
(
context
,
content
).
trim
()
actionSnackbar
.
marginDrawable
=
context
.
getDrawable
(
R
.
drawable
.
quote
)
actionSnackbar
.
messageTextView
.
content
=
spannable
return
actionSnackbar
}
...
...
@@ -37,19 +36,16 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
lateinit
var
cancelView
:
View
private
lateinit
var
messageTextView
:
TextView
private
lateinit
var
titleTextView
:
TextView
private
lateinit
var
marginDrawable
:
Drawable
var
text
:
String
=
""
set
(
value
)
{
val
spannable
=
parser
.
renderMarkdown
(
value
)
as
Spannable
spannable
.
setSpan
(
MessageParser
.
QuoteMarginSpan
(
marginDrawable
,
10
),
0
,
spannable
.
length
,
0
)
val
spannable
=
SpannableStringBuilder
.
valueOf
(
value
)
messageTextView
.
content
=
spannable
}
var
title
:
String
=
""
set
(
value
)
{
val
spannable
=
Markwon
.
markdown
(
this
.
context
,
value
)
as
Spannable
spannable
.
setSpan
(
MessageParser
.
QuoteMarginSpan
(
marginDrawable
,
10
),
0
,
spannable
.
length
,
0
)
titleTextView
.
content
=
spannable
}
...
...
@@ -68,17 +64,17 @@ class ActionSnackbar : BaseTransientBottomBar<ActionSnackbar> {
override
fun
animateContentOut
(
delay
:
Int
,
duration
:
Int
)
{
ViewCompat
.
setScaleY
(
content
,
1f
)
ViewCompat
.
animate
(
content
)
.
scaleY
(
0f
)
.
setDuration
(
duration
.
toLong
())
.
startDelay
=
delay
.
toLong
()
.
scaleY
(
0f
)
.
setDuration
(
duration
.
toLong
())
.
startDelay
=
delay
.
toLong
()
}
override
fun
animateContentIn
(
delay
:
Int
,
duration
:
Int
)
{
ViewCompat
.
setScaleY
(
content
,
0f
)
ViewCompat
.
animate
(
content
)
.
scaleY
(
1f
)
.
setDuration
(
duration
.
toLong
())
.
startDelay
=
delay
.
toLong
()
.
scaleY
(
1f
)
.
setDuration
(
duration
.
toLong
())
.
startDelay
=
delay
.
toLong
()
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
e3989237
...
...
@@ -36,7 +36,9 @@ import kotlinx.android.synthetic.main.message_attachment_options.*
import
kotlinx.android.synthetic.main.message_composer.*
import
kotlinx.android.synthetic.main.message_list.*
import
timber.log.Timber
import
java.util.concurrent.atomic.AtomicInteger
import
javax.inject.Inject
import
kotlin.math.absoluteValue
fun
newInstance
(
chatRoomId
:
String
,
chatRoomName
:
String
,
...
...
@@ -88,6 +90,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
val
centerX
by
lazy
{
recycler_view
.
right
}
private
val
centerY
by
lazy
{
recycler_view
.
bottom
}
private
val
handler
=
Handler
()
private
var
verticalScrollOffset
=
AtomicInteger
(
0
)
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
...
...
@@ -207,13 +210,54 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
})
}
recycler_view
.
addOnLayoutChangeListener
{
_
,
_
,
_
,
_
,
bottom
,
_
,
_
,
_
,
oldBottom
->
val
y
=
oldBottom
-
bottom
if
(
y
.
absoluteValue
>
0
)
{
// if y is positive the keyboard is up else it's down
recycler_view
.
post
{
if
(
y
>
0
||
verticalScrollOffset
.
get
().
absoluteValue
>=
y
.
absoluteValue
)
{
recycler_view
.
scrollBy
(
0
,
y
)
}
else
{
recycler_view
.
scrollBy
(
0
,
verticalScrollOffset
.
get
())
}
}
}
}
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
var
state
=
AtomicInteger
(
RecyclerView
.
SCROLL_STATE_IDLE
)
override
fun
onScrollStateChanged
(
recyclerView
:
RecyclerView
,
newState
:
Int
)
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
when
(
newState
)
{
RecyclerView
.
SCROLL_STATE_IDLE
->
{
if
(!
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_SETTLING
,
newState
))
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
RecyclerView
.
SCROLL_STATE_DRAGGING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
}
RecyclerView
.
SCROLL_STATE_SETTLING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
}
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
if
(
state
.
get
()
!=
RecyclerView
.
SCROLL_STATE_IDLE
)
{
verticalScrollOffset
.
getAndAdd
(
dy
)
}
}
})
}
val
oldMessagesCount
=
adapter
.
itemCount
adapter
.
appendData
(
dataSet
)
recycler_view
.
scrollToPosition
(
92
)
if
(
oldMessagesCount
==
0
&&
dataSet
.
isNotEmpty
())
{
recycler_view
.
scrollToPosition
(
0
)
verticalScrollOffset
.
set
(
0
)
}
presenter
.
loadActiveMembers
(
chatRoomId
,
chatRoomType
,
filterSelfOut
=
true
)
}
...
...
@@ -241,6 +285,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override
fun
showNewMessage
(
message
:
List
<
BaseViewModel
<*
>>)
{
adapter
.
prependData
(
message
)
recycler_view
.
scrollToPosition
(
0
)
verticalScrollOffset
.
set
(
0
)
}
override
fun
disableSendMessageButton
()
{
...
...
@@ -281,6 +326,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if
(!
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
verticalScrollOffset
.
set
(
0
)
}
}
}
...
...
@@ -430,6 +476,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
fun
setupFab
()
{
button_fab
.
setOnClickListener
{
recycler_view
.
scrollToPosition
(
0
)
verticalScrollOffset
.
set
(
0
)
button_fab
.
hide
()
}
}
...
...
@@ -453,11 +500,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
emojiKeyboardPopup
.
listener
=
this
text_message
.
listener
=
object
:
ComposerEditText
.
ComposerEditTextListener
{
override
fun
onKeyboardOpened
()
{
if
(
recycler_view
.
isAtBottom
())
{
if
(
adapter
.
itemCount
>
0
)
{
recycler_view
.
scrollToPosition
(
0
)
}
}
}
override
fun
onKeyboardClosed
()
{
...
...
@@ -511,7 +553,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
fun
setupSuggestionsView
()
{
suggestions_view
.
anchorTo
(
text_message
)
.
setMaximumHeight
(
resources
.
getDimensionPixelSize
(
R
.
dimen
.
suggestions_box_max_height
))
.
addTokenAdapter
(
PeopleSuggestionsAdapter
())
.
addTokenAdapter
(
PeopleSuggestionsAdapter
(
context
!!
))
.
addTokenAdapter
(
CommandSuggestionsAdapter
())
.
addTokenAdapter
(
RoomSuggestionsAdapter
())
.
addSuggestionProviderAction
(
"@"
)
{
query
->
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/AudioAttachmentViewModel.kt
View file @
e3989237
...
...
@@ -12,8 +12,9 @@ data class AudioAttachmentViewModel(
override
val
attachmentTitle
:
CharSequence
,
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
)
:
BaseFileAttachmentViewModel
<
AudioAttachment
>
{
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
)
:
BaseFileAttachmentViewModel
<
AudioAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
AUDIO_ATTACHMENT
.
viewType
override
val
layoutId
:
Int
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/BaseViewModel.kt
View file @
e3989237
...
...
@@ -11,6 +11,7 @@ interface BaseViewModel<out T> {
val
layoutId
:
Int
var
reactions
:
List
<
ReactionViewModel
>
var
nextDownStreamMessage
:
BaseViewModel
<*>?
var
preview
:
Message
?
enum
class
ViewType
(
val
viewType
:
Int
)
{
MESSAGE
(
0
),
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ImageAttachmentViewModel.kt
View file @
e3989237
...
...
@@ -12,7 +12,8 @@ data class ImageAttachmentViewModel(
override
val
attachmentTitle
:
CharSequence
,
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
)
:
BaseFileAttachmentViewModel
<
ImageAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
IMAGE_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageAttachmentViewModel.kt
0 → 100644
View file @
e3989237
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.R
import
chat.rocket.core.model.Message
data class
MessageAttachmentViewModel
(
override
val
message
:
Message
,
override
val
rawData
:
Message
,
override
val
messageId
:
String
,
var
senderName
:
String
,
val
time
:
CharSequence
,
val
content
:
CharSequence
,
val
isPinned
:
Boolean
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
var
messageLink
:
String
?
=
null
,
override
var
preview
:
Message
?
=
null
)
:
BaseViewModel
<
Message
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE_ATTACHMENT
.
viewType
override
val
layoutId
:
Int
get
()
=
R
.
layout
.
item_message_attachment
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
View file @
e3989237
...
...
@@ -14,6 +14,7 @@ data class MessageViewModel(
override
val
isPinned
:
Boolean
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
,
var
isFirstUnread
:
Boolean
)
:
BaseMessageViewModel
<
Message
>
{
override
val
viewType
:
Int
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/UrlPreviewViewModel.kt
View file @
e3989237
...
...
@@ -13,7 +13,8 @@ data class UrlPreviewViewModel(
val
description
:
CharSequence
?,
val
thumbUrl
:
String
?,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
)
:
BaseViewModel
<
Url
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
URL_PREVIEW
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/VideoAttachmentViewModel.kt
View file @
e3989237
...
...
@@ -12,7 +12,8 @@ data class VideoAttachmentViewModel(
override
val
attachmentTitle
:
CharSequence
,
override
val
id
:
Long
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
)
:
BaseFileAttachmentViewModel
<
VideoAttachment
>
{
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
VIDEO_ATTACHMENT
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
e3989237
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/chatroom/viewmodel/suggestion/PeopleSuggestionViewModel.kt
View file @
e3989237
...
...
@@ -3,7 +3,7 @@ package chat.rocket.android.chatroom.viewmodel.suggestion
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.common.model.UserStatus
class
PeopleSuggestionViewModel
(
val
imageUri
:
String
,
class
PeopleSuggestionViewModel
(
val
imageUri
:
String
?
,
text
:
String
,
val
username
:
String
,
val
name
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
e3989237
package
chat.rocket.android.chatrooms.presentation
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.main.presentation.MainNavigator
import
chat.rocket.android.server.domain.*
...
...
@@ -9,7 +10,10 @@ import chat.rocket.android.server.infraestructure.chatRooms
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.*
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.SimpleUser
import
chat.rocket.common.model.User
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.realtime.StreamMessage
...
...
@@ -30,6 +34,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
val
getChatRoomsInteractor
:
GetChatRoomsInteractor
,
private
val
saveChatRoomsInteractor
:
SaveChatRoomsInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
viewModelMapper
:
ViewModelMapper
,
settingsRepository
:
SettingsRepository
,
factory
:
ConnectionManagerFactory
)
{
private
val
manager
:
ConnectionManager
=
factory
.
create
(
serverInteractor
.
get
()
!!
)
...
...
@@ -89,9 +94,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val
chatRoomsCombined
=
mutableListOf
<
ChatRoom
>()
chatRoomsCombined
.
addAll
(
usersToChatRooms
(
users
))
chatRoomsCombined
.
addAll
(
roomsToChatRooms
(
rooms
))
view
.
updateChatRooms
(
chatRoomsCombined
)
view
.
updateChatRooms
(
getChatRoomsWithPreviews
(
chatRoomsCombined
.
toList
())
)
}
else
{
view
.
updateChatRooms
(
roomList
)
view
.
updateChatRooms
(
getChatRoomsWithPreviews
(
roomList
)
)
}
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
...
...
@@ -156,7 +161,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
val
sortedRooms
=
sortRooms
(
chatRooms
)
Timber
.
d
(
"Loaded rooms: ${sortedRooms.size}"
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
return
sortedRooms
return
getChatRoomsWithPreviews
(
sortedRooms
)
}
private
fun
sortRooms
(
chatRooms
:
List
<
ChatRoom
>):
List
<
ChatRoom
>
{
...
...
@@ -167,7 +172,17 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
private
fun
updateRooms
()
{
Timber
.
d
(
"Updating Rooms"
)
launch
(
strategy
.
jobs
)
{
view
.
updateChatRooms
(
getChatRoomsInteractor
.
get
(
currentServer
))
view
.
updateChatRooms
(
getChatRoomsWithPreviews
(
getChatRoomsInteractor
.
get
(
currentServer
)))
}
}
private
suspend
fun
getChatRoomsWithPreviews
(
chatRooms
:
List
<
ChatRoom
>):
List
<
ChatRoom
>
{
return
chatRooms
.
map
{
if
(
it
.
lastMessage
!=
null
)
{
it
.
copy
(
lastMessage
=
viewModelMapper
.
map
(
it
.
lastMessage
!!
).
last
().
preview
)
}
else
{
it
}
}
}
...
...
@@ -304,7 +319,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
// Update a ChatRoom with a Subscription information
private
fun
updateSubscription
(
subscription
:
Subscription
)
{
Timber
.
d
(
"Updating subscrition: ${subscription.id} - ${subscription.name}"
)
Timber
.
d
(
"Updating subscri
p
tion: ${subscription.id} - ${subscription.name}"
)
val
chatRooms
=
getChatRoomsInteractor
.
get
(
currentServer
).
toMutableList
()
val
chatRoom
=
chatRooms
.
find
{
chatRoom
->
chatRoom
.
id
==
subscription
.
roomId
}
chatRoom
?.
apply
{
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
View file @
e3989237
...
...
@@ -3,14 +3,18 @@ package chat.rocket.android.chatrooms.ui
import
DateTimeHelper
import
DrawableHelper
import
android.content.Context
import
android.graphics.
drawable.Drawable
import
android.graphics.
Color
import
android.support.v4.content.ContextCompat
import
android.support.v7.widget.RecyclerView
import
android.text.SpannableStringBuilder
import
android.text.style.ForegroundColorSpan
import
android.view.View
import
android.view.ViewGroup
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.checkIfMyself
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.content
...
...
@@ -26,6 +30,7 @@ import kotlinx.android.synthetic.main.unread_messages_badge.view.*
class
ChatRoomsAdapter
(
private
val
context
:
Context
,
private
val
settings
:
PublicSettings
,
private
val
localRepository
:
LocalRepository
,
private
val
listener
:
(
ChatRoom
)
->
Unit
)
:
RecyclerView
.
Adapter
<
ChatRoomsAdapter
.
ViewHolder
>()
{
var
dataSet
:
MutableList
<
ChatRoom
>
=
ArrayList
()
...
...
@@ -35,7 +40,7 @@ class ChatRoomsAdapter(private val context: Context,
override
fun
getItemCount
():
Int
=
dataSet
.
size
fun
updateRooms
(
newRooms
:
List
<
ChatRoom
>)
{
fun
updateRooms
(
newRooms
:
List
<
ChatRoom
>)
{
dataSet
.
clear
()
dataSet
.
addAll
(
newRooms
)
}
...
...
@@ -116,21 +121,30 @@ class ChatRoomsAdapter(private val context: Context,
val
lastMessageSender
=
lastMessage
?.
sender
if
(
lastMessage
!=
null
&&
lastMessageSender
!=
null
)
{
val
message
=
lastMessage
.
message
val
senderUsername
=
lastMessageSender
.
username
val
senderUsername
=
if
(
settings
.
useRealName
())
{
lastMessageSender
.
name
?:
lastMessageSender
.
username
}
else
{
lastMessageSender
.
username
}
when
(
senderUsername
)
{
chatRoom
.
name
->
{
textView
.
content
=
message
}
// TODO Change to MySelf
// chatRoom.user?.username -> {
// holder.lastMessage.textContent = context.getString(R.string.msg_you) + ": $message"
// }
else
->
{
textView
.
content
=
"@$senderUsername: $message"
val
user
=
if
(
localRepository
.
checkIfMyself
(
lastMessageSender
.
username
!!
))
{
"${context.getString(R.string.msg_you)}: "
}
else
{
"$senderUsername: "
}
val
spannable
=
SpannableStringBuilder
(
user
)
val
len
=
spannable
.
length
spannable
.
setSpan
(
ForegroundColorSpan
(
Color
.
BLACK
),
0
,
len
-
1
,
0
)
spannable
.
append
(
message
)
textView
.
content
=
spannable
}
}
}
else
{
textView
.
content
=
""
textView
.
content
=
context
.
getText
(
R
.
string
.
msg_no_messages_yet
)
}
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
e3989237
...
...
@@ -12,6 +12,7 @@ import android.view.*
import
chat.rocket.android.R
import
chat.rocket.android.chatrooms.presentation.ChatRoomsPresenter
import
chat.rocket.android.chatrooms.presentation.ChatRoomsView
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.util.extensions.*
...
...
@@ -31,6 +32,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
@Inject
lateinit
var
serverInteractor
:
GetCurrentServerInteractor
@Inject
lateinit
var
settingsRepository
:
SettingsRepository
@Inject
lateinit
var
localRepository
:
LocalRepository
private
var
searchView
:
SearchView
?
=
null
private
val
handler
=
Handler
()
...
...
@@ -108,7 +110,11 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
override
fun
showLoading
()
=
view_loading
.
setVisible
(
true
)
override
fun
hideLoading
()
=
view_loading
.
setVisible
(
false
)
override
fun
hideLoading
()
{
if
(
view_loading
!=
null
)
{
view_loading
.
setVisible
(
false
)
}
}
override
fun
showMessage
(
resId
:
Int
)
{
showToast
(
resId
)
...
...
@@ -152,12 +158,13 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
activity
?.
apply
{
recycler_view
.
layoutManager
=
LinearLayoutManager
(
this
,
LinearLayoutManager
.
VERTICAL
,
false
)
recycler_view
.
addItemDecoration
(
DividerItemDecoration
(
this
,
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_start
),
resources
.
getDimensionPixelSize
(
R
.
dimen
.
divider_item_decorator_bound_end
)))
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
// TODO - use a ViewModel Mapper instead of using settings on the adapter
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
,
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
))
{
chatRoom
->
recycler_view
.
adapter
=
ChatRoomsAdapter
(
this
,
settingsRepository
.
get
(
serverInteractor
.
get
()
!!
),
localRepository
)
{
chatRoom
->
presenter
.
loadChatRoom
(
chatRoom
)
}
}
...
...
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
View file @
e3989237
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/infrastructure/LocalRepository.kt
View file @
e3989237
...
...
@@ -22,4 +22,6 @@ interface LocalRepository {
const
val
SETTINGS_KEY
=
"settings_"
const
val
CURRENT_USERNAME_KEY
=
"username_"
}
}
\ No newline at end of file
}
fun
LocalRepository
.
checkIfMyself
(
username
:
String
)
=
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
==
username
\ No newline at end of file
app/src/main/java/chat/rocket/android/profile/ui/ProfileFragment.kt
View file @
e3989237
...
...
@@ -56,8 +56,8 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
text_email
.
textContent
=
email
text_avatar_url
.
textContent
=
""
currentName
=
user
name
currentUsername
=
name
currentName
=
name
currentUsername
=
user
name
currentEmail
=
email
currentAvatar
=
avatarUrl
...
...
@@ -76,7 +76,9 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
override
fun
hideLoading
()
{
view_loading
.
setVisible
(
false
)
if
(
view_loading
!=
null
)
{
view_loading
.
setVisible
(
false
)
}
enableUserInput
(
true
)
}
...
...
app/src/main/java/chat/rocket/android/widget/autocompletion/strategy/CompletionStrategy.kt
View file @
e3989237
...
...
@@ -6,5 +6,6 @@ interface CompletionStrategy {
fun
getItem
(
prefix
:
String
,
position
:
Int
):
SuggestionModel
fun
autocompleteItems
(
prefix
:
String
):
List
<
SuggestionModel
>
fun
addAll
(
list
:
List
<
SuggestionModel
>)
fun
addPinned
(
list
:
List
<
SuggestionModel
>)
fun
size
():
Int
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/widget/autocompletion/strategy/regex/StringMatchingCompletionStrategy.kt
View file @
e3989237
...
...
@@ -2,14 +2,19 @@ package chat.rocket.android.widget.autocompletion.strategy.regex
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.android.widget.autocompletion.strategy.CompletionStrategy
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
.Companion.RESULT_COUNT_UNLIMITED
import
java.util.concurrent.CopyOnWriteArrayList
internal
class
StringMatchingCompletionStrategy
(
private
val
threshold
:
Int
=
-
1
)
:
CompletionStrategy
{
internal
class
StringMatchingCompletionStrategy
(
private
val
threshold
:
Int
=
RESULT_COUNT_UNLIMITED
)
:
CompletionStrategy
{
private
val
list
=
CopyOnWriteArrayList
<
SuggestionModel
>()
private
val
pinnedList
=
mutableListOf
<
SuggestionModel
>()
init
{
check
(
threshold
>=
RESULT_COUNT_UNLIMITED
)
}
override
fun
autocompleteItems
(
prefix
:
String
):
List
<
SuggestionModel
>
{
val
r
esult
=
list
.
filter
{
val
partialR
esult
=
list
.
filter
{
it
.
searchList
.
forEach
{
word
->
if
(
word
.
contains
(
prefix
,
ignoreCase
=
true
))
{
return
@filter
true
...
...
@@ -17,13 +22,23 @@ internal class StringMatchingCompletionStrategy(private val threshold: Int = -1)
}
false
}.
sortedByDescending
{
it
.
pinned
}
return
if
(
threshold
==
SuggestionsAdapter
.
UNLIMITED_RESULT_COUNT
)
result
else
result
.
take
(
threshold
)
return
if
(
threshold
==
RESULT_COUNT_UNLIMITED
)
partialResult
.
toList
()
else
{
val
result
=
partialResult
.
take
(
threshold
).
toMutableList
()
result
.
addAll
(
pinnedList
)
result
.
toList
()
}
}
override
fun
addAll
(
list
:
List
<
SuggestionModel
>)
{
this
.
list
.
addAllAbsent
(
list
)
}
override
fun
addPinned
(
list
:
List
<
SuggestionModel
>)
{
this
.
pinnedList
.
addAll
(
list
)
}
override
fun
getItem
(
prefix
:
String
,
position
:
Int
):
SuggestionModel
{
return
list
[
position
]
}
...
...
app/src/main/java/chat/rocket/android/widget/autocompletion/strategy/trie/TrieCompletionStrategy.kt
View file @
e3989237
...
...
@@ -27,5 +27,9 @@ class TrieCompletionStrategy : CompletionStrategy {
}
}
override
fun
addPinned
(
list
:
List
<
SuggestionModel
>)
{
}
override
fun
size
()
=
items
.
size
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/widget/autocompletion/ui/SuggestionsAdapter.kt
View file @
e3989237
...
...
@@ -10,10 +10,10 @@ import kotlin.properties.Delegates
abstract
class
SuggestionsAdapter
<
VH
:
BaseSuggestionViewHolder
>(
val
token
:
String
,
val
constraint
:
Int
=
CONSTRAINT_UNBOUND
,
threshold
:
Int
=
MAX_RESULT_COUNT
)
:
RecyclerView
.
Adapter
<
VH
>()
{
threshold
:
Int
=
MAX_RESULT_COUNT
)
:
RecyclerView
.
Adapter
<
VH
>()
{
companion
object
{
// Any number of results.
const
val
UNLIMITED_RESULT_COUNT
=
-
1
const
val
RESULT_COUNT_UNLIMITED
=
-
1
// Trigger suggestions only if on the line start.
const
val
CONSTRAINT_BOUND_TO_START
=
0
// Trigger suggestions from anywhere.
...
...
@@ -21,12 +21,14 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
// Maximum number of results to display by default.
private
const
val
MAX_RESULT_COUNT
=
5
}
private
var
itemType
:
Type
?
=
null
private
var
itemClickListener
:
ItemClickListener
?
=
null
// Called to gather results when no results have previously matched.
private
var
providerExternal
:
((
query
:
String
)
->
Unit
)?
=
null
private
var
pinnedSuggestions
:
List
<
SuggestionModel
>?
=
null
// Maximum number of results/suggestions to display.
private
var
resultsThreshold
:
Int
=
if
(
threshold
>
0
)
threshold
else
UNLIMITED_RESULT_COUNT
private
var
resultsThreshold
:
Int
=
if
(
threshold
>
0
)
threshold
else
RESULT_COUNT_UNLIMITED
// The strategy used for suggesting completions.
private
val
strategy
:
CompletionStrategy
=
StringMatchingCompletionStrategy
(
resultsThreshold
)
// Current input term to look up for suggestions.
...
...
@@ -53,6 +55,15 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(
return
strategy
.
autocompleteItems
(
currentTerm
)[
position
]
}
/**
* Set suggestions that should always appear when prompted.
*
* @param suggestions The list of suggestions that will be pinned.
*/
fun
setPinnedSuggestions
(
suggestions
:
List
<
SuggestionModel
>)
{
this
.
strategy
.
addPinned
(
suggestions
)
}
fun
autocomplete
(
newTerm
:
String
)
{
this
.
currentTerm
=
newTerm
.
toLowerCase
().
trim
()
}
...
...
app/src/main/java/chat/rocket/android/widget/autocompletion/ui/SuggestionsView.kt
View file @
e3989237
...
...
@@ -23,23 +23,19 @@ import chat.rocket.android.R
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter.Companion.CONSTRAINT_BOUND_TO_START
import
java.lang.ref.WeakReference
import
java.util.concurrent.CopyOnWriteArrayList
import
java.util.concurrent.atomic.AtomicInteger
/**
* This is a special index that means we're not at an autocompleting state.
*/
// This is a special index that means we're not at an autocompleting state.
private
const
val
NO_STATE_INDEX
=
0
class
SuggestionsView
:
FrameLayout
,
TextWatcher
{
private
val
recyclerView
:
RecyclerView
private
val
registeredTokens
=
CopyOnWriteArrayList
<
String
>()
// Maps tokens to their respective adapters.
private
val
adaptersByToken
=
hashMapOf
<
String
,
SuggestionsAdapter
<
out
BaseSuggestionViewHolder
>>()
private
val
externalProvidersByToken
=
hashMapOf
<
String
,
((
query
:
String
)
->
Unit
)>()
private
val
localProvidersByToken
=
hashMapOf
<
String
,
HashMap
<
String
,
List
<
SuggestionModel
>>>()
private
var
editor
:
WeakReference
<
EditText
>?
=
null
private
var
completion
StartIndex
=
AtomicInteger
(
NO_STATE_INDEX
)
private
var
completion
Offset
=
AtomicInteger
(
NO_STATE_INDEX
)
private
var
maxHeight
:
Int
=
0
companion
object
{
...
...
@@ -66,7 +62,7 @@ class SuggestionsView : FrameLayout, TextWatcher {
// If we have a deletion.
if
(
after
==
0
)
{
val
deleted
=
s
.
subSequence
(
start
,
start
+
count
).
toString
()
if
(
adaptersByToken
.
containsKey
(
deleted
)
&&
completion
StartIndex
.
get
()
>
NO_STATE_INDEX
)
{
if
(
adaptersByToken
.
containsKey
(
deleted
)
&&
completion
Offset
.
get
()
>
NO_STATE_INDEX
)
{
// We have removed the '@', '#' or any other action token so halt completion.
cancelSuggestions
(
true
)
}
...
...
@@ -77,6 +73,11 @@ class SuggestionsView : FrameLayout, TextWatcher {
// If we don't have any adapter bound to any token bail out.
if
(
adaptersByToken
.
isEmpty
())
return
if
(
editor
?.
get
()
!=
null
&&
editor
?.
get
()
?.
selectionStart
?:
0
<=
completionOffset
.
get
())
{
completionOffset
.
set
(
NO_STATE_INDEX
)
collapse
()
}
val
new
=
s
.
subSequence
(
start
,
start
+
count
).
toString
()
if
(
adaptersByToken
.
containsKey
(
new
))
{
val
constraint
=
adapter
(
new
).
constraint
...
...
@@ -84,8 +85,8 @@ class SuggestionsView : FrameLayout, TextWatcher {
return
}
swapAdapter
(
getAdapterForToken
(
new
)
!!
)
completion
StartIndex
.
compareAndSet
(
NO_STATE_INDEX
,
start
+
1
)
editor
?.
let
{
completion
Offset
.
compareAndSet
(
NO_STATE_INDEX
,
start
+
1
)
this
.
editor
?.
let
{
// Disable keyboard suggestions when autocompleting.
val
editText
=
it
.
get
()
if
(
editText
!=
null
)
{
...
...
@@ -97,13 +98,13 @@ class SuggestionsView : FrameLayout, TextWatcher {
if
(
new
.
startsWith
(
" "
))
{
// just halts the completion execution
cancelSuggestions
(
fals
e
)
cancelSuggestions
(
tru
e
)
return
}
val
prefixEndIndex
=
editor
?.
get
()
?.
selectionStart
?:
NO_STATE_INDEX
if
(
prefixEndIndex
==
NO_STATE_INDEX
||
prefixEndIndex
<
completion
StartIndex
.
get
())
return
val
prefix
=
s
.
subSequence
(
completion
StartIndex
.
get
(),
editor
?.
get
()
?.
selectionStart
?:
completionStartIndex
.
get
()).
toString
()
val
prefixEndIndex
=
this
.
editor
?.
get
()
?.
selectionStart
?:
NO_STATE_INDEX
if
(
prefixEndIndex
==
NO_STATE_INDEX
||
prefixEndIndex
<
completion
Offset
.
get
())
return
val
prefix
=
s
.
subSequence
(
completion
Offset
.
get
(),
this
.
editor
?.
get
()
?.
selectionStart
?:
completionOffset
.
get
()).
toString
()
recyclerView
.
adapter
?.
let
{
it
as
SuggestionsAdapter
// we need to look up only after the '@'
...
...
@@ -157,7 +158,7 @@ class SuggestionsView : FrameLayout, TextWatcher {
val
adapter
=
adapter
(
token
)
localProvidersByToken
.
getOrPut
(
token
,
{
hashMapOf
()
})
.
put
(
adapter
.
term
(),
list
)
if
(
completion
StartIndex
.
get
()
>
NO_STATE_INDEX
&&
adapter
.
itemCount
==
0
)
expand
()
if
(
completion
Offset
.
get
()
>
NO_STATE_INDEX
&&
adapter
.
itemCount
==
0
)
expand
()
adapter
.
addItems
(
list
)
}
return
this
...
...
@@ -199,7 +200,7 @@ class SuggestionsView : FrameLayout, TextWatcher {
// Reset completion start index only if we've deleted the token that triggered completion or
// we finished the completion process.
if
(
haltCompletion
)
{
completion
StartIndex
.
set
(
NO_STATE_INDEX
)
completion
Offset
.
set
(
NO_STATE_INDEX
)
}
collapse
()
// Re-enable keyboard suggestions.
...
...
@@ -212,7 +213,7 @@ class SuggestionsView : FrameLayout, TextWatcher {
private
fun
insertSuggestionOnEditor
(
item
:
SuggestionModel
)
{
editor
?.
get
()
?.
let
{
val
suggestionText
=
item
.
text
it
.
text
.
replace
(
completion
StartIndex
.
get
(),
it
.
selectionStart
,
"$suggestionText "
)
it
.
text
.
replace
(
completion
Offset
.
get
(),
it
.
selectionStart
,
"$suggestionText "
)
}
}
...
...
app/src/main/java/chat/rocket/android/widget/emoji/EmojiParser.kt
View file @
e3989237
...
...
@@ -14,15 +14,21 @@ class EmojiParser {
*/
fun
parse
(
text
:
CharSequence
):
CharSequence
{
val
unicodedText
=
EmojiRepository
.
shortnameToUnicode
(
text
,
true
)
val
spannableString
=
SpannableString
.
valueOf
(
unicodedText
)
// Look for groups of emojis, set a CustomTypefaceSpan with the emojione font
val
length
=
spannableString
.
length
var
spannable
=
SpannableString
.
valueOf
(
unicodedText
)
val
typeface
=
EmojiRepository
.
cachedTypeface
// Look for groups of emojis, set a EmojiTypefaceSpan with the emojione font.
val
length
=
spannable
.
length
var
inEmoji
=
false
var
emojiStart
=
0
var
offset
=
0
while
(
offset
<
length
)
{
val
codepoint
=
unicodedText
.
codePointAt
(
offset
)
val
count
=
Character
.
charCount
(
codepoint
)
// Skip control characters.
if
(
codepoint
==
0
x2028
)
{
offset
+=
count
continue
}
if
(
codepoint
>=
0
x200
)
{
if
(!
inEmoji
)
{
emojiStart
=
offset
...
...
@@ -30,18 +36,25 @@ class EmojiParser {
inEmoji
=
true
}
else
{
if
(
inEmoji
)
{
spannable
String
.
setSpan
(
EmojiTypefaceSpan
(
"sans-serif"
,
EmojiRepository
.
cachedT
ypeface
),
spannable
.
setSpan
(
EmojiTypefaceSpan
(
"sans-serif"
,
t
ypeface
),
emojiStart
,
offset
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
)
}
inEmoji
=
false
}
offset
+=
count
if
(
offset
>=
length
&&
inEmoji
)
{
spannable
String
.
setSpan
(
EmojiTypefaceSpan
(
"sans-serif"
,
EmojiRepository
.
cachedT
ypeface
),
spannable
.
setSpan
(
EmojiTypefaceSpan
(
"sans-serif"
,
t
ypeface
),
emojiStart
,
offset
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
return
spannableString
return
spannable
}
private
fun
calculateSurrogatePairs
(
scalar
:
Int
):
Pair
<
Int
,
Int
>
{
val
temp
:
Int
=
(
scalar
-
0
x10000
)
/
0
x400
val
s1
:
Int
=
Math
.
floor
(
temp
.
toDouble
()).
toInt
()
+
0
xD800
val
s2
:
Int
=
((
scalar
-
0
x10000
)
%
0
x400
)
+
0
xDC00
return
Pair
(
s1
,
s2
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/widget/emoji/EmojiRepository.kt
View file @
e3989237
...
...
@@ -54,6 +54,10 @@ object EmojiRepository {
*/
fun
getAll
()
=
ALL_EMOJIS
// fun findEmojiByUnicode(unicode: Int) {
// ALL_EMOJIS.find { it.unicode == }
// }
/**
* Get all emojis for a given category.
*
...
...
@@ -119,10 +123,7 @@ object EmojiRepository {
var
result
:
String
=
input
.
toString
()
while
(
matcher
.
find
())
{
val
unicode
=
shortNameToUnicode
.
get
(
":${matcher.group(1)}:"
)
if
(
unicode
==
null
)
{
continue
}
val
unicode
=
shortNameToUnicode
.
get
(
":${matcher.group(1)}:"
)
?:
continue
if
(
supported
)
{
result
=
result
.
replace
(
":"
+
matcher
.
group
(
1
)
+
":"
,
unicode
)
...
...
@@ -159,9 +160,7 @@ object EmojiRepository {
private
fun
buildStringListFromJsonArray
(
array
:
JSONArray
):
List
<
String
>
{
val
list
=
ArrayList
<
String
>(
array
.
length
())
for
(
i
in
0
..
array
.
length
()
-
1
)
{
list
.
add
(
array
.
getString
(
i
))
}
(
0
until
array
.
length
()).
mapTo
(
list
)
{
array
.
getString
(
it
)
}
return
list
}
...
...
app/src/main/java/chat/rocket/android/widget/emoji/EmojiTypefaceSpan.kt
View file @
e3989237
...
...
@@ -17,22 +17,22 @@ class EmojiTypefaceSpan(family: String, private val newType: Typeface) : Typefac
private
fun
applyCustomTypeFace
(
paint
:
Paint
,
tf
:
Typeface
)
{
val
oldStyle
:
Int
val
old
=
paint
.
getTypeface
()
val
old
=
paint
.
typeface
if
(
old
==
null
)
{
oldStyle
=
0
}
else
{
oldStyle
=
old
.
getStyle
()
oldStyle
=
old
.
style
}
val
fake
=
oldStyle
and
tf
.
style
.
inv
()
if
(
fake
and
Typeface
.
BOLD
!=
0
)
{
paint
.
setFakeBoldText
(
true
)
paint
.
isFakeBoldText
=
true
}
if
(
fake
and
Typeface
.
ITALIC
!=
0
)
{
paint
.
setTextSkewX
(-
0.25f
)
paint
.
textSkewX
=
-
0.25f
}
paint
.
setTypeface
(
tf
)
paint
.
typeface
=
tf
}
}
\ No newline at end of file
app/src/main/res/drawable/quote.xml
→
app/src/main/res/drawable/quote
_vertical_bar
.xml
View file @
e3989237
...
...
@@ -2,9 +2,11 @@
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<solid
android:color=
"@color/
darkGra
y"
/>
<solid
android:color=
"@color/
colorPrimar
y"
/>
<size
android:width=
"4dp"
android:height=
"4dp"
/>
<corners
android:radius=
"8dp"
/>
</shape>
\ No newline at end of file
app/src/main/res/layout/item_message_attachment.xml
0 → 100644
View file @
e3989237
<?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:background=
"?android:attr/selectableItemBackground"
android:clickable=
"true"
android:focusable=
"true"
android:paddingBottom=
"@dimen/message_item_top_and_bottom_padding"
android:paddingEnd=
"@dimen/screen_edge_left_and_right_padding"
android:paddingStart=
"@dimen/screen_edge_left_and_right_padding"
android:paddingTop=
"@dimen/message_item_top_and_bottom_padding"
>
<View
android:id=
"@+id/quote_bar"
android:layout_width=
"4dp"
android:layout_height=
"0dp"
android:layout_marginStart=
"56dp"
android:background=
"@drawable/quote_vertical_bar"
app:layout_constraintBottom_toTopOf=
"@+id/recycler_view_reactions"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<LinearLayout
android:id=
"@+id/top_container"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:orientation=
"horizontal"
app:layout_constraintLeft_toRightOf=
"@+id/quote_bar"
app:layout_constraintTop_toBottomOf=
"@id/new_messages_notif"
>
<TextView
android:id=
"@+id/text_sender"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:textColor=
"@color/colorPrimary"
tools:text=
"Ronald Perkins"
/>
<TextView
android:id=
"@+id/text_message_time"
style=
"@style/Timestamp.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"10dp"
tools:text=
"11:45 PM"
/>
</LinearLayout>
<TextView
android:id=
"@+id/text_content"
style=
"@style/Message.Quote.TextView"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:ellipsize=
"end"
android:singleLine=
"true"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/top_container"
app:layout_constraintTop_toBottomOf=
"@+id/top_container"
tools:text=
"This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!"
/>
<include
layout=
"@layout/layout_reactions"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintStart_toStartOf=
"@+id/quote_bar"
app:layout_constraintTop_toBottomOf=
"@+id/text_content"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/message_action_bar.xml
View file @
e3989237
...
...
@@ -6,6 +6,17 @@
android:layout_height=
"wrap_content"
android:background=
"@color/colorPrimary"
>
<View
android:id=
"@+id/quote_bar"
android:layout_width=
"4dp"
android:layout_height=
"0dp"
android:background=
"@drawable/quote_vertical_bar"
android:layout_marginTop=
"4dp"
android:layout_marginBottom=
"4dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/image_view_action_cancel_quote"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/text_view_action_text"
android:layout_width=
"0dp"
...
...
app/src/main/res/layout/suggestion_member_item.xml
View file @
e3989237
...
...
@@ -6,9 +6,9 @@
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"2dp"
android:layout_marginEnd=
"2dp"
android:layout_marginLeft=
"
4
dp"
android:layout_marginLeft=
"
8
dp"
android:layout_marginRight=
"2dp"
android:layout_marginStart=
"
4
dp"
android:layout_marginStart=
"
8
dp"
android:layout_marginTop=
"2dp"
android:background=
"@color/suggestion_background_color"
>
...
...
@@ -22,7 +22,8 @@
android:id=
"@+id/image_avatar"
android:layout_width=
"24dp"
android:layout_height=
"24dp"
android:layout_margin=
"4dp"
android:layout_marginTop=
"4dp"
android:layout_marginBottom=
"4dp"
app:roundedCornerRadius=
"3dp"
tools:src=
"@tools:sample/avatars"
/>
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
e3989237
...
...
@@ -70,6 +70,10 @@
<string
name=
"msg_new_password"
>
Informe a nova senha
</string>
<string
name=
"msg_confirm_password"
>
Confirme a nova senha
</string>
<string
name=
"msg_unread_messages"
>
Mensagens não lidas
</string>
<string
name=
"msg_preview_video"
>
Vídeo
</string>
<string
name=
"msg_preview_audio"
>
Audio
</string>
<string
name=
"msg_preview_photo"
>
Foto
</string>
<string
name=
"msg_no_messages_yet"
>
Nenhuma mensagem ainda
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Nome da sala alterado para: %1$s por %2$s
</string>
...
...
@@ -116,6 +120,10 @@
<string
name=
"status_disconnecting"
>
desconectando
</string>
<string
name=
"status_waiting"
>
conectando em %d segundos
</string>
<!--Suggestions-->
<string
name=
"suggest_all_description"
>
Notifica todos nesta sala
</string>
<string
name=
"suggest_here_description"
>
Notifica usuários ativos nesta sala
</string>
<!-- Slash Commands -->
<string
name=
"Slash_Gimme_Description"
>
Exibir ༼ つ ◕_◕ ༽つ antes de sua mensagem
</string>
<string
name=
"Slash_LennyFace_Description"
>
Exibir ( ͡° ͜ʖ ͡°) depois de sua mensagem
</string>
...
...
app/src/main/res/values/strings.xml
View file @
e3989237
...
...
@@ -71,6 +71,10 @@
<string
name=
"msg_new_password"
>
Enter New Password
</string>
<string
name=
"msg_confirm_password"
>
Confirm New Password
</string>
<string
name=
"msg_unread_messages"
>
Unread messages
</string>
<string
name=
"msg_preview_video"
>
Video
</string>
<string
name=
"msg_preview_audio"
>
Audio
</string>
<string
name=
"msg_preview_photo"
>
Photo
</string>
<string
name=
"msg_no_messages_yet"
>
No messages yet
</string>
<!-- System messages -->
<string
name=
"message_room_name_changed"
>
Room name changed to: %1$s by %2$s
</string>
...
...
@@ -117,6 +121,10 @@
<string
name=
"status_disconnecting"
>
disconnecting
</string>
<string
name=
"status_waiting"
>
connecting in %d seconds
</string>
<!--Suggestions-->
<string
name=
"suggest_all_description"
>
Notify all in this room
</string>
<string
name=
"suggest_here_description"
>
Notify active users in this room
</string>
<!-- Slash Commands -->
<string
name=
"Slash_Gimme_Description"
>
Displays ༼ つ ◕_◕ ༽つ before your message
</string>
<string
name=
"Slash_LennyFace_Description"
>
Displays ( ͡° ͜ʖ ͡°) after your message
</string>
...
...
app/src/main/res/values/styles.xml
View file @
e3989237
...
...
@@ -88,6 +88,10 @@
<item
name=
"android:textColor"
>
@color/colorPrimaryText
</item>
</style>
<style
name=
"Message.Quote.TextView"
parent=
"Message.TextView"
>
<item
name=
"android:textColor"
>
@color/colorPrimaryText
</item>
</style>
<style
name=
"Timestamp.TextView"
parent=
"TextAppearance.AppCompat.Caption"
>
<item
name=
"android:textSize"
>
10sp
</item>
</style>
...
...
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