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
2e7a0037
Unverified
Commit
2e7a0037
authored
Mar 19, 2018
by
Leonardo Aramaki
Committed by
GitHub
Mar 19, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #918 from RocketChat/new/commands
[NEW] Commands
parents
d5fde6a3
f4f34ec5
Changes
18
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
359 additions
and
98 deletions
+359
-98
CommandSuggestionsAdapter.kt
...ket/android/chatroom/adapter/CommandSuggestionsAdapter.kt
+41
-0
PeopleSuggestionsAdapter.kt
...cket/android/chatroom/adapter/PeopleSuggestionsAdapter.kt
+2
-2
RoomSuggestionsAdapter.kt
...rocket/android/chatroom/adapter/RoomSuggestionsAdapter.kt
+2
-2
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+76
-30
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+12
-4
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+25
-12
ChatRoomViewModel.kt
...at/rocket/android/chatroom/viewmodel/ChatRoomViewModel.kt
+0
-9
PeopleViewModel.kt
...chat/rocket/android/chatroom/viewmodel/PeopleViewModel.kt
+0
-17
ChatRoomSuggestionViewModel.kt
...troom/viewmodel/suggestion/ChatRoomSuggestionViewModel.kt
+9
-0
CommandSuggestionViewModel.kt
...atroom/viewmodel/suggestion/CommandSuggestionViewModel.kt
+7
-0
PeopleSuggestionViewModel.kt
...hatroom/viewmodel/suggestion/PeopleSuggestionViewModel.kt
+17
-0
StringMatchingCompletionStrategy.kt
...letion/strategy/regex/StringMatchingCompletionStrategy.kt
+5
-4
SuggestionsAdapter.kt
...et/android/widget/autocompletion/ui/SuggestionsAdapter.kt
+44
-13
SuggestionsView.kt
...ocket/android/widget/autocompletion/ui/SuggestionsView.kt
+32
-5
suggestion_command_item.xml
app/src/main/res/layout/suggestion_command_item.xml
+39
-0
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+24
-0
dimens.xml
app/src/main/res/values/dimens.xml
+1
-0
strings.xml
app/src/main/res/values/strings.xml
+23
-0
No files found.
app/src/main/java/chat/rocket/android/chatroom/adapter/CommandSuggestionsAdapter.kt
0 → 100644
View file @
2e7a0037
package
chat.rocket.android.chatroom.adapter
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.CommandSuggestionsAdapter.CommandSuggestionsViewHolder
import
chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
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
)
{
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
CommandSuggestionsViewHolder
{
val
view
=
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
suggestion_command_item
,
parent
,
false
)
return
CommandSuggestionsViewHolder
(
view
)
}
class
CommandSuggestionsViewHolder
(
view
:
View
)
:
BaseSuggestionViewHolder
(
view
)
{
override
fun
bind
(
item
:
SuggestionModel
,
itemClickListener
:
SuggestionsAdapter
.
ItemClickListener
?)
{
item
as
CommandSuggestionViewModel
with
(
itemView
)
{
val
nameTextView
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_command_name
)
val
descriptionTextView
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_command_description
)
nameTextView
.
text
=
"/${item.text}"
val
res
=
context
.
resources
val
id
=
res
.
getIdentifier
(
item
.
description
,
"string"
,
context
.
packageName
)
val
description
=
if
(
id
>
0
)
res
.
getString
(
id
)
else
""
descriptionTextView
.
text
=
description
.
toLowerCase
()
setOnClickListener
{
itemClickListener
?.
onClick
(
item
)
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/adapter/PeopleSuggestionsAdapter.kt
View file @
2e7a0037
...
...
@@ -8,7 +8,7 @@ import android.widget.ImageView
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter.PeopleSuggestionViewHolder
import
chat.rocket.android.chatroom.viewmodel.
People
ViewModel
import
chat.rocket.android.chatroom.viewmodel.
suggestion.PeopleSuggestion
ViewModel
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
...
...
@@ -26,7 +26,7 @@ class PeopleSuggestionsAdapter : SuggestionsAdapter<PeopleSuggestionViewHolder>(
class
PeopleSuggestionViewHolder
(
view
:
View
)
:
BaseSuggestionViewHolder
(
view
)
{
override
fun
bind
(
item
:
SuggestionModel
,
itemClickListener
:
SuggestionsAdapter
.
ItemClickListener
?)
{
item
as
PeopleViewModel
item
as
People
Suggestion
ViewModel
with
(
itemView
)
{
val
username
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_username
)
val
name
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_name
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/RoomSuggestionsAdapter.kt
View file @
2e7a0037
...
...
@@ -6,7 +6,7 @@ import android.view.ViewGroup
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter.RoomSuggestionsViewHolder
import
chat.rocket.android.chatroom.viewmodel.
ChatRoom
ViewModel
import
chat.rocket.android.chatroom.viewmodel.
suggestion.ChatRoomSuggestion
ViewModel
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.android.widget.autocompletion.ui.BaseSuggestionViewHolder
import
chat.rocket.android.widget.autocompletion.ui.SuggestionsAdapter
...
...
@@ -22,7 +22,7 @@ class RoomSuggestionsAdapter : SuggestionsAdapter<RoomSuggestionsViewHolder>("#"
class
RoomSuggestionsViewHolder
(
view
:
View
)
:
BaseSuggestionViewHolder
(
view
)
{
override
fun
bind
(
item
:
SuggestionModel
,
itemClickListener
:
SuggestionsAdapter
.
ItemClickListener
?)
{
item
as
ChatRoomViewModel
item
as
ChatRoom
Suggestion
ViewModel
with
(
itemView
)
{
val
fullname
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_fullname
)
val
name
=
itemView
.
findViewById
<
TextView
>(
R
.
id
.
text_name
)
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
2e7a0037
...
...
@@ -6,9 +6,10 @@ import chat.rocket.android.chatroom.adapter.AutoCompleteType
import
chat.rocket.android.chatroom.adapter.PEOPLE
import
chat.rocket.android.chatroom.adapter.ROOMS
import
chat.rocket.android.chatroom.domain.UriInteractor
import
chat.rocket.android.chatroom.viewmodel.ChatRoomViewModel
import
chat.rocket.android.chatroom.viewmodel.PeopleViewModel
import
chat.rocket.android.chatroom.viewmodel.ViewModelMapper
import
chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
...
...
@@ -23,6 +24,7 @@ import chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.realtime.State
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.model.Command
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Value
import
kotlinx.coroutines.experimental.CommonPool
...
...
@@ -363,7 +365,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
// Take at most the 100 most recent messages distinguished by user. Can return less.
val
recentMessages
=
messagesRepository
.
getRecentMessages
(
chatRoomId
,
100
)
.
filterNot
{
filterSelfOut
&&
it
.
sender
?.
username
==
self
}
val
activeUsers
=
mutableListOf
<
PeopleViewModel
>()
val
activeUsers
=
mutableListOf
<
People
Suggestion
ViewModel
>()
recentMessages
.
forEach
{
val
sender
=
it
.
sender
!!
val
username
=
sender
.
username
?:
""
...
...
@@ -372,7 +374,7 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val
found
=
members
.
firstOrNull
{
member
->
member
.
username
==
username
}
val
status
=
if
(
found
!=
null
)
found
.
status
else
UserStatus
.
Offline
()
val
searchList
=
mutableListOf
(
username
,
name
)
activeUsers
.
add
(
PeopleViewModel
(
avatarUrl
,
username
,
username
,
name
,
status
,
activeUsers
.
add
(
People
Suggestion
ViewModel
(
avatarUrl
,
username
,
username
,
name
,
status
,
true
,
searchList
))
}
// Filter out from members list the active users.
...
...
@@ -387,10 +389,10 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
val
name
=
it
.
name
?:
""
val
avatarUrl
=
UrlHelper
.
getAvatarUrl
(
currentServer
,
username
)
val
searchList
=
mutableListOf
(
username
,
name
)
PeopleViewModel
(
avatarUrl
,
username
,
username
,
name
,
it
.
status
,
true
,
searchList
)
People
Suggestion
ViewModel
(
avatarUrl
,
username
,
username
,
name
,
it
.
status
,
true
,
searchList
)
})
view
.
populate
Member
s
(
activeUsers
)
view
.
populate
PeopleSuggestion
s
(
activeUsers
)
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
}
...
...
@@ -407,12 +409,12 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
usersRepository
.
saveAll
(
users
)
}
val
self
=
localRepository
.
get
(
LocalRepository
.
USERNAME_KEY
)
view
.
populate
Member
s
(
users
.
map
{
view
.
populate
PeopleSuggestion
s
(
users
.
map
{
val
username
=
it
.
username
?:
""
val
name
=
it
.
name
?:
""
val
searchList
=
mutableListOf
(
username
,
name
)
it
.
emails
?.
forEach
{
email
->
searchList
.
add
(
email
.
address
)
}
PeopleViewModel
(
UrlHelper
.
getAvatarUrl
(
currentServer
,
username
),
People
Suggestion
ViewModel
(
UrlHelper
.
getAvatarUrl
(
currentServer
,
username
),
username
,
username
,
name
,
it
.
status
,
false
,
searchList
)
}.
filterNot
{
filterSelfOut
&&
self
!=
null
&&
self
==
it
.
text
})
}
...
...
@@ -420,11 +422,11 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
if
(
rooms
.
isNotEmpty
())
{
roomsRepository
.
saveAll
(
rooms
)
}
view
.
populateRooms
(
rooms
.
map
{
view
.
populateRoom
Suggestion
s
(
rooms
.
map
{
val
fullName
=
it
.
fullName
?:
""
val
name
=
it
.
name
?:
""
val
searchList
=
mutableListOf
(
fullName
,
name
)
ChatRoomViewModel
(
name
,
fullName
,
name
,
searchList
)
ChatRoom
Suggestion
ViewModel
(
name
,
fullName
,
name
,
searchList
)
})
}
}
...
...
@@ -446,14 +448,14 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
.
map
{
chatRoom
->
val
name
=
chatRoom
.
name
val
fullName
=
chatRoom
.
fullName
?:
""
ChatRoomViewModel
(
ChatRoom
Suggestion
ViewModel
(
text
=
name
,
name
=
name
,
fullName
=
fullName
,
searchList
=
listOf
(
name
,
fullName
)
)
}
view
.
populateRooms
(
chatRooms
)
view
.
populateRoom
Suggestion
s
(
chatRooms
)
}
catch
(
e
:
RocketChatException
)
{
Timber
.
e
(
e
)
}
...
...
@@ -488,6 +490,50 @@ class ChatRoomPresenter @Inject constructor(private val view: ChatRoomView,
view
.
showReactionsPopup
(
messageId
)
}
fun
loadCommands
()
{
launchUI
(
strategy
)
{
try
{
//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
)
{
Timber
.
e
(
ex
)
}
}
}
fun
runCommand
(
text
:
String
,
roomId
:
String
)
{
launchUI
(
strategy
)
{
try
{
if
(
text
.
length
==
1
)
{
// we have just the slash, post it anyway
sendMessage
(
roomId
,
text
,
null
)
}
else
{
val
command
=
text
.
split
(
" "
)
val
name
=
command
[
0
].
substring
(
1
)
var
params
:
String
=
""
command
.
forEachIndexed
{
index
,
param
->
if
(
index
>
0
)
{
params
+=
"$param "
}
}
val
result
=
client
.
runCommand
(
Command
(
name
,
params
),
roomId
)
if
(!
result
)
{
// failed, command is not valid so post it
sendMessage
(
roomId
,
text
,
null
)
}
}
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
// command is not valid, post it
sendMessage
(
roomId
,
text
,
null
)
}
}
}
private
fun
updateMessage
(
streamedMessage
:
Message
)
{
launchUI
(
strategy
)
{
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
)
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
2e7a0037
...
...
@@ -2,8 +2,9 @@ package chat.rocket.android.chatroom.presentation
import
android.net.Uri
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.chatroom.viewmodel.ChatRoomViewModel
import
chat.rocket.android.chatroom.viewmodel.PeopleViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.State
...
...
@@ -102,12 +103,19 @@ interface ChatRoomView : LoadingView, MessageView {
fun
showInvalidFileSize
(
fileSize
:
Int
,
maxFileSize
:
Int
)
fun
showConnectionState
(
state
:
State
)
fun
populate
Members
(
members
:
List
<
People
ViewModel
>)
fun
populateRoom
s
(
chatRooms
:
List
<
ChatRoom
ViewModel
>)
fun
populate
PeopleSuggestions
(
members
:
List
<
PeopleSuggestion
ViewModel
>)
fun
populateRoom
Suggestions
(
chatRooms
:
List
<
ChatRoomSuggestion
ViewModel
>)
/**
* This user has joined the chat callback.
*/
fun
onJoined
()
fun
showReactionsPopup
(
messageId
:
String
)
/**
* Show list of commands.
*
* @param commands The list of available commands.
*/
fun
populateCommandSuggestions
(
commands
:
List
<
CommandSuggestionViewModel
>)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
2e7a0037
...
...
@@ -15,16 +15,14 @@ import android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.RecyclerView
import
android.view.*
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.ChatRoomAdapter
import
chat.rocket.android.chatroom.adapter.PEOPLE
import
chat.rocket.android.chatroom.adapter.PeopleSuggestionsAdapter
import
chat.rocket.android.chatroom.adapter.RoomSuggestionsAdapter
import
chat.rocket.android.chatroom.adapter.*
import
chat.rocket.android.chatroom.presentation.ChatRoomPresenter
import
chat.rocket.android.chatroom.presentation.ChatRoomView
import
chat.rocket.android.chatroom.viewmodel.BaseViewModel
import
chat.rocket.android.chatroom.viewmodel.ChatRoomViewModel
import
chat.rocket.android.chatroom.viewmodel.MessageViewModel
import
chat.rocket.android.chatroom.viewmodel.PeopleViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.ChatRoomSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.CommandSuggestionViewModel
import
chat.rocket.android.chatroom.viewmodel.suggestion.PeopleSuggestionViewModel
import
chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.MessageParser
...
...
@@ -218,7 +216,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override
fun
sendMessage
(
text
:
String
)
{
if
(!
text
.
isBlank
())
{
if
(!
text
.
startsWith
(
"/"
))
{
presenter
.
sendMessage
(
chatRoomId
,
text
,
editingMessageId
)
}
else
{
presenter
.
runCommand
(
text
,
chatRoomId
)
}
}
}
...
...
@@ -287,14 +289,18 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
populate
Members
(
members
:
List
<
People
ViewModel
>)
{
override
fun
populate
PeopleSuggestions
(
members
:
List
<
PeopleSuggestion
ViewModel
>)
{
suggestions_view
.
addItems
(
"@"
,
members
)
}
override
fun
populateRoom
s
(
chatRooms
:
List
<
ChatRoom
ViewModel
>)
{
override
fun
populateRoom
Suggestions
(
chatRooms
:
List
<
ChatRoomSuggestion
ViewModel
>)
{
suggestions_view
.
addItems
(
"#"
,
chatRooms
)
}
override
fun
populateCommandSuggestions
(
commands
:
List
<
CommandSuggestionViewModel
>)
{
suggestions_view
.
addItems
(
"/"
,
commands
)
}
override
fun
copyToClipboard
(
message
:
String
)
{
activity
?.
apply
{
val
clipboard
=
getSystemService
(
Context
.
CLIPBOARD_SERVICE
)
as
ClipboardManager
...
...
@@ -492,9 +498,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
private
fun
setupSuggestionsView
()
{
suggestions_view
.
anchor
(
text_message
)
.
bindTokenAdapter
(
PeopleSuggestionsAdapter
())
.
bindTokenAdapter
(
RoomSuggestionsAdapter
())
suggestions_view
.
anchorTo
(
text_message
)
.
setMaximumHeight
(
resources
.
getDimensionPixelSize
(
R
.
dimen
.
suggestions_box_max_height
))
.
addTokenAdapter
(
PeopleSuggestionsAdapter
())
.
addTokenAdapter
(
CommandSuggestionsAdapter
())
.
addTokenAdapter
(
RoomSuggestionsAdapter
())
.
addSuggestionProviderAction
(
"@"
)
{
query
->
if
(
query
.
isNotEmpty
())
{
presenter
.
spotlight
(
query
,
PEOPLE
,
true
)
...
...
@@ -505,6 +513,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter
.
loadChatRooms
()
}
}
.
addSuggestionProviderAction
(
"/"
)
{
_
->
presenter
.
loadCommands
()
}
presenter
.
loadCommands
()
}
private
fun
openEmojiKeyboardPopup
()
{
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ChatRoomViewModel.kt
deleted
100644 → 0
View file @
d5fde6a3
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
class
ChatRoomViewModel
(
text
:
String
,
val
fullName
:
String
,
val
name
:
String
,
searchList
:
List
<
String
>)
:
SuggestionModel
(
text
,
searchList
,
false
)
{
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/PeopleViewModel.kt
deleted
100644 → 0
View file @
d5fde6a3
package
chat.rocket.android.chatroom.viewmodel
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
import
chat.rocket.common.model.UserStatus
class
PeopleViewModel
(
val
imageUri
:
String
,
text
:
String
,
val
username
:
String
,
val
name
:
String
,
val
status
:
UserStatus
?,
pinned
:
Boolean
=
false
,
searchList
:
List
<
String
>)
:
SuggestionModel
(
text
,
searchList
,
pinned
)
{
override
fun
toString
():
String
{
return
"PeopleViewModel(imageUri='$imageUri', username='$username', name='$name', status=$status, pinned=$pinned)"
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/suggestion/ChatRoomSuggestionViewModel.kt
0 → 100644
View file @
2e7a0037
package
chat.rocket.android.chatroom.viewmodel.suggestion
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
class
ChatRoomSuggestionViewModel
(
text
:
String
,
val
fullName
:
String
,
val
name
:
String
,
searchList
:
List
<
String
>)
:
SuggestionModel
(
text
,
searchList
,
false
)
{
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/suggestion/CommandSuggestionViewModel.kt
0 → 100644
View file @
2e7a0037
package
chat.rocket.android.chatroom.viewmodel.suggestion
import
chat.rocket.android.widget.autocompletion.model.SuggestionModel
class
CommandSuggestionViewModel
(
text
:
String
,
val
description
:
String
,
searchList
:
List
<
String
>)
:
SuggestionModel
(
text
,
searchList
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/suggestion/PeopleSuggestionViewModel.kt
0 → 100644
View file @
2e7a0037
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
,
text
:
String
,
val
username
:
String
,
val
name
:
String
,
val
status
:
UserStatus
?,
pinned
:
Boolean
=
false
,
searchList
:
List
<
String
>)
:
SuggestionModel
(
text
,
searchList
,
pinned
)
{
override
fun
toString
():
String
{
return
"PeopleSuggestionViewModel(imageUri='$imageUri', username='$username', name='$name', status=$status, pinned=$pinned)"
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/widget/autocompletion/strategy/regex/StringMatchingCompletionStrategy.kt
View file @
2e7a0037
...
...
@@ -2,24 +2,25 @@ 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
java.util.concurrent.CopyOnWriteArrayList
internal
class
StringMatchingCompletionStrategy
:
CompletionStrategy
{
internal
class
StringMatchingCompletionStrategy
(
private
val
threshold
:
Int
=
-
1
)
:
CompletionStrategy
{
private
val
list
=
CopyOnWriteArrayList
<
SuggestionModel
>()
override
fun
autocompleteItems
(
prefix
:
String
):
List
<
SuggestionModel
>
{
return
list
.
filter
{
val
result
=
list
.
filter
{
it
.
searchList
.
forEach
{
word
->
if
(
word
.
contains
(
prefix
,
ignoreCase
=
true
))
{
return
@filter
true
}
}
false
}.
sortedByDescending
{
it
.
pinned
}.
take
(
5
)
}.
sortedByDescending
{
it
.
pinned
}
return
if
(
threshold
==
SuggestionsAdapter
.
UNLIMITED_RESULT_COUNT
)
result
else
result
.
take
(
threshold
)
}
override
fun
addAll
(
list
:
List
<
SuggestionModel
>)
{
// this.list.removeAll { !it.pinned }
this
.
list
.
addAllAbsent
(
list
)
}
...
...
app/src/main/java/chat/rocket/android/widget/autocompletion/ui/SuggestionsAdapter.kt
View file @
2e7a0037
...
...
@@ -7,14 +7,32 @@ import chat.rocket.android.widget.autocompletion.strategy.regex.StringMatchingCo
import
java.lang.reflect.Type
import
kotlin.properties.Delegates
abstract
class
SuggestionsAdapter
<
VH
:
BaseSuggestionViewHolder
>(
val
token
:
String
)
:
RecyclerView
.
Adapter
<
VH
>()
{
private
val
strategy
:
CompletionStrategy
=
StringMatchingCompletionStrategy
()
abstract
class
SuggestionsAdapter
<
VH
:
BaseSuggestionViewHolder
>(
val
token
:
String
,
val
constraint
:
Int
=
CONSTRAINT_UNBOUND
,
threshold
:
Int
=
MAX_RESULT_COUNT
)
:
RecyclerView
.
Adapter
<
VH
>()
{
companion
object
{
// Any number of results.
const
val
UNLIMITED_RESULT_COUNT
=
-
1
// Trigger suggestions only if on the line start.
const
val
CONSTRAINT_BOUND_TO_START
=
0
// Trigger suggestions from anywhere.
const
val
CONSTRAINT_UNBOUND
=
1
// 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
prefix
:
String
by
Delegates
.
observable
(
""
,
{
_
,
_
,
_
->
strategy
.
autocompleteItems
(
prefix
)
notifyItemRangeChanged
(
0
,
5
)
// Maximum number of results/suggestions to display.
private
var
resultsThreshold
:
Int
=
if
(
threshold
>
0
)
threshold
else
UNLIMITED_RESULT_COUNT
// The strategy used for suggesting completions.
private
val
strategy
:
CompletionStrategy
=
StringMatchingCompletionStrategy
(
resultsThreshold
)
// Current input term to look up for suggestions.
private
var
currentTerm
:
String
by
Delegates
.
observable
(
""
,
{
_
,
_
,
newTerm
->
val
items
=
strategy
.
autocompleteItems
(
newTerm
)
notifyDataSetChanged
()
})
init
{
...
...
@@ -29,21 +47,21 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(val token: Stri
holder
.
bind
(
getItem
(
position
),
itemClickListener
)
}
override
fun
getItemCount
()
=
strategy
.
autocompleteItems
(
prefix
).
size
override
fun
getItemCount
()
=
strategy
.
autocompleteItems
(
currentTerm
).
size
private
fun
getItem
(
position
:
Int
):
SuggestionModel
{
return
strategy
.
autocompleteItems
(
prefix
)[
position
]
return
strategy
.
autocompleteItems
(
currentTerm
)[
position
]
}
fun
autocomplete
(
prefix
:
String
)
{
this
.
prefix
=
prefix
.
toLowerCase
().
trim
()
fun
autocomplete
(
newTerm
:
String
)
{
this
.
currentTerm
=
newTerm
.
toLowerCase
().
trim
()
}
fun
addItems
(
list
:
List
<
SuggestionModel
>)
{
strategy
.
addAll
(
list
)
// Since we've just added new items we should check for possible new completion suggestions.
strategy
.
autocompleteItems
(
prefix
)
notify
ItemRangeChanged
(
0
,
5
)
strategy
.
autocompleteItems
(
currentTerm
)
notify
DataSetChanged
(
)
}
fun
setOnClickListener
(
clickListener
:
ItemClickListener
)
{
...
...
@@ -52,11 +70,24 @@ abstract class SuggestionsAdapter<VH : BaseSuggestionViewHolder>(val token: Stri
fun
hasItemClickListener
()
=
itemClickListener
!=
null
fun
prefix
()
=
prefix
/**
* Return the current searched term.
*/
fun
term
()
=
this
.
currentTerm
/**
* Set the maximum number of results to show.
*
* @param threshold The maximum number of suggestions to display.
*/
fun
setResultsThreshold
(
threshold
:
Int
)
{
check
(
threshold
>
0
)
resultsThreshold
=
threshold
}
fun
cancel
()
{
strategy
.
addAll
(
emptyList
())
strategy
.
autocompleteItems
(
prefix
)
strategy
.
autocompleteItems
(
currentTerm
)
notifyDataSetChanged
()
}
...
...
app/src/main/java/chat/rocket/android/widget/autocompletion/ui/SuggestionsView.kt
View file @
2e7a0037
...
...
@@ -21,7 +21,9 @@ import android.widget.EditText
import
android.widget.FrameLayout
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
/**
...
...
@@ -31,12 +33,14 @@ 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
completionStartIndex
=
AtomicInteger
(
NO_STATE_INDEX
)
private
var
maxHeight
:
Int
=
0
companion
object
{
private
val
SLIDE_TRANSITION
=
Slide
(
Gravity
.
BOTTOM
).
setDuration
(
200
)
...
...
@@ -75,6 +79,10 @@ class SuggestionsView : FrameLayout, TextWatcher {
val
new
=
s
.
subSequence
(
start
,
start
+
count
).
toString
()
if
(
adaptersByToken
.
containsKey
(
new
))
{
val
constraint
=
adapter
(
new
).
constraint
if
(
constraint
==
CONSTRAINT_BOUND_TO_START
&&
start
!=
0
)
{
return
}
swapAdapter
(
getAdapterForToken
(
new
)
!!
)
completionStartIndex
.
compareAndSet
(
NO_STATE_INDEX
,
start
+
1
)
editor
?.
let
{
...
...
@@ -110,9 +118,18 @@ class SuggestionsView : FrameLayout, TextWatcher {
}
}
override
fun
onMeasure
(
widthMeasureSpec
:
Int
,
heightMeasureSpec
:
Int
)
{
if
(
maxHeight
>
0
)
{
val
hSpec
=
MeasureSpec
.
makeMeasureSpec
(
maxHeight
,
MeasureSpec
.
AT_MOST
)
super
.
onMeasure
(
widthMeasureSpec
,
hSpec
)
}
else
{
super
.
onMeasure
(
widthMeasureSpec
,
heightMeasureSpec
)
}
}
private
fun
swapAdapter
(
adapter
:
SuggestionsAdapter
<
*
>):
SuggestionsView
{
recyclerView
.
adapter
=
adapter
// Don't override if user
set an item click listener already/
// Don't override if user
has set an item click listener already
if
(!
adapter
.
hasItemClickListener
())
{
setOnItemClickListener
(
adapter
)
{
// set default item click behavior
...
...
@@ -121,16 +138,16 @@ class SuggestionsView : FrameLayout, TextWatcher {
return
this
}
fun
getAdapterForToken
(
token
:
String
):
SuggestionsAdapter
<
*
>?
=
adaptersByToken
.
get
(
token
)
private
fun
getAdapterForToken
(
token
:
String
):
SuggestionsAdapter
<
*
>?
=
adaptersByToken
.
get
(
token
)
fun
anchor
(
editText
:
EditText
):
SuggestionsView
{
fun
anchor
To
(
editText
:
EditText
):
SuggestionsView
{
editText
.
removeTextChangedListener
(
this
)
editText
.
addTextChangedListener
(
this
)
editor
=
WeakReference
(
editText
)
return
this
}
fun
bin
dTokenAdapter
(
adapter
:
SuggestionsAdapter
<
*
>):
SuggestionsView
{
fun
ad
dTokenAdapter
(
adapter
:
SuggestionsAdapter
<
*
>):
SuggestionsView
{
adaptersByToken
.
getOrPut
(
adapter
.
token
,
{
adapter
})
return
this
}
...
...
@@ -139,13 +156,20 @@ class SuggestionsView : FrameLayout, TextWatcher {
if
(
list
.
isNotEmpty
())
{
val
adapter
=
adapter
(
token
)
localProvidersByToken
.
getOrPut
(
token
,
{
hashMapOf
()
})
.
put
(
adapter
.
prefix
(),
list
)
.
put
(
adapter
.
term
(),
list
)
if
(
completionStartIndex
.
get
()
>
NO_STATE_INDEX
&&
adapter
.
itemCount
==
0
)
expand
()
adapter
.
addItems
(
list
)
}
return
this
}
fun
setMaximumHeight
(
height
:
Int
):
SuggestionsView
{
check
(
height
>
0
)
this
.
maxHeight
=
height
requestLayout
()
return
this
}
fun
setOnItemClickListener
(
tokenAdapter
:
SuggestionsAdapter
<
*
>,
clickListener
:
(
item
:
SuggestionModel
)
->
Unit
):
SuggestionsView
{
tokenAdapter
.
setOnClickListener
(
object
:
SuggestionsAdapter
.
ItemClickListener
{
...
...
@@ -160,6 +184,9 @@ class SuggestionsView : FrameLayout, TextWatcher {
}
fun
addSuggestionProviderAction
(
token
:
String
,
provider
:
(
query
:
String
)
->
Unit
):
SuggestionsView
{
if
(
adaptersByToken
[
token
]
==
null
)
{
throw
IllegalStateException
(
"token \"$token\" suggestion provider added without adapter"
)
}
externalProvidersByToken
.
getOrPut
(
token
,
{
provider
})
return
this
}
...
...
app/src/main/res/layout/suggestion_command_item.xml
0 → 100644
View file @
2e7a0037
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"4dp"
android:layout_marginTop=
"4dp"
android:background=
"@color/suggestion_background_color"
android:orientation=
"horizontal"
android:paddingTop=
"2dp"
>
<TextView
android:id=
"@+id/text_command_name"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentLeft=
"true"
android:layout_alignParentStart=
"true"
android:layout_marginStart=
"8dp"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textColor=
"@color/black"
android:textSize=
"14sp"
tools:text=
"/leave"
/>
<TextView
android:id=
"@+id/text_command_description"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"8dp"
android:layout_marginStart=
"16dp"
android:layout_toRightOf=
"@id/text_command_name"
android:ellipsize=
"end"
android:gravity=
"start"
android:maxLines=
"1"
android:textColor=
"@color/gray_material"
android:textSize=
"14sp"
tools:text=
"Leave a channel"
/>
</RelativeLayout>
\ No newline at end of file
app/src/main/res/values-pt-rBR/strings.xml
View file @
2e7a0037
...
...
@@ -114,4 +114,28 @@
<string
name=
"status_authenticating"
>
autenticando
</string>
<string
name=
"status_disconnecting"
>
desconectando
</string>
<string
name=
"status_waiting"
>
conectando em %d segundos
</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>
<string
name=
"Slash_Shrug_Description"
>
Exibir ¯\_(ツ)_/¯ depois de sua mensagem
</string>
<string
name=
"Slash_Tableflip_Description"
>
Exibir (╯°□°)╯︵ ┻━┻
</string>
<string
name=
"Slash_TableUnflip_Description"
>
Exibir ┬─┬ ノ( ゜-゜ノ)
</string>
<string
name=
"Create_A_New_Channel"
>
Criar um novo canal
</string>
<string
name=
"Show_the_keyboard_shortcut_list"
>
Show the keyboard shortcut list
</string>
<string
name=
"Invite_user_to_join_channel_all_from"
>
do [#canal] para entrar neste
</string>
<string
name=
"Invite_user_to_join_channel_all_to"
>
Convidar todos os usuários deste canal para entrar no [#canal]
</string>
<string
name=
"Archive"
>
Arquivar
</string>
<string
name=
"Remove_someone_from_room"
>
Remover alguém do canal
</string>
<string
name=
"Leave_the_current_channel"
>
Sair do canal atual
</string>
<string
name=
"Displays_action_text"
>
Exibir texto de ação
</string>
<string
name=
"Direct_message_someone"
>
Enviar DM para alguém
</string>
<string
name=
"Mute_someone_in_room"
>
Mutar alguém
</string>
<string
name=
"Unmute_someone_in_room"
>
Desmutar alguém na sala
</string>
<string
name=
"Invite_user_to_join_channel"
>
Convidar algum usuário para entrar neste canal
</string>
<string
name=
"Unarchive"
>
Desarquivar
</string>
<string
name=
"Join_the_given_channel"
>
Entrar no canal especificado
</string>
<string
name=
"Guggy_Command_Description"
>
Gera um gif baseado no texto dado
</string>
<string
name=
"Slash_Topic_Description"
>
Definir tópico
</string>
</resources>
\ No newline at end of file
app/src/main/res/values/dimens.xml
View file @
2e7a0037
...
...
@@ -34,5 +34,6 @@
<!-- Autocomplete Popup -->
<dimen
name=
"popup_max_height"
>
150dp
</dimen>
<dimen
name=
"suggestions_box_max_height"
>
250dp
</dimen>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
2e7a0037
...
...
@@ -116,4 +116,27 @@
<string
name=
"status_disconnecting"
>
disconnecting
</string>
<string
name=
"status_waiting"
>
connecting in %d seconds
</string>
<!-- Slash Commands -->
<string
name=
"Slash_Gimme_Description"
>
Displays ༼ つ ◕_◕ ༽つ before your message
</string>
<string
name=
"Slash_LennyFace_Description"
>
Displays ( ͡° ͜ʖ ͡°) after your message
</string>
<string
name=
"Slash_Shrug_Description"
>
Displays ¯\_(ツ)_/¯ after your message
</string>
<string
name=
"Slash_Tableflip_Description"
>
Displays (╯°□°)╯︵ ┻━┻
</string>
<string
name=
"Slash_TableUnflip_Description"
>
Displays ┬─┬ ノ( ゜-゜ノ)
</string>
<string
name=
"Create_A_New_Channel"
>
Create a new channel
</string>
<string
name=
"Show_the_keyboard_shortcut_list"
>
Show the keyboard shortcut list
</string>
<string
name=
"Invite_user_to_join_channel_all_from"
>
Invite all users from [#channel] to join this channel
</string>
<string
name=
"Invite_user_to_join_channel_all_to"
>
Invite all users from this channel to join [#channel]
</string>
<string
name=
"Archive"
>
Archive
</string>
<string
name=
"Remove_someone_from_room"
>
Remove someone from the room
</string>
<string
name=
"Leave_the_current_channel"
>
Leave the current channel
</string>
<string
name=
"Displays_action_text"
>
Displays action text
</string>
<string
name=
"Direct_message_someone"
>
Direct message someone
</string>
<string
name=
"Mute_someone_in_room"
>
Mute someone in the room
</string>
<string
name=
"Unmute_someone_in_room"
>
Unmute someone in the room
</string>
<string
name=
"Invite_user_to_join_channel"
>
Invite one user to join this channel
</string>
<string
name=
"Unarchive"
>
Unarchive
</string>
<string
name=
"Join_the_given_channel"
>
Join the given channel
</string>
<string
name=
"Guggy_Command_Description"
>
Generates a gif based upon the provided text
</string>
<string
name=
"Slash_Topic_Description"
>
Set topic
</string>
</resources>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment