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
212b870f
Unverified
Commit
212b870f
authored
Jul 17, 2018
by
divyanshu bhargava
Committed by
GitHub
Jul 17, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #40 from RocketChat/develop
merge
parents
98b0b8f6
a56f09ba
Changes
58
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
58 changed files
with
1193 additions
and
451 deletions
+1193
-451
build.gradle
app/build.gradle
+0
-1
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+5
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+44
-11
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+9
-2
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+60
-91
Dialog.kt
app/src/main/java/chat/rocket/android/chatroom/ui/Dialog.kt
+0
-0
Menu.kt
app/src/main/java/chat/rocket/android/chatroom/ui/Menu.kt
+129
-0
ChatRoomsFragmentModule.kt
...at/rocket/android/chatrooms/di/ChatRoomsFragmentModule.kt
+26
-14
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+12
-19
CreateChannelFragment.kt
.../rocket/android/createchannel/ui/CreateChannelFragment.kt
+1
-2
AppModule.kt
.../main/java/chat/rocket/android/dagger/module/AppModule.kt
+1
-0
MessageParser.kt
...src/main/java/chat/rocket/android/helper/MessageParser.kt
+37
-1
OauthHelper.kt
app/src/main/java/chat/rocket/android/helper/OauthHelper.kt
+10
-4
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+38
-22
MainView.kt
...in/java/chat/rocket/android/main/presentation/MainView.kt
+10
-3
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+42
-53
ProfileFragment.kt
...in/java/chat/rocket/android/profile/ui/ProfileFragment.kt
+1
-0
PasswordFragment.kt
...t/rocket/android/settings/password/ui/PasswordFragment.kt
+1
-1
Text.kt
...src/main/java/chat/rocket/android/util/extensions/Text.kt
+0
-23
WrappedLiveData.kt
...java/chat/rocket/android/util/livedata/WrappedLiveData.kt
+0
-1
ic_search_white_24dp.xml
app/src/main/res/drawable/ic_search_white_24dp.xml
+4
-3
round_textview.xml
app/src/main/res/drawable/round_textview.xml
+2
-2
fragment_chat_room.xml
app/src/main/res/layout/fragment_chat_room.xml
+5
-4
fragment_chat_rooms.xml
app/src/main/res/layout/fragment_chat_rooms.xml
+3
-2
item_message.xml
app/src/main/res/layout/item_message.xml
+51
-47
message_list.xml
app/src/main/res/layout/message_list.xml
+27
-33
chatrooms.xml
app/src/main/res/menu/chatrooms.xml
+1
-1
strings.xml
app/src/main/res/values-es/strings.xml
+6
-1
strings.xml
app/src/main/res/values-fr/strings.xml
+7
-1
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+7
-1
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+5
-2
strings.xml
app/src/main/res/values-uk-rRU/strings.xml
+5
-1
strings.xml
app/src/main/res/values/strings.xml
+4
-0
styles.xml
app/src/main/res/values/styles.xml
+8
-3
dependencies.gradle
dependencies.gradle
+32
-23
build.gradle
emoji/build.gradle
+2
-0
ComposerEditText.kt
...c/main/java/chat/rocket/android/emoji/ComposerEditText.kt
+1
-1
Emoji.kt
emoji/src/main/java/chat/rocket/android/emoji/Emoji.kt
+3
-1
EmojiKeyboardPopup.kt
...main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
+110
-11
EmojiParser.kt
emoji/src/main/java/chat/rocket/android/emoji/EmojiParser.kt
+4
-2
EmojiPickerPopup.kt
...c/main/java/chat/rocket/android/emoji/EmojiPickerPopup.kt
+24
-4
EmojiRepository.kt
...rc/main/java/chat/rocket/android/emoji/EmojiRepository.kt
+51
-13
EmojiTypefaceSpan.kt
.../main/java/chat/rocket/android/emoji/EmojiTypefaceSpan.kt
+0
-17
Fitzpatrick.kt
emoji/src/main/java/chat/rocket/android/emoji/Fitzpatrick.kt
+30
-0
EmojiCategory.kt
.../java/chat/rocket/android/emoji/internal/EmojiCategory.kt
+5
-2
EmojiPagerAdapter.kt
...a/chat/rocket/android/emoji/internal/EmojiPagerAdapter.kt
+158
-0
Preferences.kt
...in/java/chat/rocket/android/emoji/internal/Preferences.kt
+4
-0
color_change_circle.xml
emoji/src/main/res/drawable/color_change_circle.xml
+8
-0
emoji_picker.xml
emoji/src/main/res/layout-land/emoji_picker.xml
+26
-0
color_select_popup.xml
emoji/src/main/res/layout/color_select_popup.xml
+107
-0
emoji_keyboard.xml
emoji/src/main/res/layout/emoji_keyboard.xml
+21
-10
emoji_row_item.xml
emoji/src/main/res/layout/emoji_row_item.xml
+8
-17
colors.xml
emoji/src/main/res/values/colors.xml
+8
-0
strings.xml
emoji/src/main/res/values/strings.xml
+1
-0
styles.xml
emoji/src/main/res/values/styles.xml
+7
-0
build.gradle
util/build.gradle
+4
-0
Rx.kt
util/src/main/java/chat/rocket/android/util/extension/Rx.kt
+1
-1
Widget.kt
...rc/main/java/chat/rocket/android/util/extension/Widget.kt
+17
-0
No files found.
app/build.gradle
View file @
212b870f
...
...
@@ -108,7 +108,6 @@ dependencies {
implementation
libraries
.
timber
implementation
libraries
.
threeTenABP
implementation
libraries
.
rxBinding
implementation
libraries
.
fresco
api
libraries
.
frescoOkHttp
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
212b870f
...
...
@@ -139,6 +139,11 @@ class ChatRoomAdapter(
}
}
fun
clearData
()
{
dataSet
.
clear
()
notifyDataSetChanged
()
}
fun
appendData
(
dataSet
:
List
<
BaseUiModel
<*
>>)
{
val
previousDataSetSize
=
this
.
dataSet
.
size
this
.
dataSet
.
addAll
(
dataSet
)
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
212b870f
package
chat.rocket.android.chatroom.presentation
import
android.graphics.BitmapFactory
import
android.net.Uri
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.adapter.AutoCompleteType
...
...
@@ -64,6 +63,7 @@ import chat.rocket.core.internal.rest.unstarMessage
import
chat.rocket.core.internal.rest.updateMessage
import
chat.rocket.core.internal.rest.uploadFile
import
chat.rocket.core.internal.rest.favorite
import
chat.rocket.core.internal.rest.searchMessages
import
chat.rocket.core.model.ChatRoomRole
import
chat.rocket.core.model.Command
import
chat.rocket.core.model.Message
...
...
@@ -158,7 +158,12 @@ class ChatRoomPresenter @Inject constructor(
}
?:
false
}
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
)
{
fun
loadMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
,
clearDataSet
:
Boolean
=
false
)
{
this
.
chatRoomId
=
chatRoomId
this
.
chatRoomType
=
chatRoomType
launchUI
(
strategy
)
{
...
...
@@ -173,13 +178,13 @@ class ChatRoomPresenter @Inject constructor(
)
)
if
(
oldMessages
.
isNotEmpty
())
{
view
.
showMessages
(
oldMessages
)
view
.
showMessages
(
oldMessages
,
clearDataSet
)
loadMissingMessages
()
}
else
{
loadAndShowMessages
(
chatRoomId
,
chatRoomType
,
offset
)
loadAndShowMessages
(
chatRoomId
,
chatRoomType
,
offset
,
clearDataSet
)
}
}
else
{
loadAndShowMessages
(
chatRoomId
,
chatRoomType
,
offset
)
loadAndShowMessages
(
chatRoomId
,
chatRoomType
,
offset
,
clearDataSet
)
}
// TODO: For now we are marking the room as read if we can get the messages (I mean, no exception occurs)
...
...
@@ -206,21 +211,49 @@ class ChatRoomPresenter @Inject constructor(
private
suspend
fun
loadAndShowMessages
(
chatRoomId
:
String
,
chatRoomType
:
String
,
offset
:
Long
=
0
offset
:
Long
=
0
,
clearDataSet
:
Boolean
)
{
val
messages
=
retryIO
(
description
=
"messages chatRoom: $chatRoomId, type: $chatRoomType, offset:
$offset"
)
{
retryIO
(
"loadAndShowMessages($chatRoomId, $chatRoomType,
$offset"
)
{
client
.
messages
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
30
).
result
}
messagesRepository
.
saveAll
(
messages
)
view
.
showMessages
(
mapper
.
map
(
messages
,
RoomUiModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
))
mapper
.
map
(
messages
,
RoomUiModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
,
isRoom
=
true
)
),
clearDataSet
)
}
fun
searchMessages
(
chatRoomId
:
String
,
searchText
:
String
)
{
launchUI
(
strategy
)
{
try
{
view
.
showLoading
()
val
messages
=
retryIO
(
"searchMessages($chatRoomId, $searchText)"
)
{
client
.
searchMessages
(
chatRoomId
,
searchText
).
result
}
view
.
showSearchedMessages
(
mapper
.
map
(
messages
,
RoomUiModel
(
chatRoles
,
chatIsBroadcast
,
true
)
)
)
}
catch
(
ex
:
Exception
)
{
Timber
.
e
(
ex
)
ex
.
message
?.
let
{
view
.
showMessage
(
it
)
}.
ifNull
{
view
.
showGenericErrorMessage
()
}
}
finally
{
view
.
hideLoading
()
}
}
}
fun
sendMessage
(
chatRoomId
:
String
,
text
:
String
,
messageId
:
String
?)
{
launchUI
(
strategy
)
{
try
{
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
212b870f
package
chat.rocket.android.chatroom.presentation
import
android.net.Uri
import
chat.rocket.android.chatroom.uimodel.BaseUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
...
...
@@ -23,8 +22,16 @@ interface ChatRoomView : LoadingView, MessageView {
* Shows the chat room messages.
*
* @param dataSet The data set to show.
* @param clearDataSet If true it will clear the previous data set.
*/
fun
showMessages
(
dataSet
:
List
<
BaseUiModel
<*
>>)
fun
showMessages
(
dataSet
:
List
<
BaseUiModel
<*
>>,
clearDataSet
:
Boolean
)
/**
* Shows the chat room messages in the basis of a search term.
*
* @param dataSet The data set to show.
*/
fun
showSearchedMessages
(
dataSet
:
List
<
BaseUiModel
<*
>>)
/**
* Send a message to a chat room.
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
212b870f
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/chatroom/ui/
Attachment
.kt
→
app/src/main/java/chat/rocket/android/chatroom/ui/
Dialog
.kt
View file @
212b870f
File moved
app/src/main/java/chat/rocket/android/chatroom/ui/Menu.kt
0 → 100644
View file @
212b870f
package
chat.rocket.android.chatroom.ui
import
android.content.Context
import
android.view.Menu
import
android.view.MenuItem
import
android.widget.EditText
import
androidx.appcompat.widget.SearchView
import
androidx.core.content.res.ResourcesCompat
import
chat.rocket.android.R
import
chat.rocket.android.util.extension.onQueryTextListener
import
chat.rocket.common.model.RoomType
internal
fun
ChatRoomFragment
.
setupMenu
(
menu
:
Menu
)
{
setupSearchMessageMenuItem
(
menu
,
requireContext
())
setupFavoriteMenuItem
(
menu
)
if
(
chatRoomType
!=
RoomType
.
DIRECT_MESSAGE
)
{
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_MEMBER
,
Menu
.
NONE
,
R
.
string
.
title_members_list
)
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_MENTIONS
,
Menu
.
NONE
,
R
.
string
.
msg_mentions
)
}
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_PINNED_MESSAGES
,
Menu
.
NONE
,
R
.
string
.
title_pinned_messages
)
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_FAVORITE_MESSAGES
,
Menu
.
NONE
,
R
.
string
.
title_favorite_messages
)
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_FILES
,
Menu
.
NONE
,
R
.
string
.
title_files
)
}
internal
fun
ChatRoomFragment
.
setOnMenuItemClickListener
(
item
:
MenuItem
)
{
when
(
item
.
itemId
)
{
MENU_ACTION_FAVORITE_UNFAVORITE_CHAT
->
presenter
.
toggleFavoriteChatRoom
(
chatRoomId
,
isFavorite
)
MENU_ACTION_MEMBER
->
presenter
.
toMembersList
(
chatRoomId
)
MENU_ACTION_MENTIONS
->
presenter
.
toMentions
(
chatRoomId
)
MENU_ACTION_PINNED_MESSAGES
->
presenter
.
toPinnedMessageList
(
chatRoomId
)
MENU_ACTION_FAVORITE_MESSAGES
->
presenter
.
toFavoriteMessageList
(
chatRoomId
)
MENU_ACTION_FILES
->
presenter
.
toFileList
(
chatRoomId
)
}
}
private
fun
ChatRoomFragment
.
setupSearchMessageMenuItem
(
menu
:
Menu
,
context
:
Context
)
{
val
searchItem
=
menu
.
add
(
Menu
.
NONE
,
Menu
.
NONE
,
Menu
.
NONE
,
R
.
string
.
title_search_message
).
setActionView
(
SearchView
(
context
))
.
setIcon
(
R
.
drawable
.
ic_search_white_24dp
)
.
setShowAsActionFlags
(
MenuItem
.
SHOW_AS_ACTION_IF_ROOM
or
MenuItem
.
SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW
)
(
searchItem
?.
actionView
as
?
SearchView
)
?.
let
{
// TODO: Check why we need to stylize the search text programmatically instead of by defining it in the styles.xml (ChatRoom.SearchView)
stylizeSearchView
(
it
,
context
)
setupSearchViewTextListener
(
it
)
if
(
it
.
isIconified
)
{
isSearchTermQueried
=
false
}
}
}
private
fun
stylizeSearchView
(
searchView
:
SearchView
,
context
:
Context
)
{
val
searchText
=
searchView
.
findViewById
<
EditText
>(
androidx
.
appcompat
.
R
.
id
.
search_src_text
)
searchText
.
setTextColor
(
ResourcesCompat
.
getColor
(
context
.
resources
,
R
.
color
.
color_white
,
null
))
searchText
.
setHintTextColor
(
ResourcesCompat
.
getColor
(
context
.
resources
,
R
.
color
.
color_white
,
null
)
)
}
private
fun
ChatRoomFragment
.
setupSearchViewTextListener
(
searchView
:
SearchView
)
{
searchView
.
onQueryTextListener
{
// TODO: We use isSearchTermQueried to avoid querying when the search view is expanded but the user doesn't start typing. Check for a native solution.
if
(
it
.
isEmpty
()
&&
isSearchTermQueried
)
{
presenter
.
loadMessages
(
chatRoomId
,
chatRoomType
,
clearDataSet
=
true
)
}
else
if
(
it
.
isNotEmpty
()){
presenter
.
searchMessages
(
chatRoomId
,
it
)
isSearchTermQueried
=
true
}
}
}
private
fun
ChatRoomFragment
.
setupFavoriteMenuItem
(
menu
:
Menu
)
{
if
(
isFavorite
)
{
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_FAVORITE_UNFAVORITE_CHAT
,
Menu
.
NONE
,
R
.
string
.
title_unfavorite_chat
).
setIcon
(
R
.
drawable
.
ic_star_yellow_24dp
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_IF_ROOM
)
}
else
{
menu
.
add
(
Menu
.
NONE
,
MENU_ACTION_FAVORITE_UNFAVORITE_CHAT
,
Menu
.
NONE
,
R
.
string
.
title_favorite_chat
).
setIcon
(
R
.
drawable
.
ic_star_border_white_24dp
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_IF_ROOM
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/di/ChatRoomsFragmentModule.kt
View file @
212b870f
...
...
@@ -46,15 +46,19 @@ class ChatRoomsFragmentModule {
@Provides
@PerFragment
fun
provideRocketChatClient
(
factory
:
RocketChatClientFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
RocketChatClient
{
fun
provideRocketChatClient
(
factory
:
RocketChatClientFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
RocketChatClient
{
return
factory
.
create
(
currentServer
)
}
@Provides
@PerFragment
fun
provideDatabaseManager
(
factory
:
DatabaseManagerFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
DatabaseManager
{
fun
provideDatabaseManager
(
factory
:
DatabaseManagerFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
DatabaseManager
{
return
factory
.
create
(
currentServer
)
}
...
...
@@ -64,31 +68,39 @@ class ChatRoomsFragmentModule {
@Provides
@PerFragment
fun
provideConnectionManager
(
factory
:
ConnectionManagerFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
ConnectionManager
{
fun
provideConnectionManager
(
factory
:
ConnectionManagerFactory
,
@Named
(
"currentServer"
)
currentServer
:
String
):
ConnectionManager
{
return
factory
.
create
(
currentServer
)
}
@Provides
@PerFragment
fun
provideFetchChatRoomsInteractor
(
client
:
RocketChatClient
,
dbManager
:
DatabaseManager
):
FetchChatRoomsInteractor
{
fun
provideFetchChatRoomsInteractor
(
client
:
RocketChatClient
,
dbManager
:
DatabaseManager
):
FetchChatRoomsInteractor
{
return
FetchChatRoomsInteractor
(
client
,
dbManager
)
}
@Provides
@PerFragment
fun
providePublicSettings
(
repository
:
SettingsRepository
,
@Named
(
"currentServer"
)
currentServer
:
String
):
PublicSettings
{
fun
providePublicSettings
(
repository
:
SettingsRepository
,
@Named
(
"currentServer"
)
currentServer
:
String
):
PublicSettings
{
return
repository
.
get
(
currentServer
)
}
@Provides
@PerFragment
fun
provideRoomMapper
(
context
:
Application
,
repository
:
SettingsRepository
,
localRepository
:
LocalRepository
,
@Named
(
"currentServer"
)
serverUrl
:
String
):
RoomUiModelMapper
{
fun
provideRoomMapper
(
context
:
Application
,
repository
:
SettingsRepository
,
localRepository
:
LocalRepository
,
@Named
(
"currentServer"
)
serverUrl
:
String
):
RoomUiModelMapper
{
return
RoomUiModelMapper
(
context
,
repository
.
get
(
serverUrl
),
localRepository
,
serverUrl
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
212b870f
...
...
@@ -32,6 +32,7 @@ import chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.helper.ChatRoomsSortOrder
import
chat.rocket.android.helper.Constants
import
chat.rocket.android.helper.SharedPreferenceHelper
import
chat.rocket.android.util.extension.onQueryTextListener
import
chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.inflate
...
...
@@ -164,15 +165,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
searchView
=
searchItem
?.
actionView
as
?
SearchView
searchView
?.
setIconifiedByDefault
(
false
)
searchView
?.
maxWidth
=
Integer
.
MAX_VALUE
searchView
?.
setOnQueryTextListener
(
object
:
SearchView
.
OnQueryTextListener
{
override
fun
onQueryTextSubmit
(
query
:
String
?):
Boolean
{
return
queryChatRoomsByName
(
query
)
}
override
fun
onQueryTextChange
(
newText
:
String
?):
Boolean
{
return
queryChatRoomsByName
(
newText
)
}
})
searchView
?.
onQueryTextListener
{
queryChatRoomsByName
(
it
)
}
val
expandListener
=
object
:
MenuItem
.
OnActionExpandListener
{
override
fun
onMenuItemActionCollapse
(
item
:
MenuItem
):
Boolean
{
...
...
@@ -297,25 +290,25 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private
fun
showConnectionState
(
state
:
State
)
{
Timber
.
d
(
"Got new state: $state"
)
ui
{
connection_status_text
.
fadeIn
()
text_connection_status
.
fadeIn
()
handler
.
removeCallbacks
(
dismissStatus
)
when
(
state
)
{
is
State
.
Connected
->
{
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connected
)
text_connection_status
.
text
=
getString
(
R
.
string
.
status_connected
)
handler
.
postDelayed
(
dismissStatus
,
2000
)
}
is
State
.
Disconnected
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
connection_status_text
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
is
State
.
Disconnected
->
text_connection_status
.
text
=
getString
(
R
.
string
.
status_disconnected
)
is
State
.
Connecting
->
text_connection_status
.
text
=
getString
(
R
.
string
.
status_connecting
)
is
State
.
Authenticating
->
text_connection_status
.
text
=
getString
(
R
.
string
.
status_authenticating
)
is
State
.
Disconnecting
->
text_connection_status
.
text
=
getString
(
R
.
string
.
status_disconnecting
)
is
State
.
Waiting
->
text_connection_status
.
text
=
getString
(
R
.
string
.
status_waiting
,
state
.
seconds
)
}
}
}
private
val
dismissStatus
=
{
if
(
connection_status_text
!=
null
)
{
connection_status_text
.
fadeOut
()
if
(
text_connection_status
!=
null
)
{
text_connection_status
.
fadeOut
()
}
}
...
...
@@ -331,4 +324,4 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
}
return
true
}
}
\ No newline at end of file
}
app/src/main/java/chat/rocket/android/createchannel/ui/CreateChannelFragment.kt
View file @
212b870f
...
...
@@ -20,7 +20,7 @@ import chat.rocket.android.createchannel.presentation.CreateChannelView
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.members.adapter.MembersAdapter
import
chat.rocket.android.members.uimodel.MemberUiModel
import
chat.rocket.android.util.extension
s
.asObservable
import
chat.rocket.android.util.extension.asObservable
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.ui
...
...
@@ -221,7 +221,6 @@ class CreateChannelFragment : Fragment(), CreateChannelView, ActionMode.Callback
LinearLayoutManager
(
context
,
RecyclerView
.
VERTICAL
,
false
)
recycler_view
.
addItemDecoration
(
DividerItemDecoration
(
it
,
DividerItemDecoration
.
HORIZONTAL
))
recycler_view
.
adapter
=
adapter
}
}
...
...
app/src/main/java/chat/rocket/android/dagger/module/AppModule.kt
View file @
212b870f
...
...
@@ -247,6 +247,7 @@ class AppModule {
val
res
=
context
.
resources
return
SpannableConfiguration
.
builder
(
context
)
.
theme
(
SpannableTheme
.
builder
()
.
blockMargin
(
0
)
.
linkColor
(
res
.
getColor
(
R
.
color
.
colorAccent
))
.
build
())
.
build
()
...
...
app/src/main/java/chat/rocket/android/helper/MessageParser.kt
View file @
212b870f
...
...
@@ -10,6 +10,7 @@ import androidx.core.content.res.ResourcesCompat
import
android.text.Spanned
import
android.text.style.ClickableSpan
import
android.text.style.ReplacementSpan
import
android.text.style.StyleSpan
import
android.util.Patterns
import
android.view.View
import
chat.rocket.android.R
...
...
@@ -23,6 +24,10 @@ import chat.rocket.common.model.SimpleUser
import
chat.rocket.core.model.Message
import
org.commonmark.node.AbstractVisitor
import
org.commonmark.node.Document
import
org.commonmark.node.ListItem
import
org.commonmark.node.Node
import
org.commonmark.node.OrderedList
import
org.commonmark.node.Paragraph
import
org.commonmark.node.Text
import
ru.noties.markwon.Markwon
import
ru.noties.markwon.SpannableBuilder
...
...
@@ -59,7 +64,7 @@ class MessageParser @Inject constructor(
val
builder
=
SpannableBuilder
()
val
content
=
EmojiRepository
.
shortnameToUnicode
(
text
,
true
)
val
parentNode
=
parser
.
parse
(
toLenientMarkdown
(
content
))
parentNode
.
accept
(
Spannable
MarkdownVisitor
(
configuration
,
builder
))
parentNode
.
accept
(
MarkdownVisitor
(
configuration
,
builder
))
parentNode
.
accept
(
LinkVisitor
(
builder
))
parentNode
.
accept
(
EmojiVisitor
(
configuration
,
builder
))
message
.
mentions
?.
let
{
...
...
@@ -138,6 +143,37 @@ class MessageParser @Inject constructor(
}
}
class
MarkdownVisitor
(
configuration
:
SpannableConfiguration
,
val
builder
:
SpannableBuilder
)
:
SpannableMarkdownVisitor
(
configuration
,
builder
)
{
/**
* NOOP
*/
override
fun
visit
(
orderedList
:
OrderedList
)
{
var
number
=
orderedList
.
startNumber
val
delimiter
=
orderedList
.
delimiter
var
node
:
Node
?
=
orderedList
.
firstChild
while
(
node
!=
null
)
{
if
(
node
is
ListItem
)
{
newLine
()
builder
.
append
(
"$number$delimiter "
)
super
.
visit
(
node
.
firstChild
as
Paragraph
)
newLine
()
}
number
++
node
=
node
.
next
}
}
private
fun
newLine
()
{
if
(
builder
.
length
()
>
0
&&
'\n'
!=
builder
.
lastChar
())
{
builder
.
append
(
'\n'
)
}
}
}
class
LinkVisitor
(
private
val
builder
:
SpannableBuilder
)
:
AbstractVisitor
()
{
override
fun
visit
(
text
:
Text
)
{
...
...
app/src/main/java/chat/rocket/android/helper/OauthHelper.kt
View file @
212b870f
...
...
@@ -84,7 +84,7 @@ object OauthHelper {
* @return The Facebook Oauth URL.
*/
fun
getFacebookOauthUrl
(
clientId
:
String
,
serverUrl
:
String
,
state
:
String
):
String
{
return
"https://facebook.com/v2.9/dialog/oauth"
+
return
"https://facebook.com/v2.9/dialog/oauth"
+
"?client_id=$clientId"
+
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/facebook?close"
+
"&state=$state"
+
...
...
@@ -113,12 +113,18 @@ object OauthHelper {
state
:
String
,
scope
:
String
):
String
{
return
host
+
authorizePath
+
(
authorizePath
+
"?client_id=$clientId"
+
"&redirect_uri=${serverUrl.removeTrailingSlash()}/_oauth/$serviceName"
+
"&state=$state"
+
"&scope=$scope"
+
"&response_type=code"
).
let
{
return
if
(
it
.
contains
(
host
))
{
it
}
else
{
host
+
it
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
212b870f
...
...
@@ -38,20 +38,20 @@ import timber.log.Timber
import
javax.inject.Inject
class
MainPresenter
@Inject
constructor
(
private
val
view
:
MainView
,
private
val
strategy
:
CancelStrategy
,
private
val
navigator
:
MainNavigator
,
private
val
tokenRepository
:
TokenRepository
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
localRepository
:
LocalRepository
,
private
val
navHeaderMapper
:
NavHeaderUiModelMapper
,
private
val
saveAccountInteractor
:
SaveAccountInteractor
,
private
val
getAccountsInteractor
:
GetAccountsInteractor
,
private
val
removeAccountInteractor
:
RemoveAccountInteractor
,
private
val
factory
:
RocketChatClientFactory
,
dbManagerFactory
:
DatabaseManagerFactory
,
getSettingsInteractor
:
GetSettingsInteractor
,
managerFactory
:
ConnectionManagerFactory
private
val
view
:
MainView
,
private
val
strategy
:
CancelStrategy
,
private
val
navigator
:
MainNavigator
,
private
val
tokenRepository
:
TokenRepository
,
private
val
serverInteractor
:
GetCurrentServerInteractor
,
private
val
localRepository
:
LocalRepository
,
private
val
navHeaderMapper
:
NavHeaderUiModelMapper
,
private
val
saveAccountInteractor
:
SaveAccountInteractor
,
private
val
getAccountsInteractor
:
GetAccountsInteractor
,
private
val
removeAccountInteractor
:
RemoveAccountInteractor
,
private
val
factory
:
RocketChatClientFactory
,
dbManagerFactory
:
DatabaseManagerFactory
,
getSettingsInteractor
:
GetSettingsInteractor
,
managerFactory
:
ConnectionManagerFactory
)
:
CheckServerPresenter
(
strategy
,
factory
,
view
=
view
)
{
private
val
currentServer
=
serverInteractor
.
get
()
!!
private
val
manager
=
managerFactory
.
create
(
currentServer
)
...
...
@@ -69,16 +69,34 @@ class MainPresenter @Inject constructor(
fun
toCreateChannel
()
=
navigator
.
toCreateChannel
()
fun
loadServerAccounts
()
{
launchUI
(
strategy
)
{
try
{
view
.
setupServerAccountList
(
getAccountsInteractor
.
get
())
}
catch
(
ex
:
Exception
)
{
when
(
ex
)
{
is
RocketChatAuthException
->
logout
()
else
->
{
Timber
.
d
(
ex
,
"Error loading serve accounts"
)
ex
.
message
?.
let
{
view
.
showMessage
(
it
)
}.
ifNull
{
view
.
showGenericErrorMessage
()
}
}
}
}
}
}
fun
loadCurrentInfo
()
{
checkServerInfo
(
currentServer
)
launchUI
(
strategy
)
{
try
{
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
val
model
=
navHeaderMapper
.
mapToUiModel
(
me
)
saveAccount
(
model
)
view
.
setup
NavHeader
(
model
,
getAccountsInteractor
.
get
()
)
view
.
setup
UserAccountInfo
(
model
)
}
catch
(
ex
:
Exception
)
{
when
(
ex
)
{
is
RocketChatAuthException
->
{
...
...
@@ -208,8 +226,6 @@ class MainPresenter @Inject constructor(
}
}
private
suspend
fun
updateMyself
(
myself
:
Myself
)
{
val
model
=
navHeaderMapper
.
mapToUiModel
(
myself
)
view
.
setupNavHeader
(
model
,
getAccountsInteractor
.
get
())
}
private
fun
updateMyself
(
myself
:
Myself
)
=
view
.
setupUserAccountInfo
(
navHeaderMapper
.
mapToUiModel
(
myself
))
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/presentation/MainView.kt
View file @
212b870f
...
...
@@ -16,17 +16,24 @@ interface MainView : MessageView, VersionCheckView {
fun
showUserStatus
(
userStatus
:
UserStatus
)
/**
* Setups the
navigation header.
* Setups the
user account info (displayed in the nav. header)
*
* @param uiModel The [NavHeaderUiModel].
* @param accounts The list of accounts.
*/
fun
setupNavHeader
(
uiModel
:
NavHeaderUiModel
,
accounts
:
List
<
Account
>)
fun
setupUserAccountInfo
(
uiModel
:
NavHeaderUiModel
)
/**
* Setups the server account list.
*
* @param serverAccountList The list of server accounts.
*/
fun
setupServerAccountList
(
serverAccountList
:
List
<
Account
>)
fun
closeServerSelection
()
fun
invalidateToken
(
token
:
String
)
fun
showProgress
()
fun
hideProgress
()
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
View file @
212b870f
...
...
@@ -10,7 +10,6 @@ import androidx.appcompat.app.AppCompatActivity
import
androidx.recyclerview.widget.LinearLayoutManager
import
android.view.Gravity
import
android.view.MenuItem
import
android.view.View
import
androidx.annotation.IdRes
import
androidx.drawerlayout.widget.DrawerLayout
import
chat.rocket.android.BuildConfig
...
...
@@ -56,6 +55,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
private
var
expanded
=
false
private
val
headerLayout
by
lazy
{
view_navigation
.
getHeaderView
(
0
)
}
private
var
chatRoomId
:
String
?
=
null
private
var
progressDialog
:
ProgressDialog
?
=
null
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
AndroidInjection
.
inject
(
this
)
...
...
@@ -75,6 +75,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
presenter
.
connect
()
presenter
.
loadServerAccounts
()
presenter
.
loadCurrentInfo
()
setupToolbar
()
setupNavigationView
()
...
...
@@ -119,8 +120,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
}
}
override
fun
setupNavHeader
(
uiModel
:
NavHeaderUiModel
,
accounts
:
List
<
Account
>)
{
Timber
.
d
(
"Setting up nav header: $uiModel"
)
override
fun
setupUserAccountInfo
(
uiModel
:
NavHeaderUiModel
)
{
with
(
headerLayout
)
{
with
(
uiModel
)
{
if
(
userStatus
!=
null
)
{
...
...
@@ -139,10 +139,43 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
}
text_server_url
.
text
=
uiModel
.
serverUrl
}
setupAccountsList
(
headerLayout
,
accounts
)
}
}
override
fun
setupServerAccountList
(
serverAccountList
:
List
<
Account
>)
{
accounts_list
.
layoutManager
=
LinearLayoutManager
(
this
)
accounts_list
.
adapter
=
AccountsAdapter
(
serverAccountList
,
object
:
Selector
{
override
fun
onStatusSelected
(
userStatus
:
UserStatus
)
{
presenter
.
changeDefaultStatus
(
userStatus
)
}
override
fun
onAccountSelected
(
serverUrl
:
String
)
{
presenter
.
changeServer
(
serverUrl
)
}
override
fun
onAddedAccountSelected
()
{
presenter
.
addNewServer
()
}
})
headerLayout
.
account_container
.
setOnClickListener
{
it
.
image_account_expand
.
rotateBy
(
180f
)
if
(
expanded
)
{
accounts_list
.
fadeOut
()
}
else
{
accounts_list
.
fadeIn
()
}
expanded
=
!
expanded
}
headerLayout
.
image_avatar
.
setOnClickListener
{
view_navigation
.
menu
.
findItem
(
R
.
id
.
action_update_profile
).
isChecked
=
true
presenter
.
toUserProfile
()
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
}
}
override
fun
closeServerSelection
()
{
view_navigation
.
getHeaderView
(
0
).
account_container
.
performClick
()
}
...
...
@@ -174,9 +207,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
.
show
()
}
override
fun
invalidateToken
(
token
:
String
)
{
override
fun
invalidateToken
(
token
:
String
)
=
FirebaseInstanceId
.
getInstance
().
deleteToken
(
token
,
FirebaseMessaging
.
INSTANCE_ID_SCOPE
)
}
override
fun
showMessage
(
resId
:
Int
)
=
showToast
(
resId
)
...
...
@@ -221,57 +253,14 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
}
}
private
fun
setupAccountsList
(
header
:
View
,
accounts
:
List
<
Account
>)
{
accounts_list
.
layoutManager
=
LinearLayoutManager
(
this
)
accounts_list
.
adapter
=
AccountsAdapter
(
accounts
,
object
:
Selector
{
override
fun
onStatusSelected
(
userStatus
:
UserStatus
)
{
presenter
.
changeDefaultStatus
(
userStatus
)
}
override
fun
onAccountSelected
(
serverUrl
:
String
)
{
presenter
.
changeServer
(
serverUrl
)
}
override
fun
onAddedAccountSelected
()
{
presenter
.
addNewServer
()
}
})
header
.
account_container
.
setOnClickListener
{
header
.
image_account_expand
.
rotateBy
(
180f
)
if
(
expanded
)
{
accounts_list
.
fadeOut
()
}
else
{
accounts_list
.
fadeIn
()
}
expanded
=
!
expanded
}
header
.
image_avatar
.
setOnClickListener
{
view_navigation
.
menu
.
findItem
(
R
.
id
.
action_update_profile
).
isChecked
=
true
presenter
.
toUserProfile
()
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
}
}
fun
getDrawerLayout
():
DrawerLayout
{
return
drawer_layout
}
fun
getDrawerLayout
():
DrawerLayout
=
drawer_layout
fun
openDrawer
()
{
drawer_layout
.
openDrawer
(
Gravity
.
START
)
}
fun
openDrawer
()
=
drawer_layout
.
openDrawer
(
Gravity
.
START
)
fun
closeDrawer
()
{
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
}
fun
closeDrawer
()
=
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
fun
setCheckedNavDrawerItem
(
@IdRes
item
:
Int
)
{
view_navigation
.
setCheckedItem
(
item
)
}
fun
setCheckedNavDrawerItem
(
@IdRes
item
:
Int
)
=
view_navigation
.
setCheckedItem
(
item
)
private
var
progressDialog
:
ProgressDialog
?
=
null
override
fun
showProgress
()
{
progressDialog
=
ProgressDialog
.
show
(
this
,
getString
(
R
.
string
.
app_name
),
getString
(
R
.
string
.
msg_log_out
),
true
,
false
)
}
...
...
app/src/main/java/chat/rocket/android/profile/ui/ProfileFragment.kt
View file @
212b870f
...
...
@@ -11,6 +11,7 @@ import chat.rocket.android.R
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.profile.presentation.ProfilePresenter
import
chat.rocket.android.profile.presentation.ProfileView
import
chat.rocket.android.util.extension.asObservable
import
chat.rocket.android.util.extensions.*
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.disposables.Disposable
...
...
app/src/main/java/chat/rocket/android/settings/password/ui/PasswordFragment.kt
View file @
212b870f
...
...
@@ -9,7 +9,7 @@ import chat.rocket.android.settings.password.presentation.PasswordPresenter
import
chat.rocket.android.settings.password.presentation.PasswordView
import
chat.rocket.android.util.extensions.inflate
import
androidx.appcompat.view.ActionMode
import
chat.rocket.android.util.extension
s
.asObservable
import
chat.rocket.android.util.extension.asObservable
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.android.util.extensions.ui
import
dagger.android.support.AndroidSupportInjection
...
...
app/src/main/java/chat/rocket/android/util/extensions/Text.kt
View file @
212b870f
...
...
@@ -5,10 +5,8 @@ import android.text.Spanned
import
android.text.TextUtils
import
android.util.Base64
import
android.util.Patterns
import
android.widget.EditText
import
android.widget.TextView
import
chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiTypefaceSpan
import
org.json.JSONObject
import
ru.noties.markwon.Markwon
import
java.net.URLDecoder
...
...
@@ -21,21 +19,6 @@ fun String.ifEmpty(value: String): String {
return
this
}
fun
CharSequence
.
ifEmpty
(
value
:
String
):
CharSequence
{
if
(
isEmpty
())
{
return
value
}
return
this
}
fun
EditText
.
erase
()
{
this
.
text
.
clear
()
val
spans
=
this
.
text
.
getSpans
(
0
,
text
.
length
,
EmojiTypefaceSpan
::
class
.
java
)
spans
.
forEach
{
text
.
removeSpan
(
it
)
}
}
fun
String
.
isEmail
():
Boolean
=
Patterns
.
EMAIL_ADDRESS
.
matcher
(
this
).
matches
()
fun
String
.
encodeToBase64
():
String
{
...
...
@@ -94,9 +77,3 @@ var TextView.content: CharSequence?
Markwon
.
scheduleDrawables
(
this
)
Markwon
.
scheduleTableRows
(
this
)
}
var
TextView
.
spanned
:
CharSequence
?
get
()
=
text
set
(
value
)
{
text
=
spanned
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/livedata/WrappedLiveData.kt
View file @
212b870f
...
...
@@ -11,7 +11,6 @@ import kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
kotlin.coroutines.experimental.CoroutineContext
class
WrappedLiveData
<
Source
,
Output
>(
private
val
runContext
:
CoroutineContext
=
CommonPool
,
private
val
source
:
LiveData
<
Source
>,
...
...
app/src/main/res/drawable/ic_search_white_24
px
.xml
→
app/src/main/res/drawable/ic_search_white_24
dp
.xml
View file @
212b870f
...
...
@@ -2,9 +2,10 @@
android:width=
"24dp"
android:height=
"24dp"
android:autoMirrored=
"true"
android:viewportHeight=
"24.0"
android:viewportWidth=
"24.0"
>
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
<path
android:fillColor=
"#FFFFFF"
android:pathData=
"M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"
/>
</vector>
</vector>
\ No newline at end of file
app/src/main/res/drawable/round_textview.xml
View file @
212b870f
...
...
@@ -4,7 +4,7 @@
<solid
android:color=
"@color/colorAccent"
/>
<size
android:width=
"2
0
dp"
android:height=
"2
0
dp"
/>
android:width=
"2
4
dp"
android:height=
"2
4
dp"
/>
</shape>
\ No newline at end of file
app/src/main/res/layout/fragment_chat_room.xml
View file @
212b870f
...
...
@@ -12,9 +12,9 @@
android:layout_width=
"0dp"
android:layout_height=
"0dp"
app:layout_constraintBottom_toTopOf=
"@id/text_typing_status"
app:layout_constraintTop_toBottomOf=
"@+id/text_connection_status"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
>
app:layout_constraintStart_toStartOf=
"parent"
>
<include
android:id=
"@+id/layout_message_list"
...
...
@@ -117,7 +117,7 @@
app:layout_constraintBottom_toTopOf=
"@id/layout_message_composer"
/>
<TextView
android:id=
"@+id/
connection_status_text
"
android:id=
"@+id/
text_connection_status
"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:alpha=
"0"
...
...
@@ -127,6 +127,7 @@
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:textColor=
"@color/colorWhite"
android:visibility=
"gone"
app:layout_constraintTop_toTopOf=
"parent"
tools:alpha=
"1"
tools:text=
"connected"
tools:visibility=
"visible"
/>
...
...
@@ -144,4 +145,4 @@
app:layout_constraintTop_toTopOf=
"parent"
tools:visibility=
"visible"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
app/src/main/res/layout/fragment_chat_rooms.xml
View file @
212b870f
...
...
@@ -8,6 +8,7 @@
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recycler_view"
android:layout_below=
"@+id/text_connection_status"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
...
...
@@ -31,7 +32,7 @@
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/
connection_status_text
"
android:id=
"@+id/
text_connection_status
"
android:layout_width=
"match_parent"
android:layout_height=
"32dp"
android:alpha=
"0"
...
...
@@ -55,4 +56,4 @@
android:textSize=
"20sp"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
</RelativeLayout>
\ No newline at end of file
</RelativeLayout>
app/src/main/res/layout/item_message.xml
View file @
212b870f
...
...
@@ -8,10 +8,10 @@
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"
>
android:paddingTop=
"@dimen/message_item_top_and_bottom_padding"
android:paddingEnd=
"@dimen/screen_edge_left_and_right_padding"
android:paddingBottom=
"@dimen/message_item_top_and_bottom_padding"
>
<LinearLayout
android:id=
"@+id/new_messages_notif"
...
...
@@ -19,8 +19,8 @@
android:layout_height=
"wrap_content"
android:orientation=
"horizontal"
android:visibility=
"gone"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:visibility=
"visible"
>
...
...
@@ -56,63 +56,67 @@
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/new_messages_notif"
/>
<LinearLayout
android:id=
"@+id/top_container"
android:layout_width=
"0dp"
<TextView
android:id=
"@+id/text_sender"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"16dp"
a
ndroid:orientation=
"horizontal
"
a
pp:layout_constraintHorizontal_bias=
"0.5
"
app:layout_constraintStart_toEndOf=
"@+id/layout_avatar"
app:layout_constraintTop_toBottomOf=
"@+id/new_messages_notif"
>
app:layout_constraintTop_toBottomOf=
"@+id/new_messages_notif"
tools:text=
"Ronald Perkins"
/>
<TextView
android:id=
"@+id/text_sender"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
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"
/>
<TextView
android:id=
"@+id/text_message_time"
style=
"@style/Timestamp.TextView"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"4dp"
app:layout_constraintBaseline_toBaselineOf=
"@+id/text_sender"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/text_sender"
tools:text=
"11:45 PM"
/>
<TextView
android:id=
"@+id/text_edit_indicator"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:text=
"@string/msg_edited"
android:textStyle=
"italic"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/text_edit_indicator"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"4dp"
android:text=
"@string/msg_edited"
android:textStyle=
"italic"
android:visibility=
"gone"
app:layout_constraintBaseline_toBaselineOf=
"@+id/text_sender"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/text_message_time"
tools:visibility=
"visible"
/>
<ImageView
android:id=
"@+id/image_star_indicator"
android:layout_width=
"14dp"
android:layout_height=
"14dp"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"8dp"
android:layout_marginTop=
"2dp"
android:src=
"@drawable/ic_action_message_star_24dp"
android:visibility=
"gone"
tools:visibility=
"visible"
/>
</LinearLayout>
<ImageView
android:id=
"@+id/image_star_indicator"
android:layout_width=
"14dp"
android:layout_height=
"14dp"
android:layout_gravity=
"center_vertical"
android:layout_marginStart=
"4dp"
android:layout_marginTop=
"2dp"
android:src=
"@drawable/ic_action_message_star_24dp"
android:visibility=
"gone"
app:layout_constraintBottom_toBottomOf=
"@+id/text_sender"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/text_edit_indicator"
app:layout_constraintTop_toTopOf=
"@+id/text_sender"
tools:visibility=
"visible"
/>
<TextView
android:id=
"@+id/text_content"
style=
"@style/Message.TextView"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"2dp"
android:layout_marginTop=
"5dp"
a
pp:layout_constraintStart_toStartOf=
"@+id/top_container
"
a
ndroid:layout_marginBottom=
"2dp
"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/top_container"
android:textDirection=
"locale"
android:layout_marginEnd=
"4dp"
app:layout_constraintStart_toStartOf=
"@+id/text_sender"
app:layout_constraintTop_toBottomOf=
"@+id/text_sender"
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
...
...
app/src/main/res/layout/message_list.xml
View file @
212b870f
...
...
@@ -2,7 +2,8 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
android:layout_height=
"match_parent"
xmlns:tools=
"http://schemas.android.com/tools"
>
<androidx.recyclerview.widget.RecyclerView
android:id=
"@+id/recycler_view"
...
...
@@ -10,40 +11,33 @@
android:layout_height=
"match_parent"
android:scrollbars=
"vertical"
/>
<RelativeLayout
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=
"@+id/button_fab"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentEnd=
"true"
android:layout_alignParentBottom=
"true"
app:layout_anchor=
"@id/recycler_view"
app:layout_anchorGravity=
"bottom|end"
>
android:visibility=
"invisible"
android:layout_margin=
"16dp"
android:src=
"@drawable/ic_arrow_downward_24dp"
android:theme=
"@style/Theme.AppCompat"
android:tint=
"@color/actionMenuColor"
app:backgroundTint=
"@color/colorWhite"
tools:visibility=
"visible"
android:layout_gravity=
"end|bottom"
/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=
"@+id/button_fab"
android:layout_alignParentEnd=
"true"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:visibility=
"invisible"
android:layout_margin=
"16dp"
android:src=
"@drawable/ic_arrow_downward_24dp"
android:theme=
"@style/Theme.AppCompat"
android:tint=
"@color/actionMenuColor"
app:backgroundTint=
"@color/colorWhite"
app:fabSize=
"mini"
/>
<TextView
android:id=
"@+id/text_count"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:elevation=
"7dp"
android:padding=
"2dp"
android:gravity=
"center"
android:layout_margin=
"10dp"
android:textColor=
"@color/colorWhite"
android:visibility=
"invisible"
android:layout_alignParentEnd=
"true"
android:textSize=
"@dimen/message_time_text_size"
android:background=
"@drawable/round_textview"
/>
</RelativeLayout>
<TextView
android:id=
"@+id/text_count"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:elevation=
"6dp"
android:gravity=
"center"
android:textColor=
"@color/colorWhite"
android:visibility=
"invisible"
android:textSize=
"@dimen/message_time_text_size"
android:background=
"@drawable/round_textview"
tools:visibility=
"visible"
tools:text=
"1"
android:layout_gravity=
"center_vertical"
app:layout_anchor=
"@id/button_fab"
app:layout_anchorGravity=
"end"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
app/src/main/res/menu/chatrooms.xml
View file @
212b870f
...
...
@@ -4,7 +4,7 @@
<item
android:id=
"@+id/action_search"
android:icon=
"@drawable/ic_search_white_24
px
"
android:icon=
"@drawable/ic_search_white_24
dp
"
android:title=
"@string/action_search"
app:actionViewClass=
"androidx.appcompat.widget.SearchView"
app:showAsAction=
"ifRoom|collapseActionView"
/>
...
...
app/src/main/res/values-es/strings.xml
View file @
212b870f
...
...
@@ -191,6 +191,10 @@
// TODO: Add proper translation.
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Search message -->
<!-- TODO Add proper translation-->
<string
name=
"title_search_message"
>
Search message
</string>
<!-- Favorite/Unfavorite chat room -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_chat"
>
Favorite chat
</string>
...
...
@@ -263,6 +267,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
Sin emojis recientes
</string>
<string
name=
"alert_title_default_skin_tone"
>
Tono de piel predeterminado
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
Ordenar
</string>
...
...
@@ -284,5 +289,5 @@
<string
name=
"notif_action_reply_hint"
>
RESPUESTA
</string>
<string
name=
"notif_error_sending"
>
La respuesta ha fallado. Inténtalo de nuevo.
</string>
<string
name=
"notif_success_sending"
>
Mensaje enviado a %1$s!
</string>
<string
name=
"msg_log_out"
>
Saliendo de tu cuenta
...
</string>
<string
name=
"msg_log_out"
>
Saliendo de tu cuenta
…
</string>
</resources>
app/src/main/res/values-fr/strings.xml
View file @
212b870f
...
...
@@ -193,6 +193,10 @@
// TODO: Add proper translation.
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Search message -->
<!-- TODO Add proper translation-->
<string
name=
"title_search_message"
>
Search message
</string>
<!-- Favorite/Unfavorite chat room -->
<!-- TODO Add proper translation-->
<string
name=
"title_favorite_chat"
>
Favorite chat
</string>
...
...
@@ -265,6 +269,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
Aucun emoji récent
</string>
<string
name=
"alert_title_default_skin_tone"
>
Tonalité de peau par défaut
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
Trier
</string>
...
...
@@ -286,5 +291,6 @@
<string
name=
"notif_action_reply_hint"
>
RÉPONDRE
</string>
<string
name=
"notif_error_sending"
>
La réponse a échoué. Veuillez réessayer.
</string>
<string
name=
"notif_success_sending"
>
Message envoyé à %1$s!
</string>
<string
name=
"msg_log_out"
>
Logging out...
</string>
<!-- TODO Add proper translation-->
<string
name=
"msg_log_out"
>
Logging out…
</string>
</resources>
app/src/main/res/values-hi-rIN/strings.xml
View file @
212b870f
...
...
@@ -174,6 +174,10 @@
<string
name=
"permission_pinning_not_allowed"
>
पिनि करने की अनुमति नहीं है
</string>
<string
name=
"permission_starring_not_allowed"
>
तारांकित की अनुमति नहीं है
</string>
<!-- Search message -->
<!-- TODO Add proper translation-->
<string
name=
"title_search_message"
>
Search message
</string>
<!-- Favorite/Unfavorite chat room -->
<string
name=
"title_favorite_chat"
>
पसंदीदा चैट
</string>
<string
name=
"title_unfavorite_chat"
>
नापसंद चैट
</string>
...
...
@@ -242,6 +246,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
कोई नया इमोजी नहीं
</string>
<string
name=
"alert_title_default_skin_tone"
>
डिफ़ॉल्ट त्वचा टोन
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
क्रम
</string>
...
...
@@ -263,5 +268,6 @@
<string
name=
"notif_action_reply_hint"
>
जवाब
</string>
<string
name=
"notif_error_sending"
>
उत्तर विफल हुआ है। कृपया फिर से प्रयास करें।
</string>
<string
name=
"notif_success_sending"
>
संदेश भेजा गया %1$s!
</string>
<string
name=
"msg_log_out"
>
Logging out...
</string>
<!-- TODO Add proper translation-->
<string
name=
"msg_log_out"
>
Logging out…
</string>
</resources>
\ No newline at end of file
app/src/main/res/values-pt-rBR/strings.xml
View file @
212b870f
...
...
@@ -155,7 +155,6 @@
// TODO:Add proper translation.
<string
name=
"message_credentials_saved_successfully"
>
Credentials saved successfully
</string>
<!-- Message actions -->
<string
name=
"action_msg_reply"
>
Responder
</string>
<string
name=
"action_msg_edit"
>
Editar
</string>
...
...
@@ -176,6 +175,9 @@
<string
name=
"permission_pinning_not_allowed"
>
Pinagem não permitida
</string>
<string
name=
"permission_starring_not_allowed"
>
Favoritar não permitido
</string>
<!-- Search message -->
<string
name=
"title_search_message"
>
Procurar mensagem
</string>
<!-- Favorite/Unfavorite chat room -->
<string
name=
"title_favorite_chat"
>
Favoritar canal
</string>
<string
name=
"title_unfavorite_chat"
>
Desfavoritar canal
</string>
...
...
@@ -244,6 +246,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
Nenhum emoji recente
</string>
<string
name=
"alert_title_default_skin_tone"
>
Tom de pele padrão
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
Ordenar
</string>
...
...
@@ -265,5 +268,5 @@
<string
name=
"notif_action_reply_hint"
>
RESPONDER
</string>
<string
name=
"notif_error_sending"
>
Falha ao enviar a mensagem.
</string>
<string
name=
"notif_success_sending"
>
Mensagem enviada para %1$s!
</string>
<string
name=
"msg_log_out"
>
Deslogando
...
</string>
<string
name=
"msg_log_out"
>
Deslogando
…
</string>
</resources>
app/src/main/res/values-uk-rRU/strings.xml
View file @
212b870f
...
...
@@ -171,6 +171,9 @@
<string
name=
"permission_pinning_not_allowed"
>
Прикрепление запрещено
</string>
<string
name=
"permission_starring_not_allowed"
>
Отмечивание запрещено
</string>
<!-- Search message -->
<string
name=
"title_search_message"
>
Поиск сообщения
</string>
<!-- Favorite/Unfavorite chat room -->
<string
name=
"title_favorite_chat"
>
Добавить чат в избранное
</string>
<string
name=
"title_unfavorite_chat"
>
Удалить чат из избранного
</string>
...
...
@@ -239,6 +242,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
Нет недавно используемых emoji
</string>
<string
name=
"alert_title_default_skin_tone"
>
По умолчанию тон кожи
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
Сортировать
</string>
...
...
@@ -260,5 +264,5 @@
<string
name=
"notif_action_reply_hint"
>
ОТВЕТИТЬ
</string>
<string
name=
"notif_error_sending"
>
Ошибка ответа. Пожалуйста, попробуйте еще раз.
</string>
<string
name=
"notif_success_sending"
>
Сообщение отправлено %1$s!
</string>
<string
name=
"msg_log_out"
>
Logging out...
</string>
<string
name=
"msg_log_out"
>
Выход…
</string>
</resources>
app/src/main/res/values/strings.xml
View file @
212b870f
...
...
@@ -175,6 +175,9 @@
<string
name=
"permission_pinning_not_allowed"
>
Pinning is not allowed
</string>
<string
name=
"permission_starring_not_allowed"
>
Starring is not allowed
</string>
<!-- Search message -->
<string
name=
"title_search_message"
>
Search message
</string>
<!-- Favorite/Unfavorite chat room -->
<string
name=
"title_favorite_chat"
>
Favorite chat
</string>
<string
name=
"title_unfavorite_chat"
>
Unfavorite chat
</string>
...
...
@@ -243,6 +246,7 @@
<!-- Emoji message-->
<string
name=
"msg_no_recent_emoji"
>
No recent emojis
</string>
<string
name=
"alert_title_default_skin_tone"
>
Default skin tone
</string>
<!-- Sorting and grouping-->
<string
name=
"menu_chatroom_sort"
>
Sort
</string>
...
...
app/src/main/res/values/styles.xml
View file @
212b870f
...
...
@@ -9,10 +9,8 @@
<item
name=
"android:statusBarColor"
>
@color/colorPrimaryDark
</item>
<item
name=
"windowActionModeOverlay"
>
true
</item>
</style>
<style
name=
"ToolbarTheme"
parent=
"Base.Widget.AppCompat.Toolbar"
>
<item
name=
"android:colorAccent"
>
@color/colorLightTheme
</item>
<item
name=
"searchViewStyle"
>
@style/ChatRoom.SearchView
</item>
</style>
<style
name=
"AuthenticationTheme"
parent=
"Theme.AppCompat.NoActionBar"
>
...
...
@@ -75,6 +73,13 @@
<item
name=
"android:paddingStart"
>
@dimen/edit_text_margin
</item>
</style>
<style
name=
"ChatRoom.SearchView"
parent=
"Widget.AppCompat.SearchView"
>
<item
name=
"queryHint"
>
@string/title_search_message
</item>
<item
name=
"searchIcon"
>
@drawable/ic_search_white_24dp
</item>
<item
name=
"searchHintIcon"
>
@drawable/ic_search_white_24dp
</item>
<item
name=
"closeIcon"
>
@drawable/ic_close_white_24dp
</item>
</style>
<style
name=
"ChatRooms.Header"
parent=
"TextAppearance.AppCompat.Headline"
>
<item
name=
"android:textSize"
>
16sp
</item>
</style>
...
...
dependencies.gradle
View file @
212b870f
ext
{
versions
=
[
// For project configuration
java
:
JavaVersion
.
VERSION_1_8
,
compileSdk
:
28
,
targetSdk
:
28
,
minSdk
:
21
,
buildTools
:
'28.0.0-rc2'
,
dokka
:
'0.9.16'
,
// For app
kotlin
:
'1.2.51'
,
coroutine
:
'0.23.1'
,
dokka
:
'0.9.16'
,
// Main dependencies
appCompat
:
'1.0.0-alpha1'
,
recyclerview
:
'1.0.0-alpha1'
,
material
:
'1.0.0-alpha1'
,
cardview
:
'1.0.0-alpha1'
,
browser
:
'1.0.0-alpha1'
,
constraintLayout
:
'1.1.0'
,
androidKtx
:
'1.0.0-alpha1'
,
appCompat
:
'1.0.0-beta01'
,
recyclerview
:
'1.0.0-beta01'
,
constraintLayout
:
'2.0.0-alpha1'
,
cardview
:
'1.0.0-beta01'
,
browser
:
'1.0.0-beta01'
,
androidKtx
:
'1.0.0-beta01'
,
dagger
:
'2.16'
,
exoPlayer
:
'2.6.0'
,
playServices
:
'15.0.0'
,
firebase
:
'15.0.0'
,
playServices
:
'15.0.0'
,
exoPlayer
:
'2.6.0'
,
flexbox
:
'0.3.2'
,
material
:
'1.0.0-alpha1'
,
room
:
'2.0.0-alpha1'
,
lifecycle
:
'2.0.0-beta01'
,
rxKotlin
:
'2.2.0'
,
rxAndroid
:
'2.0.2'
,
moshi
:
'1.6.0'
,
okhttp
:
'3.10.0'
,
timber
:
'4.7.0'
,
threeTenABP
:
'1.0.5'
,
rxBinding
:
'2.0.0'
,
fresco
:
'1.9.0'
,
kotshi
:
'1.0.2'
,
frescoImageViewer
:
'0.5.1'
,
markwon
:
'1.0.
3
'
,
markwon
:
'1.0.
6
'
,
aVLoadingIndicatorView:
'2.1.3'
,
flexbox
:
'0.3.2'
,
// For wearable
wear
:
'2.3.0'
,
playServicesWearable
:
'15.0.1'
,
supportWearable
:
'26.1.0'
,
// For testing
junit
:
'4.12'
,
truth
:
'0.36'
,
espresso
:
'3.1.0-alpha2'
,
mockito
:
'2.10.0'
,
//For wearable
wear
:
'2.3.0'
,
playServicesWearable
:
'15.0.1'
,
supportWearable
:
'26.1.0'
mockito
:
'2.10.0'
]
libraries
=
[
kotlin
:
"org.jetbrains.kotlin:kotlin-stdlib-jre8:${versions.kotlin}"
,
...
...
@@ -55,12 +65,9 @@ ext {
appCompat
:
"androidx.appcompat:appcompat:${versions.appCompat}"
,
recyclerview
:
"androidx.recyclerview:recyclerview:${versions.recyclerview}"
,
material
:
"com.google.android.material:material:${versions.material}"
,
constraintlayout
:
"androidx.constraintlayout:constraintlayout:${versions.constraintLayout}"
,
cardview
:
"androidx.cardview:cardview:${versions.cardview}"
,
flexbox
:
"com.google.android:flexbox:${versions.flexbox}"
,
browser
:
"androidx.browser:browser:${versions.browser}"
,
androidKtx
:
"androidx.core:core-ktx:${versions.androidKtx}"
,
dagger
:
"com.google.dagger:dagger:${versions.dagger}"
,
...
...
@@ -70,6 +77,8 @@ ext {
fcm
:
"com.google.firebase:firebase-messaging:${versions.firebase}"
,
playServicesAuth
:
"com.google.android.gms:play-services-auth:${versions.playServices}"
,
exoPlayer
:
"com.google.android.exoplayer:exoplayer:${versions.exoPlayer}"
,
flexbox
:
"com.google.android:flexbox:${versions.flexbox}"
,
material
:
"com.google.android.material:material:${versions.material}"
,
room
:
"androidx.room:room-runtime:${versions.room}"
,
roomProcessor
:
"androidx.room:room-compiler:${versions.room}"
,
...
...
@@ -103,7 +112,7 @@ ext {
aVLoadingIndicatorView:
"com.wang.avi:library:${versions.aVLoadingIndicatorView}"
,
//
For the wear app
//
For wearable
wearable
:
"com.google.android.support:wearable:${versions.wear}"
,
playServicesWearable
:
"com.google.android.gms:play-services-wearable:${versions.playServicesWearable}"
,
percentLayout
:
"com.android.support:percent:${versions.supportWearable}"
,
...
...
emoji/build.gradle
View file @
212b870f
...
...
@@ -29,6 +29,8 @@ dependencies {
implementation
libraries
.
androidKtx
implementation
libraries
.
appCompat
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutinesAndroid
implementation
libraries
.
constraintlayout
implementation
libraries
.
recyclerview
implementation
libraries
.
material
...
...
emoji/src/main/java/chat/rocket/android/emoji/ComposerEditText.kt
View file @
212b870f
...
...
@@ -22,7 +22,7 @@ class ComposerEditText : AppCompatEditText {
override
fun
dispatchKeyEventPreIme
(
event
:
KeyEvent
):
Boolean
{
if
(
event
.
keyCode
==
KeyEvent
.
KEYCODE_BACK
)
{
val
state
=
getKeyDispatcherState
()
val
state
=
keyDispatcherState
if
(
state
!=
null
)
{
if
(
event
.
action
==
KeyEvent
.
ACTION_DOWN
)
{
state
.
startTracking
(
event
,
this
)
...
...
emoji/src/main/java/chat/rocket/android/emoji/Emoji.kt
View file @
212b870f
...
...
@@ -6,5 +6,7 @@ data class Emoji(
val
unicode
:
String
,
val
keywords
:
List
<
String
>,
val
category
:
String
,
val
count
:
Int
=
0
val
count
:
Int
=
0
,
val
siblings
:
MutableCollection
<
Emoji
>
=
mutableListOf
(),
val
fitzpatrick
:
Fitzpatrick
=
Fitzpatrick
.
Default
)
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
View file @
212b870f
package
chat.rocket.android.emoji
import
android.annotation.SuppressLint
import
android.content.Context
import
android.text.Editable
import
android.text.TextWatcher
...
...
@@ -9,23 +10,32 @@ import android.view.View
import
android.view.ViewGroup
import
android.widget.EditText
import
android.widget.ImageView
import
android.widget.TextView
import
androidx.annotation.ColorInt
import
androidx.appcompat.app.AlertDialog
import
androidx.appcompat.app.AppCompatActivity
import
androidx.core.content.ContextCompat
import
androidx.core.content.edit
import
androidx.core.graphics.drawable.DrawableCompat
import
androidx.viewpager.widget.ViewPager
import
chat.rocket.android.emoji.internal.EmojiCategory
import
chat.rocket.android.emoji.internal.EmojiPagerAdapter
import
chat.rocket.android.emoji.internal.PREF_EMOJI_SKIN_TONE
import
com.google.android.material.tabs.TabLayout
class
EmojiKeyboardPopup
(
context
:
Context
,
view
:
View
)
:
OverKeyboardPopupWindow
(
context
,
view
)
{
class
EmojiKeyboardPopup
(
context
:
Context
,
view
:
View
)
:
OverKeyboardPopupWindow
(
context
,
view
)
{
private
lateinit
var
viewPager
:
ViewPager
private
lateinit
var
tabLayout
:
TabLayout
private
lateinit
var
searchView
:
View
private
lateinit
var
backspaceView
:
View
private
lateinit
var
parentContainer
:
ViewGroup
private
lateinit
var
changeColorView
:
View
private
lateinit
var
adapter
:
EmojiPagerAdapter
var
listener
:
EmojiKeyboardListener
?
=
null
@SuppressLint
(
"InflateParams"
)
override
fun
onCreateView
(
inflater
:
LayoutInflater
):
View
{
val
view
=
inflater
.
inflate
(
R
.
layout
.
emoji_keyboard
,
null
)
parentContainer
=
view
.
findViewById
(
R
.
id
.
emoji_keyboard_container
)
...
...
@@ -33,6 +43,7 @@ class EmojiKeyboardPopup(
searchView
=
view
.
findViewById
(
R
.
id
.
emoji_search
)
backspaceView
=
view
.
findViewById
(
R
.
id
.
emoji_backspace
)
tabLayout
=
view
.
findViewById
(
R
.
id
.
tabs
)
changeColorView
=
view
.
findViewById
(
R
.
id
.
color_change_view
)
tabLayout
.
setupWithViewPager
(
viewPager
)
return
view
}
...
...
@@ -44,11 +55,97 @@ class EmojiKeyboardPopup(
private
fun
setupBottomBar
()
{
searchView
.
setOnClickListener
{
//TODO: search not yet implemented
}
backspaceView
.
setOnClickListener
{
listener
?.
onNonEmojiKeyPressed
(
KeyEvent
.
KEYCODE_BACK
)
}
changeColorView
.
setOnClickListener
{
showSkinToneChooser
()
}
val
sharedPreferences
=
context
.
getSharedPreferences
(
"emoji"
,
Context
.
MODE_PRIVATE
)
sharedPreferences
.
getString
(
PREF_EMOJI_SKIN_TONE
,
""
)
?.
let
{
changeSkinTone
(
Fitzpatrick
.
valueOf
(
it
))
}
}
private
fun
showSkinToneChooser
()
{
val
view
=
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
color_select_popup
,
null
)
val
dialog
=
AlertDialog
.
Builder
(
context
,
R
.
style
.
Dialog
)
.
setView
(
view
)
.
setTitle
(
context
.
getString
(
R
.
string
.
alert_title_default_skin_tone
))
.
setCancelable
(
true
)
.
create
()
view
.
findViewById
<
TextView
>(
R
.
id
.
default_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
Default
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
light_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
LightTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_light_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumLightTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_dark_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumDarkTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
dark_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
it
.
text
)
}.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
DarkTone
)
}
dialog
.
show
()
}
private
fun
changeSkinTone
(
tone
:
Fitzpatrick
)
{
val
drawable
=
ContextCompat
.
getDrawable
(
context
,
R
.
drawable
.
color_change_circle
)
!!
val
wrappedDrawable
=
DrawableCompat
.
wrap
(
drawable
)
DrawableCompat
.
setTint
(
wrappedDrawable
,
getFitzpatrickColor
(
tone
))
(
changeColorView
as
ImageView
).
setImageDrawable
(
wrappedDrawable
)
adapter
.
setFitzpatrick
(
tone
)
}
@ColorInt
private
fun
getFitzpatrickColor
(
tone
:
Fitzpatrick
):
Int
{
val
sharedPreferences
=
context
.
getSharedPreferences
(
"emoji"
,
Context
.
MODE_PRIVATE
)
sharedPreferences
.
edit
{
putString
(
PREF_EMOJI_SKIN_TONE
,
tone
.
type
)
}
return
when
(
tone
)
{
Fitzpatrick
.
Default
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_default
)
Fitzpatrick
.
LightTone
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_light
)
Fitzpatrick
.
MediumLightTone
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_medium_light
)
Fitzpatrick
.
MediumTone
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_medium
)
Fitzpatrick
.
MediumDarkTone
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_medium_dark
)
Fitzpatrick
.
DarkTone
->
ContextCompat
.
getColor
(
context
,
R
.
color
.
tone_dark
)
}
}
private
fun
setupViewPager
()
{
...
...
@@ -64,12 +161,14 @@ class EmojiKeyboardPopup(
}
}
viewPager
.
adapter
=
Category
PagerAdapter
(
object
:
EmojiKeyboardListener
{
adapter
=
Emoji
PagerAdapter
(
object
:
EmojiKeyboardListener
{
override
fun
onEmojiAdded
(
emoji
:
Emoji
)
{
EmojiRepository
.
addToRecents
(
emoji
)
callback
.
onEmojiAdded
(
emoji
)
}
})
viewPager
.
offscreenPageLimit
=
EmojiCategory
.
values
().
size
viewPager
.
adapter
=
adapter
for
(
category
in
EmojiCategory
.
values
())
{
val
tab
=
tabLayout
.
getTabAt
(
category
.
ordinal
)
...
...
@@ -79,14 +178,18 @@ class EmojiKeyboardPopup(
textView
.
setImageResource
(
category
.
resourceIcon
())
}
val
currentTab
=
if
(
EmojiRepository
.
getRecents
().
isEmpty
())
EmojiCategory
.
PEOPLE
.
ordinal
else
val
currentTab
=
if
(
EmojiRepository
.
getRecents
().
isEmpty
())
{
EmojiCategory
.
PEOPLE
.
ordinal
}
else
{
EmojiCategory
.
RECENTS
.
ordinal
}
viewPager
.
currentItem
=
currentTab
}
}
class
EmojiTextWatcher
(
private
val
editor
:
EditText
)
:
TextWatcher
{
@Volatile
private
var
emojiToRemove
=
mutableListOf
<
EmojiTypefaceSpan
>()
@Volatile
private
var
emojiToRemove
=
mutableListOf
<
EmojiTypefaceSpan
>()
override
fun
afterTextChanged
(
s
:
Editable
)
{
val
message
=
editor
.
editableText
...
...
@@ -128,8 +231,4 @@ class EmojiKeyboardPopup(
override
fun
onTextChanged
(
s
:
CharSequence
,
start
:
Int
,
before
:
Int
,
count
:
Int
)
{
}
}
companion
object
{
const
val
PREF_EMOJI_RECENTS
=
"PREF_EMOJI_RECENTS"
}
}
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/EmojiParser.kt
View file @
212b870f
package
chat.rocket.android.emoji
import
android.text.Spannable
import
android.text.SpannableString
import
android.text.Spanned
...
...
@@ -11,11 +12,12 @@ class EmojiParser {
* Spannable.
*
* @param text The text to parse
* @param factory Optional. A [Spannable.Factory] instance to reuse when creating [Spannable].
* @return A rendered Spannable containing any supported emoji.
*/
fun
parse
(
text
:
CharSequence
):
CharSequence
{
fun
parse
(
text
:
CharSequence
,
factory
:
Spannable
.
Factory
?
=
null
):
CharSequence
{
val
unicodedText
=
EmojiRepository
.
shortnameToUnicode
(
text
,
true
)
val
spannable
=
SpannableString
.
valueOf
(
unicodedText
)
val
spannable
=
factory
?.
newSpannable
(
unicodedText
)
?:
SpannableString
.
valueOf
(
unicodedText
)
val
typeface
=
EmojiRepository
.
cachedTypeface
// Look for groups of emojis, set a EmojiTypefaceSpan with the emojione font.
val
length
=
spannable
.
length
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiPickerPopup.kt
View file @
212b870f
...
...
@@ -3,18 +3,23 @@ package chat.rocket.android.emoji
import
android.app.Dialog
import
android.content.Context
import
android.os.Bundle
import
com.google.android.material.tabs.TabLayout
import
androidx.viewpager.widget.ViewPager
import
android.view.LayoutInflater
import
android.view.Window
import
android.view.WindowManager
import
android.widget.ImageView
import
androidx.annotation.ColorInt
import
androidx.core.content.ContextCompat
import
androidx.core.content.edit
import
chat.rocket.android.emoji.internal.EmojiCategory
import
chat.rocket.android.emoji.internal.EmojiPagerAdapter
import
chat.rocket.android.emoji.internal.PREF_EMOJI_SKIN_TONE
import
kotlinx.android.synthetic.main.emoji_picker.*
class
EmojiPickerPopup
(
context
:
Context
)
:
Dialog
(
context
)
{
var
listener
:
EmojiKeyboardListener
?
=
null
private
lateinit
var
adapter
:
EmojiPagerAdapter
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
...
...
@@ -35,7 +40,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
}
private
fun
setupViewPager
()
{
pager_categories
.
adapter
=
Category
PagerAdapter
(
object
:
EmojiKeyboardListener
{
adapter
=
Emoji
PagerAdapter
(
object
:
EmojiKeyboardListener
{
override
fun
onEmojiAdded
(
emoji
:
Emoji
)
{
EmojiRepository
.
addToRecents
(
emoji
)
dismiss
()
...
...
@@ -43,6 +48,14 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
}
})
val
sharedPreferences
=
context
.
getSharedPreferences
(
"emoji"
,
Context
.
MODE_PRIVATE
)
sharedPreferences
.
getString
(
PREF_EMOJI_SKIN_TONE
,
""
)
?.
let
{
changeSkinTone
(
Fitzpatrick
.
valueOf
(
it
))
}
pager_categories
.
adapter
=
adapter
pager_categories
.
offscreenPageLimit
=
EmojiCategory
.
values
().
size
for
(
category
in
EmojiCategory
.
values
())
{
val
tab
=
tabs
.
getTabAt
(
category
.
ordinal
)
val
tabView
=
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
emoji_picker_tab
,
null
)
...
...
@@ -51,8 +64,15 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
textView
.
setImageResource
(
category
.
resourceIcon
())
}
val
currentTab
=
if
(
EmojiRepository
.
getRecents
().
isEmpty
())
EmojiCategory
.
PEOPLE
.
ordinal
else
val
currentTab
=
if
(
EmojiRepository
.
getRecents
().
isEmpty
())
{
EmojiCategory
.
PEOPLE
.
ordinal
}
else
{
EmojiCategory
.
RECENTS
.
ordinal
}
pager_categories
.
currentItem
=
currentTab
}
private
fun
changeSkinTone
(
tone
:
Fitzpatrick
)
{
adapter
.
setFitzpatrick
(
tone
)
}
}
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/EmojiRepository.kt
View file @
212b870f
...
...
@@ -3,6 +3,12 @@ package chat.rocket.android.emoji
import
android.content.Context
import
android.content.SharedPreferences
import
android.graphics.Typeface
import
android.os.SystemClock
import
chat.rocket.android.emoji.internal.EmojiCategory
import
chat.rocket.android.emoji.internal.PREF_EMOJI_RECENTS
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.experimental.yield
import
org.json.JSONArray
import
org.json.JSONObject
import
java.io.BufferedReader
...
...
@@ -10,8 +16,11 @@ import java.io.InputStream
import
java.io.InputStreamReader
import
java.util.*
import
java.util.regex.Pattern
import
kotlin.coroutines.experimental.buildSequence
object
EmojiRepository
{
private
val
FITZPATRICK_REGEX
=
"(.*)_(tone[0-9]):"
.
toRegex
(
RegexOption
.
IGNORE_CASE
)
private
val
shortNameToUnicode
=
HashMap
<
String
,
String
>()
private
val
SHORTNAME_PATTERN
=
Pattern
.
compile
(
":([-+\\w]+):"
)
private
val
ALL_EMOJIS
=
mutableListOf
<
Emoji
>()
...
...
@@ -24,9 +33,9 @@ object EmojiRepository {
cachedTypeface
=
Typeface
.
createFromAsset
(
context
.
assets
,
"fonts/emojione-android.ttf"
)
val
stream
=
context
.
assets
.
open
(
path
)
val
emojis
=
loadEmojis
(
stream
)
emojis
.
forEach
{
emojis
.
forEach
{
emoji
->
val
unicodeIntList
=
mutableListOf
<
Int
>()
it
.
unicode
.
split
(
"-"
).
forEach
{
emoji
.
unicode
.
split
(
"-"
).
forEach
{
val
value
=
it
.
toInt
(
16
)
if
(
value
>=
0
x10000
)
{
val
surrogatePair
=
calculateSurrogatePairs
(
value
)
...
...
@@ -38,20 +47,40 @@ object EmojiRepository {
}
val
unicodeIntArray
=
unicodeIntList
.
toIntArray
()
val
unicode
=
String
(
unicodeIntArray
,
0
,
unicodeIntArray
.
size
)
ALL_EMOJIS
.
add
(
it
.
copy
(
unicode
=
unicode
))
val
emojiWithUnicode
=
emoji
.
copy
(
unicode
=
unicode
)
if
(
hasFitzpatrick
(
emoji
.
shortname
))
{
val
matchResult
=
FITZPATRICK_REGEX
.
find
(
emoji
.
shortname
)
val
prefix
=
matchResult
!!
.
groupValues
[
1
]
+
":"
val
fitzpatrick
=
Fitzpatrick
.
valueOf
(
matchResult
.
groupValues
[
2
])
val
defaultEmoji
=
ALL_EMOJIS
.
firstOrNull
{
it
.
shortname
==
prefix
}
val
emojiWithFitzpatrick
=
emojiWithUnicode
.
copy
(
fitzpatrick
=
fitzpatrick
)
if
(
defaultEmoji
!=
null
)
{
defaultEmoji
.
siblings
.
add
(
emojiWithFitzpatrick
)
}
else
{
// This emoji doesn't have a default tone, ie. :man_in_business_suit_levitating_tone1:
// In this case, the default emoji becomes the first toned one.
ALL_EMOJIS
.
add
(
emojiWithFitzpatrick
)
}
}
else
{
ALL_EMOJIS
.
add
(
emojiWithUnicode
)
}
shortNameToUnicode
.
apply
{
put
(
it
.
shortname
,
unicode
)
it
.
shortnameAlternates
.
forEach
{
alternate
->
put
(
alternate
,
unicode
)
}
put
(
emoji
.
shortname
,
unicode
)
emoji
.
shortnameAlternates
.
forEach
{
alternate
->
put
(
alternate
,
unicode
)
}
}
}
}
private
fun
hasFitzpatrick
(
shortname
:
String
):
Boolean
{
return
FITZPATRICK_REGEX
matches
shortname
}
/**
* Get all loaded emojis as list of Emoji objects.
*
* @return All emojis for all categories.
*/
fun
getAll
()
=
ALL_EMOJIS
internal
fun
getAll
()
=
ALL_EMOJIS
/**
* Get all emojis for a given category.
...
...
@@ -60,10 +89,19 @@ object EmojiRepository {
*
* @return All emoji from specified category
*/
fun
getEmojisByCategory
(
category
:
EmojiCategory
):
List
<
Emoji
>
{
internal
fun
getEmojisByCategory
(
category
:
EmojiCategory
):
List
<
Emoji
>
{
return
ALL_EMOJIS
.
filter
{
it
.
category
.
toLowerCase
()
==
category
.
name
.
toLowerCase
()
}
}
internal
fun
getEmojiSequenceByCategory
(
category
:
EmojiCategory
):
Sequence
<
Emoji
>
{
val
list
=
ALL_EMOJIS
.
filter
{
it
.
category
.
toLowerCase
()
==
category
.
name
.
toLowerCase
()
}
return
buildSequence
{
list
.
forEach
{
yield
(
it
)
}
}
}
/**
* Get the emoji given by a specified shortname. Returns null if can't find any.
*
...
...
@@ -71,21 +109,21 @@ object EmojiRepository {
*
* @return Emoji given by shortname or null
*/
fun
getEmojiByShortname
(
shortname
:
String
)
=
ALL_EMOJIS
.
firstOrNull
{
it
.
shortname
==
shortname
}
internal
fun
getEmojiByShortname
(
shortname
:
String
)
=
ALL_EMOJIS
.
firstOrNull
{
it
.
shortname
==
shortname
}
/**
* Add an emoji to the Recents category.
*/
fun
addToRecents
(
emoji
:
Emoji
)
{
internal
fun
addToRecents
(
emoji
:
Emoji
)
{
val
emojiShortname
=
emoji
.
shortname
val
recentsJson
=
JSONObject
(
preferences
.
getString
(
EmojiKeyboardPopup
.
PREF_EMOJI_RECENTS
,
"{}"
))
val
recentsJson
=
JSONObject
(
preferences
.
getString
(
PREF_EMOJI_RECENTS
,
"{}"
))
if
(
recentsJson
.
has
(
emojiShortname
))
{
val
useCount
=
recentsJson
.
getInt
(
emojiShortname
)
recentsJson
.
put
(
emojiShortname
,
useCount
+
1
)
}
else
{
recentsJson
.
put
(
emojiShortname
,
1
)
}
preferences
.
edit
().
putString
(
EmojiKeyboardPopup
.
PREF_EMOJI_RECENTS
,
recentsJson
.
toString
()).
apply
()
preferences
.
edit
().
putString
(
PREF_EMOJI_RECENTS
,
recentsJson
.
toString
()).
apply
()
}
/**
...
...
@@ -93,9 +131,9 @@ object EmojiRepository {
*
* @return All recent emojis ordered by usage.
*/
fun
getRecents
():
List
<
Emoji
>
{
internal
fun
getRecents
():
List
<
Emoji
>
{
val
list
=
mutableListOf
<
Emoji
>()
val
recentsJson
=
JSONObject
(
preferences
.
getString
(
EmojiKeyboardPopup
.
PREF_EMOJI_RECENTS
,
"{}"
))
val
recentsJson
=
JSONObject
(
preferences
.
getString
(
PREF_EMOJI_RECENTS
,
"{}"
))
for
(
shortname
in
recentsJson
.
keys
())
{
val
emoji
=
getEmojiByShortname
(
shortname
)
emoji
?.
let
{
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiTypefaceSpan.kt
View file @
212b870f
...
...
@@ -16,23 +16,6 @@ class EmojiTypefaceSpan(family: String, private val newType: Typeface) : Typefac
}
private
fun
applyCustomTypeFace
(
paint
:
Paint
,
tf
:
Typeface
)
{
val
oldStyle
:
Int
val
old
=
paint
.
typeface
if
(
old
==
null
)
{
oldStyle
=
0
}
else
{
oldStyle
=
old
.
style
}
val
fake
=
oldStyle
and
tf
.
style
.
inv
()
if
(
fake
and
Typeface
.
BOLD
!=
0
)
{
paint
.
isFakeBoldText
=
true
}
if
(
fake
and
Typeface
.
ITALIC
!=
0
)
{
paint
.
textSkewX
=
-
0.25f
}
paint
.
typeface
=
tf
}
}
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/Fitzpatrick.kt
0 → 100644
View file @
212b870f
package
chat.rocket.android.emoji
/**
* Taken the Fitzpatrick scale as reference and adapted to be used with emojione.
*
* @see <a href="https://en.wikipedia.org/wiki/Fitzpatrick_scale">https://en.wikipedia.org/wiki/Fitzpatrick_scale</a>
*/
sealed
class
Fitzpatrick
(
val
type
:
String
)
{
object
Default
:
Fitzpatrick
(
""
)
object
LightTone
:
Fitzpatrick
(
"tone1"
)
object
MediumLightTone
:
Fitzpatrick
(
"tone2"
)
object
MediumTone
:
Fitzpatrick
(
"tone3"
)
object
MediumDarkTone
:
Fitzpatrick
(
"tone4"
)
object
DarkTone
:
Fitzpatrick
(
"tone5"
)
companion
object
{
fun
valueOf
(
type
:
String
):
Fitzpatrick
{
return
when
(
type
)
{
""
->
Default
"tone1"
->
LightTone
"tone2"
->
MediumLightTone
"tone3"
->
MediumTone
"tone4"
->
MediumDarkTone
"tone5"
->
DarkTone
else
->
throw
IllegalArgumentException
(
"Fitzpatrick type '$type' is invalid"
)
}
}
}
}
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/EmojiCategory.kt
→
emoji/src/main/java/chat/rocket/android/emoji/
internal/
EmojiCategory.kt
View file @
212b870f
package
chat.rocket.android.emoji
package
chat.rocket.android.emoji
.internal
import
android.text.SpannableString
import
android.text.Spanned
import
androidx.annotation.DrawableRes
import
chat.rocket.android.emoji.EmojiRepository
import
chat.rocket.android.emoji.EmojiTypefaceSpan
import
chat.rocket.android.emoji.R
enum
class
EmojiCategory
{
internal
enum
class
EmojiCategory
{
RECENTS
{
override
fun
resourceIcon
()
=
R
.
drawable
.
ic_emoji_recents
...
...
emoji/src/main/java/chat/rocket/android/emoji/
Category
PagerAdapter.kt
→
emoji/src/main/java/chat/rocket/android/emoji/
internal/Emoji
PagerAdapter.kt
View file @
212b870f
package
chat.rocket.android.emoji
package
chat.rocket.android.emoji
.internal
import
android.text.Spannable
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
import
android.widget.TextView
import
androidx.core.view.isVisible
import
androidx.recyclerview.widget.DefaultItemAnimator
import
androidx.recyclerview.widget.GridLayoutManager
import
androidx.recyclerview.widget.RecyclerView
import
androidx.viewpager.widget.PagerAdapter
import
chat.rocket.android.emoji.Emoji
import
chat.rocket.android.emoji.EmojiKeyboardListener
import
chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiRepository
import
chat.rocket.android.emoji.Fitzpatrick
import
chat.rocket.android.emoji.R
import
kotlinx.android.synthetic.main.emoji_category_layout.view.*
import
java.util.*
import
kotlinx.android.synthetic.main.emoji_row_item.view.*
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
internal
class
CategoryPagerAdapter
(
private
val
listener
:
EmojiKeyboardListener
)
:
PagerAdapter
()
{
internal
class
EmojiPagerAdapter
(
private
val
listener
:
EmojiKeyboardListener
)
:
PagerAdapter
()
{
private
val
adapters
=
hashMapOf
<
EmojiCategory
,
EmojiAdapter
>()
private
var
fitzpatrick
:
Fitzpatrick
=
Fitzpatrick
.
Default
override
fun
isViewFromObject
(
view
:
View
,
obj
:
Any
):
Boolean
{
return
view
==
obj
...
...
@@ -23,21 +36,28 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
.
inflate
(
R
.
layout
.
emoji_category_layout
,
container
,
false
)
with
(
view
)
{
val
layoutManager
=
GridLayoutManager
(
context
,
8
)
val
adapter
=
EmojiAdapter
(
layoutManager
.
spanCount
,
listener
)
val
category
=
EmojiCategory
.
values
()[
position
]
val
emojis
=
if
(
category
!=
EmojiCategory
.
RECENTS
)
{
EmojiRepository
.
getEmojisByCategory
(
category
)
}
else
{
EmojiRepository
.
getRecents
()
}
val
recentEmojiSize
=
EmojiRepository
.
getRecents
().
size
text_no_recent_emoji
.
isVisible
=
category
==
EmojiCategory
.
RECENTS
&&
recentEmojiSize
==
0
adapter
.
addEmojis
(
emojis
)
emoji_recycler_view
.
layoutManager
=
layoutManager
emoji_recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
emoji_recycler_view
.
adapter
=
adapter
emoji_recycler_view
.
isNestedScrollingEnabled
=
false
emoji_recycler_view
.
setRecycledViewPool
(
RecyclerView
.
RecycledViewPool
())
container
.
addView
(
view
)
launch
(
UI
)
{
val
emojis
=
if
(
category
!=
EmojiCategory
.
RECENTS
)
{
EmojiRepository
.
getEmojiSequenceByCategory
(
category
)
}
else
{
sequenceOf
(*
EmojiRepository
.
getRecents
().
toTypedArray
())
}
val
recentEmojiSize
=
EmojiRepository
.
getRecents
().
size
text_no_recent_emoji
.
isVisible
=
category
==
EmojiCategory
.
RECENTS
&&
recentEmojiSize
==
0
if
(
adapters
[
category
]
==
null
)
{
val
adapter
=
EmojiAdapter
(
listener
=
listener
)
emoji_recycler_view
.
adapter
=
adapter
adapters
[
category
]
=
adapter
adapter
.
addEmojisFromSequence
(
emojis
)
}
adapters
[
category
]
!!
.
setFitzpatrick
(
fitzpatrick
)
}
}
return
view
}
...
...
@@ -50,25 +70,58 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
override
fun
getPageTitle
(
position
:
Int
)
=
EmojiCategory
.
values
()[
position
].
textIcon
()
override
fun
getItemPosition
(
`object`
:
Any
):
Int
{
return
POSITION_NONE
}
fun
setFitzpatrick
(
fitzpatrick
:
Fitzpatrick
)
{
this
.
fitzpatrick
=
fitzpatrick
for
(
entry
in
adapters
.
entries
)
{
if
(
entry
.
key
!=
EmojiCategory
.
RECENTS
)
{
entry
.
value
.
setFitzpatrick
(
fitzpatrick
)
}
}
}
class
EmojiAdapter
(
private
va
l
spanCount
:
In
t
,
private
va
r
fitzpatrick
:
Fitzpatrick
=
Fitzpatrick
.
Defaul
t
,
private
val
listener
:
EmojiKeyboardListener
)
:
RecyclerView
.
Adapter
<
EmojiRowViewHolder
>()
{
private
va
r
emojis
=
Collections
.
emptyList
<
Emoji
>()
private
va
l
emojis
=
mutableListOf
<
Emoji
>()
fun
addEmojis
(
emojis
:
List
<
Emoji
>)
{
this
.
emojis
=
emojis
notifyItemRangeInserted
(
0
,
emojis
.
size
)
this
.
emojis
.
clear
()
this
.
emojis
.
addAll
(
emojis
)
notifyDataSetChanged
()
}
suspend
fun
addEmojisFromSequence
(
emojiSequence
:
Sequence
<
Emoji
>)
{
withContext
(
CommonPool
)
{
emojiSequence
.
forEachIndexed
{
index
,
emoji
->
withContext
(
UI
)
{
emojis
.
add
(
emoji
)
notifyItemInserted
(
index
)
}
}
}
}
fun
setFitzpatrick
(
fitzpatrick
:
Fitzpatrick
)
{
this
.
fitzpatrick
=
fitzpatrick
notifyDataSetChanged
()
}
override
fun
onBindViewHolder
(
holder
:
EmojiRowViewHolder
,
position
:
Int
)
{
holder
.
bind
(
emojis
[
position
])
val
emoji
=
emojis
[
position
]
holder
.
bind
(
emoji
.
siblings
.
find
{
it
.
fitzpatrick
==
fitzpatrick
}
?:
emoji
)
}
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
EmojiRowViewHolder
{
val
view
=
LayoutInflater
.
from
(
parent
.
context
).
inflate
(
R
.
layout
.
emoji_row_item
,
parent
,
false
)
return
EmojiRowViewHolder
(
view
,
itemCount
,
spanCount
,
listener
)
return
EmojiRowViewHolder
(
view
,
listener
)
}
override
fun
getItemCount
():
Int
=
emojis
.
size
...
...
@@ -76,25 +129,30 @@ internal class CategoryPagerAdapter(private val listener: EmojiKeyboardListener)
class
EmojiRowViewHolder
(
itemView
:
View
,
private
val
itemCount
:
Int
,
private
val
spanCount
:
Int
,
private
val
listener
:
EmojiKeyboardListener
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
private
val
emojiView
:
TextView
=
itemView
.
findViewById
(
R
.
id
.
emoji
)
fun
bind
(
emoji
:
Emoji
)
{
val
context
=
itemView
.
context
emojiView
.
text
=
EmojiParser
.
parse
(
emoji
.
unicode
)
val
remainder
=
itemCount
%
spanCount
val
lastLineItemCount
=
if
(
remainder
==
0
)
spanCount
else
remainder
val
paddingBottom
=
context
.
resources
.
getDimensionPixelSize
(
R
.
dimen
.
picker_padding_bottom
)
if
(
adapterPosition
>=
itemCount
-
lastLineItemCount
)
{
itemView
.
setPadding
(
0
,
0
,
0
,
paddingBottom
)
}
itemView
.
setOnClickListener
{
listener
.
onEmojiAdded
(
emoji
)
with
(
itemView
)
{
val
parsedUnicode
=
unicodeCache
[
emoji
.
unicode
]
emoji_view
.
setSpannableFactory
(
spannableFactory
)
emoji_view
.
text
=
if
(
parsedUnicode
==
null
)
{
EmojiParser
.
parse
(
emoji
.
unicode
,
spannableFactory
).
let
{
unicodeCache
[
emoji
.
unicode
]
=
it
it
}
}
else
{
parsedUnicode
}
itemView
.
setOnClickListener
{
listener
.
onEmojiAdded
(
emoji
)
}
}
}
companion
object
{
private
val
spannableFactory
=
Spannable
.
Factory
()
private
val
unicodeCache
=
mutableMapOf
<
CharSequence
,
CharSequence
>()
}
}
}
\ No newline at end of file
emoji/src/main/java/chat/rocket/android/emoji/internal/Preferences.kt
0 → 100644
View file @
212b870f
package
chat.rocket.android.emoji.internal
internal
const
val
PREF_EMOJI_RECENTS
=
"PREF_EMOJI_RECENTS"
internal
const
val
PREF_EMOJI_SKIN_TONE
=
"PREF_EMOJI_SKIN_TONE"
\ No newline at end of file
emoji/src/main/res/drawable/color_change_circle.xml
0 → 100644
View file @
212b870f
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"oval"
>
<solid
android:color=
"@color/tone_default"
/>
<size
android:width=
"24dp"
android:height=
"24dp"
/>
</shape>
\ No newline at end of file
emoji/src/main/res/layout-land/emoji_picker.xml
0 → 100644
View file @
212b870f
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/picker_container"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"@color/colorWhite"
android:orientation=
"vertical"
>
<com.google.android.material.tabs.TabLayout
android:id=
"@+id/tabs"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
app:tabBackground=
"@color/whitesmoke"
app:tabGravity=
"fill"
app:tabMode=
"scrollable"
/>
<androidx.viewpager.widget.ViewPager
android:id=
"@+id/pager_categories"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_marginEnd=
"8dp"
android:layout_marginStart=
"8dp"
android:background=
"@color/colorWhite"
/>
</LinearLayout>
\ No newline at end of file
emoji/src/main/res/layout/color_select_popup.xml
0 → 100644
View file @
212b870f
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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=
"wrap_content"
android:layout_height=
"wrap_content"
>
<TextView
android:id=
"@+id/default_tone_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"16dp"
android:text=
"👌"
android:textSize=
"32sp"
android:tint=
"@color/tone_default"
app:layout_constraintEnd_toStartOf=
"@+id/light_tone_text"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintHorizontal_chainStyle=
"packed"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
/>
<TextView
android:id=
"@+id/light_tone_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"24dp"
android:layout_marginTop=
"16dp"
android:layout_marginEnd=
"24dp"
android:text=
"👌🏻"
android:textSize=
"32sp"
android:tint=
"@color/tone_light"
app:layout_constraintEnd_toStartOf=
"@+id/medium_light_text"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/default_tone_text"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
<TextView
android:id=
"@+id/medium_light_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"16dp"
android:text=
"👌🏼"
android:textSize=
"32sp"
android:tint=
"@color/tone_medium_light"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/light_tone_text"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
<TextView
android:id=
"@+id/medium_tone_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"8dp"
android:layout_marginBottom=
"16dp"
android:text=
"👌🏽"
android:textSize=
"32sp"
android:tint=
"@color/tone_medium"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/medium_dark_tone_text"
app:layout_constraintHorizontal_chainStyle=
"packed"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/default_tone_text"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
<TextView
android:id=
"@+id/medium_dark_tone_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"24dp"
android:layout_marginTop=
"8dp"
android:layout_marginEnd=
"24dp"
android:layout_marginBottom=
"16dp"
android:text=
"👌🏾"
android:textSize=
"32sp"
android:tint=
"@color/tone_medium_dark"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toStartOf=
"@+id/dark_tone_text"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/medium_tone_text"
app:layout_constraintTop_toBottomOf=
"@+id/light_tone_text"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
<TextView
android:id=
"@+id/dark_tone_text"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"8dp"
android:layout_marginBottom=
"16dp"
android:text=
"👌🏿"
android:textSize=
"32sp"
android:tint=
"@color/tone_dark"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/medium_dark_tone_text"
app:layout_constraintTop_toBottomOf=
"@+id/medium_light_text"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
emoji/src/main/res/layout/emoji_keyboard.xml
View file @
212b870f
...
...
@@ -3,7 +3,7 @@
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/emoji_keyboard_container"
android:layout_width=
"match_parent"
android:layout_height=
"
0dp
"
android:layout_height=
"
wrap_content
"
android:background=
"@color/colorWhite"
>
<View
...
...
@@ -26,7 +26,7 @@
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<
Relative
Layout
<
androidx.constraintlayout.widget.Constraint
Layout
android:id=
"@+id/emoji_actions_container"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
...
...
@@ -36,29 +36,40 @@
app:layout_constraintStart_toStartOf=
"parent"
>
<ImageView
android:id=
"@+id/
emoji_search
"
android:id=
"@+id/
color_change_view
"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentStart=
"true"
android:layout_centerVertical=
"true"
android:padding=
"8dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
/>
<ImageView
android:id=
"@+id/emoji_search"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:background=
"@color/whitesmoke"
android:clickable=
"true"
android:focusable=
"true"
android:padding=
"8dp"
android:src=
"@drawable/ic_search_gray_24px"
android:visibility=
"invisible"
/>
android:visibility=
"invisible"
app:layout_constraintEnd_toStartOf=
"@+id/emoji_backspace"
app:layout_constraintStart_toEndOf=
"@+id/color_change_view"
/>
<ImageView
android:id=
"@+id/emoji_backspace"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_alignParentEnd=
"true"
android:layout_centerVertical=
"true"
android:background=
"@color/whitesmoke"
android:clickable=
"true"
android:focusable=
"true"
android:padding=
"8dp"
android:src=
"@drawable/ic_backspace_gray_24dp"
/>
</RelativeLayout>
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/ic_backspace_gray_24dp"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
emoji/src/main/res/layout/emoji_row_item.xml
View file @
212b870f
<?xml version="1.0" encoding="utf-8"?>
<
FrameLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<
TextView
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_gravity=
"center"
android:id=
"@+id/emoji_view"
style=
"@style/TextAppearance.AppCompat.Title"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:foreground=
"?selectableItemBackground"
android:gravity=
"center"
>
<TextView
android:id=
"@+id/emoji"
style=
"@style/TextAppearance.AppCompat.Title"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:gravity=
"center"
android:textColor=
"#000000"
android:textSize=
"26sp"
tools:text=
"😀"
/>
</FrameLayout>
\ No newline at end of file
android:textColor=
"#000000"
android:textSize=
"26sp"
tools:text=
"😀"
/>
emoji/src/main/res/values/colors.xml
View file @
212b870f
...
...
@@ -7,4 +7,12 @@
<color
name=
"colorEmojiIcon"
>
#FF767676
</color>
<color
name=
"colorDividerMessageComposer"
>
#D8D8D8
</color>
<!-- Skin tone colors -->
<color
name=
"tone_default"
>
#fdcb45
</color>
<color
name=
"tone_light"
>
#fcd6b4
</color>
<color
name=
"tone_medium_light"
>
#ecc0a1
</color>
<color
name=
"tone_medium"
>
#ba8b6f
</color>
<color
name=
"tone_medium_dark"
>
#926b4f
</color>
<color
name=
"tone_dark"
>
#614833
</color>
</resources>
emoji/src/main/res/values/strings.xml
View file @
212b870f
<resources>
<string
name=
"msg_no_recent_emoji"
>
No recent emoji
</string>
<string
name=
"alert_title_default_skin_tone"
>
Default skin tone
</string>
</resources>
emoji/src/main/res/values/styles.xml
0 → 100644
View file @
212b870f
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name=
"Dialog"
parent=
"Theme.AppCompat.Light.Dialog.Alert"
>
<item
name=
"android:windowMinWidthMajor"
>
80%
</item>
<item
name=
"android:windowMinWidthMinor"
>
80%
</item>
</style>
</resources>
\ No newline at end of file
util/build.gradle
View file @
212b870f
...
...
@@ -29,6 +29,10 @@ dependencies {
implementation
libraries
.
coroutines
implementation
libraries
.
coroutinesAndroid
implementation
libraries
.
appCompat
implementation
libraries
.
rxBinding
// TODO This is a dependency from the core module, but the util module are unable to get that dependencies. Check why it is occurring since transitive is enable by default
implementation
libraries
.
lifecycleExtensions
kapt
libraries
.
lifecycleCompiler
...
...
app/src/main/java/chat/rocket/android/util/extensions
/Rx.kt
→
util/src/main/java/chat/rocket/android/util/extension
/Rx.kt
View file @
212b870f
package
chat.rocket.android.util.extension
s
package
chat.rocket.android.util.extension
import
android.widget.EditText
import
com.jakewharton.rxbinding2.widget.RxTextView
...
...
util/src/main/java/chat/rocket/android/util/extension/Widget.kt
0 → 100644
View file @
212b870f
package
chat.rocket.android.util.extension
import
androidx.appcompat.widget.SearchView
fun
SearchView
.
onQueryTextListener
(
queryListener
:
(
String
)
->
Unit
)
{
return
this
.
setOnQueryTextListener
(
object
:
SearchView
.
OnQueryTextListener
{
override
fun
onQueryTextSubmit
(
query
:
String
):
Boolean
{
queryListener
(
query
)
return
true
}
override
fun
onQueryTextChange
(
newText
:
String
):
Boolean
{
queryListener
(
newText
)
return
true
}
})
}
\ 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