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
ee16b416
Commit
ee16b416
authored
Nov 26, 2018
by
Filipe de Lima Brito
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' of github.com:RocketChat/Rocket.Chat.Android into new/licence-contactus
parents
1ba86e52
4c9ae11a
Changes
56
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
56 changed files
with
2277 additions
and
379 deletions
+2277
-379
12.json
app/schemas/chat.rocket.android.db.RCDatabase/12.json
+1087
-0
Analytics.kt
app/src/main/java/chat/rocket/android/analytics/Analytics.kt
+8
-1
AnalyticsManager.kt
...in/java/chat/rocket/android/analytics/AnalyticsManager.kt
+6
-0
BaseViewHolder.kt
...va/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
+4
-0
MessageReactionsAdapter.kt
...ocket/android/chatroom/adapter/MessageReactionsAdapter.kt
+8
-1
ChatRoomFragmentModule.kt
...chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
+33
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+113
-76
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+3
-10
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+62
-14
ReactionUiModel.kt
...a/chat/rocket/android/chatroom/uimodel/ReactionUiModel.kt
+3
-2
UiModelMapper.kt
...ava/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt
+5
-2
RoomUiModelMapper.kt
...hat/rocket/android/chatrooms/adapter/RoomUiModelMapper.kt
+15
-6
RoomUiModel.kt
...hat/rocket/android/chatrooms/adapter/model/RoomUiModel.kt
+6
-2
ChatRoomsFragmentModule.kt
...at/rocket/android/chatrooms/di/ChatRoomsFragmentModule.kt
+4
-2
ChatRoomsRepository.kt
...t/android/chatrooms/infrastructure/ChatRoomsRepository.kt
+7
-3
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+24
-2
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+1
-2
ChatRoomsViewModel.kt
.../rocket/android/chatrooms/viewmodel/ChatRoomsViewModel.kt
+2
-2
DatabaseManager.kt
app/src/main/java/chat/rocket/android/db/DatabaseManager.kt
+94
-83
RCDatabase.kt
app/src/main/java/chat/rocket/android/db/RCDatabase.kt
+5
-2
ChatRoomEntity.kt
.../main/java/chat/rocket/android/db/model/ChatRoomEntity.kt
+6
-2
FavoriteMessagesPresenter.kt
...avoritemessages/presentation/FavoriteMessagesPresenter.kt
+2
-1
FilesPresenter.kt
.../chat/rocket/android/files/presentation/FilesPresenter.kt
+1
-0
MembersPresenter.kt
...t/rocket/android/members/presentation/MembersPresenter.kt
+2
-1
PinnedMessagesPresenter.kt
...id/pinnedmessages/presentation/PinnedMessagesPresenter.kt
+2
-1
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+2
-5
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+22
-0
DatabaseMessageMapper.kt
...t/android/server/infraestructure/DatabaseMessageMapper.kt
+7
-34
DatabaseMessagesRepository.kt
...roid/server/infraestructure/DatabaseMessagesRepository.kt
+30
-17
PasswordPresenter.kt
...droid/settings/password/presentation/PasswordPresenter.kt
+5
-1
SettingsFragment.kt
.../java/chat/rocket/android/settings/ui/SettingsFragment.kt
+22
-11
IO.kt
app/src/main/java/chat/rocket/android/util/IO.kt
+29
-0
emoji_image_row_item.xml
app/src/main/res/layout/emoji_image_row_item.xml
+14
-0
emoji_row_item.xml
app/src/main/res/layout/emoji_row_item.xml
+12
-0
reaction_praises_list_item.xml
app/src/main/res/layout/reaction_praises_list_item.xml
+35
-0
strings.xml
app/src/main/res/values-de/strings.xml
+13
-4
strings.xml
app/src/main/res/values-es/strings.xml
+11
-4
strings.xml
app/src/main/res/values-fr/strings.xml
+12
-5
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+13
-5
strings.xml
app/src/main/res/values-it/strings.xml
+321
-0
strings.xml
app/src/main/res/values-ja/strings.xml
+15
-9
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+13
-6
strings.xml
app/src/main/res/values-ru-rRU/strings.xml
+13
-5
strings.xml
app/src/main/res/values-tr/strings.xml
+12
-6
strings.xml
app/src/main/res/values-uk/strings.xml
+12
-4
defaults.xml
app/src/main/res/values/defaults.xml
+1
-0
strings.xml
app/src/main/res/values/strings.xml
+11
-4
AnswersAnalytics.kt
...ay/java/chat/rocket/android/analytics/AnswersAnalytics.kt
+7
-0
GoogleAnalyticsForFirebase.kt
...at/rocket/android/analytics/GoogleAnalyticsForFirebase.kt
+5
-0
FirebaseMessagingService.kt
...java/chat/rocket/android/push/FirebaseMessagingService.kt
+7
-3
EmojiKeyboardPopup.kt
...main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
+29
-38
EmojiReactionListener.kt
...n/java/chat/rocket/android/emoji/EmojiReactionListener.kt
+11
-1
bg_skin_tone.xml
emoji/src/main/res/drawable/bg_skin_tone.xml
+3
-1
dialog_skin_tone_chooser.xml
emoji/src/main/res/layout/dialog_skin_tone_chooser.xml
+85
-0
emoji_keyboard.xml
emoji/src/main/res/layout/emoji_keyboard.xml
+1
-1
strings.xml
emoji/src/main/res/values/strings.xml
+1
-0
No files found.
app/schemas/chat.rocket.android.db.RCDatabase/12.json
0 → 100644
View file @
ee16b416
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/analytics/Analytics.kt
View file @
ee16b416
...
...
@@ -61,7 +61,14 @@ interface Analytics {
fun
logServerSwitch
(
serverUrl
:
String
,
serverCount
:
Int
)
{}
/**
* Logs the admin opening.
* Logs the admin opening
event
.
*/
fun
logOpenAdmin
()
{}
/**
* Logs the reset password event.
*
* @param resetPasswordSucceeded True if successful reset password, false otherwise.
*/
fun
logResetPassword
(
resetPasswordSucceeded
:
Boolean
)
{}
}
app/src/main/java/chat/rocket/android/analytics/AnalyticsManager.kt
View file @
ee16b416
...
...
@@ -70,4 +70,10 @@ class AnalyticsManager @Inject constructor(
analytics
.
forEach
{
it
.
logOpenAdmin
()
}
}
}
fun
logResetPassword
(
resetPasswordSucceeded
:
Boolean
)
{
if
(
analyticsTrackingInteractor
.
get
())
{
analytics
.
forEach
{
it
.
logResetPassword
(
resetPasswordSucceeded
)
}
}
}
}
app/src/main/java/chat/rocket/android/chatroom/adapter/BaseViewHolder.kt
View file @
ee16b416
...
...
@@ -61,6 +61,10 @@ abstract class BaseViewHolder<T : BaseUiModel<*>>(
reactionListener
?.
onReactionAdded
(
messageId
,
emoji
)
}
}
override
fun
onReactionLongClicked
(
shortname
:
String
,
isCustom
:
Boolean
,
url
:
String
?,
usernames
:
List
<
String
>)
{
reactionListener
?.
onReactionLongClicked
(
shortname
,
isCustom
,
url
,
usernames
)
}
}
val
context
=
itemView
.
context
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageReactionsAdapter.kt
View file @
ee16b416
...
...
@@ -75,7 +75,7 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
class
ReactionViewHolder
(
view
:
View
,
private
val
listener
:
EmojiReactionListener
?
)
:
RecyclerView
.
ViewHolder
(
view
),
View
.
OnClickListener
{
)
:
RecyclerView
.
ViewHolder
(
view
),
View
.
OnClickListener
,
View
.
OnLongClickListener
{
@Inject
lateinit
var
localRepository
:
LocalRepository
...
...
@@ -121,6 +121,8 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
view_flipper_reaction
.
setOnClickListener
(
this
@ReactionViewHolder
)
text_count
.
setOnClickListener
(
this
@ReactionViewHolder
)
view_flipper_reaction
.
setOnLongClickListener
(
this
@ReactionViewHolder
)
text_count
.
setOnLongClickListener
(
this
@ReactionViewHolder
)
}
}
...
...
@@ -132,6 +134,11 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
}
}
}
override
fun
onLongClick
(
v
:
View
?):
Boolean
{
listener
?.
onReactionLongClicked
(
reaction
.
shortname
,
reaction
.
isCustom
,
reaction
.
url
,
reaction
.
usernames
)
return
true
}
}
class
AddReactionViewHolder
(
...
...
app/src/main/java/chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
View file @
ee16b416
package
chat.rocket.android.chatroom.di
import
android.app.Application
import
androidx.lifecycle.LifecycleOwner
import
chat.rocket.android.chatroom.presentation.ChatRoomView
import
chat.rocket.android.chatroom.ui.ChatRoomFragment
import
chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.db.ChatRoomDao
import
chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.db.DatabaseManagerFactory
import
chat.rocket.android.db.UserDao
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetCurrentUserInteractor
import
chat.rocket.android.server.domain.PermissionsInteractor
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.TokenRepository
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.experimental.Job
...
...
@@ -42,4 +49,30 @@ class ChatRoomFragmentModule {
@Provides
@PerFragment
fun
provideChatRoomDao
(
manager
:
DatabaseManager
):
ChatRoomDao
=
manager
.
chatRoomDao
()
@Provides
@PerFragment
fun
provideUserDao
(
manager
:
DatabaseManager
):
UserDao
=
manager
.
userDao
()
@Provides
@PerFragment
fun
provideGetCurrentUserInteractor
(
tokenRepository
:
TokenRepository
,
@Named
(
"currentServer"
)
serverUrl
:
String
,
userDao
:
UserDao
):
GetCurrentUserInteractor
{
return
GetCurrentUserInteractor
(
tokenRepository
,
serverUrl
,
userDao
)
}
@Provides
@PerFragment
fun
provideRoomMapper
(
context
:
Application
,
repository
:
SettingsRepository
,
userInteractor
:
GetCurrentUserInteractor
,
@Named
(
"currentServer"
)
serverUrl
:
String
,
permissionsInteractor
:
PermissionsInteractor
):
RoomUiModelMapper
{
return
RoomUiModelMapper
(
context
,
repository
.
get
(
serverUrl
),
userInteractor
,
serverUrl
,
permissionsInteractor
)
}
}
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
ee16b416
...
...
@@ -15,6 +15,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import
chat.rocket.android.chatrooms.adapter.RoomUiModelMapper
import
chat.rocket.android.core.behaviours.showMessage
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.db.DatabaseManager
...
...
@@ -34,11 +35,11 @@ import chat.rocket.android.server.domain.uploadMimeTypeFilter
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extension.compressImageAndGetByteArray
import
chat.rocket.android.util.extension.getByteArray
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.getBitmpap
import
chat.rocket.android.util.extensions.exhaustive
import
chat.rocket.android.util.retryDB
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.RoomType
...
...
@@ -74,6 +75,7 @@ import chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoomRole
import
chat.rocket.core.model.Command
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Room
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.DefaultDispatcher
import
kotlinx.coroutines.experimental.android.UI
...
...
@@ -82,7 +84,6 @@ import kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
org.threeten.bp.Instant
import
timber.log.Timber
import
java.io.InputStream
import
java.util.*
import
javax.inject.Inject
...
...
@@ -98,6 +99,7 @@ class ChatRoomPresenter @Inject constructor(
private
val
analyticsManager
:
AnalyticsManager
,
private
val
userHelper
:
UserHelper
,
private
val
mapper
:
UiModelMapper
,
private
val
roomMapper
:
RoomUiModelMapper
,
private
val
jobSchedulerInteractor
:
JobSchedulerInteractor
,
private
val
messageHelper
:
MessageHelper
,
private
val
dbManager
:
DatabaseManager
,
...
...
@@ -120,6 +122,7 @@ class ChatRoomPresenter @Inject constructor(
private
var
typingStatusSubscriptionId
:
String
?
=
null
private
var
lastState
=
manager
.
state
private
var
typingStatusList
=
arrayListOf
<
String
>()
private
val
roomChangesChannel
=
Channel
<
Room
>(
Channel
.
CONFLATED
)
fun
setupChatRoom
(
roomId
:
String
,
...
...
@@ -127,7 +130,7 @@ class ChatRoomPresenter @Inject constructor(
roomType
:
String
,
chatRoomMessage
:
String
?
=
null
)
{
launch
UI
(
strategy
)
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
try
{
chatRoles
=
if
(
roomTypeOf
(
roomType
)
!
is
RoomType
.
DirectMessage
)
{
client
.
chatRoomRoles
(
roomType
=
roomTypeOf
(
roomType
),
roomName
=
roomName
)
...
...
@@ -137,13 +140,21 @@ class ChatRoomPresenter @Inject constructor(
chatRoles
=
emptyList
()
}
finally
{
// User has at least an 'owner' or 'moderator' role.
val
userCanMod
=
isOwnerOrMod
()
val
canModerate
=
isOwnerOrMod
()
// Can post anyway if has the 'post-readonly' permission on server.
val
userCanPost
=
userCanMod
||
permissions
.
canPostToReadOnlyChannels
()
chatIsBroadcast
=
dbManager
.
getRoom
(
roomId
)
?.
chatRoom
?.
run
{
broadcast
}
?:
false
view
.
onRoomUpdated
(
userCanPost
,
chatIsBroadcast
,
userCanMod
)
val
room
=
dbManager
.
getRoom
(
roomId
)
room
?.
let
{
chatIsBroadcast
=
it
.
chatRoom
.
broadcast
?:
false
val
roomUiModel
=
roomMapper
.
map
(
it
,
true
)
launchUI
(
strategy
)
{
view
.
onRoomUpdated
(
roomUiModel
=
roomUiModel
.
copy
(
broadcast
=
chatIsBroadcast
,
canModerate
=
canModerate
,
writable
=
roomUiModel
.
writable
||
canModerate
))
}
}
loadMessages
(
roomId
,
roomType
,
clearDataSet
=
true
)
chatRoomMessage
?.
let
{
messageHelper
.
messageIdFromPermalink
(
it
)
}
?.
let
{
messageId
->
...
...
@@ -155,10 +166,26 @@ class ChatRoomPresenter @Inject constructor(
true
)
}
subscribeRoomChanges
()
}
}
}
private
suspend
fun
subscribeRoomChanges
()
{
chatRoomId
?.
let
{
manager
.
addRoomChannel
(
it
,
roomChangesChannel
)
for
(
room
in
roomChangesChannel
)
{
dbManager
.
getRoom
(
room
.
id
)
?.
let
{
view
.
onRoomUpdated
(
roomMapper
.
map
(
chatRoom
=
it
,
showLastMessage
=
true
))
}
}
}
}
private
fun
unsubscribeRoomChanges
()
{
chatRoomId
?.
let
{
manager
.
removeRoomChannel
(
it
)
}
}
private
fun
isOwnerOrMod
():
Boolean
{
return
chatRoles
.
firstOrNull
{
it
.
user
.
username
==
currentLoggedUsername
}
?.
roles
?.
any
{
it
==
"owner"
||
it
==
"moderator"
...
...
@@ -901,7 +928,8 @@ class ChatRoomPresenter @Inject constructor(
// TODO: move this to new interactor or FetchChatRoomsInteractor?
private
suspend
fun
getChatRoomAsync
(
roomId
:
String
):
ChatRoom
?
=
withContext
(
CommonPool
)
{
return
@withContext
dbManager
.
chatRoomDao
().
get
(
roomId
)
?.
let
{
retryDB
(
"getRoom($roomId)"
)
{
dbManager
.
chatRoomDao
().
get
(
roomId
)
?.
let
{
with
(
it
.
chatRoom
)
{
ChatRoom
(
id
=
id
,
...
...
@@ -934,10 +962,12 @@ class ChatRoomPresenter @Inject constructor(
}
}
}
}
// TODO: move this to new interactor or FetchChatRoomsInteractor?
private
suspend
fun
getChatRoomsAsync
(
name
:
String
?
=
null
):
List
<
ChatRoom
>
=
withContext
(
CommonPool
)
{
return
@withContext
dbManager
.
chatRoomDao
().
getAllSync
().
filter
{
retryDB
(
"getAllSync()"
)
{
dbManager
.
chatRoomDao
().
getAllSync
().
filter
{
if
(
name
==
null
)
{
return
@filter
true
}
...
...
@@ -975,13 +1005,19 @@ class ChatRoomPresenter @Inject constructor(
}
}
}
}
fun
joinChat
(
chatRoomId
:
String
)
{
launchUI
(
strategy
)
{
try
{
retryIO
(
"joinChat($chatRoomId)"
)
{
client
.
joinChat
(
chatRoomId
)
}
val
canPost
=
permissions
.
canPostToReadOnlyChannels
()
view
.
onJoined
(
canPost
)
dbManager
.
getRoom
(
chatRoomId
)
?.
let
{
val
roomUiModel
=
roomMapper
.
map
(
it
,
true
).
copy
(
writable
=
canPost
)
view
.
onJoined
(
roomUiModel
=
roomUiModel
)
view
.
onRoomUpdated
(
roomUiModel
=
roomUiModel
)
}
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
)
}
...
...
@@ -1151,6 +1187,7 @@ class ChatRoomPresenter @Inject constructor(
}
fun
disconnect
()
{
unsubscribeRoomChanges
()
unsubscribeTypingStatus
()
if
(
chatRoomId
!=
null
)
{
unsubscribeMessages
(
chatRoomId
.
toString
())
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
ee16b416
...
...
@@ -5,6 +5,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import
chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import
chat.rocket.android.core.behaviours.LoadingView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.core.internal.realtime.socket.model.State
...
...
@@ -131,12 +132,7 @@ interface ChatRoomView : LoadingView, MessageView {
fun
populateEmojiSuggestions
(
emojis
:
List
<
EmojiSuggestionUiModel
>)
/**
* This user has joined the chat callback.
*
* @param userCanPost Whether the user can post a message or not.
*/
fun
onJoined
(
userCanPost
:
Boolean
)
fun
onJoined
(
roomUiModel
:
RoomUiModel
)
fun
showReactionsPopup
(
messageId
:
String
)
...
...
@@ -147,9 +143,6 @@ interface ChatRoomView : LoadingView, MessageView {
*/
fun
populateCommandSuggestions
(
commands
:
List
<
CommandSuggestionUiModel
>)
/**
* Communicate whether it's a broadcast channel and if current user can post to it.
*/
fun
onRoomUpdated
(
userCanPost
:
Boolean
,
channelIsBroadcast
:
Boolean
,
userCanMod
:
Boolean
)
fun
onRoomUpdated
(
roomUiModel
:
RoomUiModel
)
}
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
ee16b416
...
...
@@ -51,6 +51,7 @@ import chat.rocket.android.chatroom.uimodel.suggestion.ChatRoomSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.CommandSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.EmojiSuggestionUiModel
import
chat.rocket.android.chatroom.uimodel.suggestion.PeopleSuggestionUiModel
import
chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import
chat.rocket.android.draw.main.ui.DRAWING_BYTE_ARRAY_EXTRA_DATA
import
chat.rocket.android.draw.main.ui.DrawingActivity
import
chat.rocket.android.emoji.ComposerEditText
...
...
@@ -60,6 +61,7 @@ import chat.rocket.android.emoji.EmojiKeyboardPopup
import
chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiPickerPopup
import
chat.rocket.android.emoji.EmojiReactionListener
import
chat.rocket.android.emoji.internal.GlideApp
import
chat.rocket.android.emoji.internal.isCustom
import
chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import
chat.rocket.android.helper.ImageHelper
...
...
@@ -84,6 +86,8 @@ import dagger.android.support.AndroidSupportInjection
import
io.reactivex.Observable
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.Disposable
import
kotlinx.android.synthetic.main.emoji_image_row_item.*
import
kotlinx.android.synthetic.main.emoji_row_item.*
import
kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.message_attachment_options.*
import
kotlinx.android.synthetic.main.message_composer.*
...
...
@@ -91,6 +95,7 @@ import kotlinx.android.synthetic.main.message_list.*
import
timber.log.Timber
import
java.io.File
import
java.io.IOException
import
kotlinx.android.synthetic.main.reaction_praises_list_item.*
import
java.util.concurrent.TimeUnit
import
java.util.concurrent.atomic.AtomicInteger
import
javax.inject.Inject
...
...
@@ -397,17 +402,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
empty_chat_view
.
isVisible
=
adapter
.
itemCount
==
0
}
override
fun
onRoomUpdated
(
userCanPost
:
Boolean
,
channelIsBroadcast
:
Boolean
,
userCanMod
:
Boolean
)
{
override
fun
onRoomUpdated
(
roomUiModel
:
RoomUiModel
)
{
// TODO: We should rely solely on the user being able to post, but we cannot guarantee
// that the "(channels|groups).roles" endpoint is supported by the server in use.
ui
{
setupMessageComposer
(
userCanPost
)
isBroadcastChannel
=
channelIsBroadcast
if
(
isBroadcastChannel
&&
!
userCanMod
)
{
setupToolbar
(
roomUiModel
.
name
.
toString
())
setupMessageComposer
(
roomUiModel
)
isBroadcastChannel
=
roomUiModel
.
broadcast
if
(
isBroadcastChannel
&&
!
roomUiModel
.
canModerate
)
{
disableMenu
=
true
activity
?.
invalidateOptionsMenu
()
}
...
...
@@ -544,7 +546,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
ui
{
button_send
.
isEnabled
=
true
text_message
.
isEnabled
=
true
clearMessageComposition
(
true
)
}
}
...
...
@@ -611,7 +612,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override
fun
showGenericErrorMessage
(){
override
fun
showGenericErrorMessage
()
{
ui
{
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
}
...
...
@@ -688,6 +689,44 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
presenter
.
react
(
messageId
,
emoji
.
shortname
)
}
override
fun
onReactionLongClicked
(
shortname
:
String
,
isCustom
:
Boolean
,
url
:
String
?,
usernames
:
List
<
String
>)
{
val
layout
=
LayoutInflater
.
from
(
requireContext
()).
inflate
(
R
.
layout
.
reaction_praises_list_item
,
null
)
val
dialog
=
AlertDialog
.
Builder
(
requireContext
())
.
setView
(
layout
)
.
setCancelable
(
true
)
with
(
layout
)
{
view_flipper
.
displayedChild
=
if
(
isCustom
)
1
else
0
if
(
isCustom
&&
url
!=
null
)
{
val
glideRequest
=
if
(
url
.
endsWith
(
"gif"
,
true
))
{
GlideApp
.
with
(
requireContext
()).
asGif
()
}
else
{
GlideApp
.
with
(
requireContext
()).
asBitmap
()
}
glideRequest
.
load
(
url
).
into
(
emoji_image_view
)
}
else
{
emoji_view
.
text
=
EmojiParser
.
parse
(
requireContext
(),
shortname
)
}
var
listing
=
""
if
(
usernames
.
size
==
1
)
{
listing
=
usernames
.
first
()
}
else
{
usernames
.
forEachIndexed
{
index
,
username
->
listing
+=
if
(
index
==
usernames
.
size
-
1
)
"|$username"
else
"$username, "
}
listing
=
listing
.
replace
(
", |"
,
" ${requireContext().getString(R.string.msg_and)} "
)
}
text_view_usernames
.
text
=
requireContext
().
resources
.
getQuantityString
(
R
.
plurals
.
msg_reacted_with_
,
usernames
.
size
,
listing
,
shortname
)
dialog
.
show
()
}
}
override
fun
showReactionsPopup
(
messageId
:
String
)
{
ui
{
val
emojiPickerPopup
=
EmojiPickerPopup
(
it
)
...
...
@@ -748,12 +787,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
override
fun
onJoined
(
userCanPost
:
Boolean
)
{
override
fun
onJoined
(
roomUiModel
:
RoomUiModel
)
{
ui
{
input_container
.
isVisible
=
true
button_join_chat
.
isVisible
=
false
isSubscribed
=
true
setupMessageComposer
(
userCanPost
)
}
}
...
...
@@ -786,15 +824,25 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
fun
setupMessageComposer
(
canPost
:
Boolean
)
{
if
(
isReadOnly
&&
!
canPost
)
{
private
fun
setupMessageComposer
(
roomUiModel
:
RoomUiModel
)
{
if
(
isReadOnly
||
!
roomUiModel
.
writable
)
{
text_room_is_read_only
.
isVisible
=
true
input_container
.
isVisible
=
false
text_room_is_read_only
.
setText
(
if
(
isReadOnly
)
{
R
.
string
.
msg_this_room_is_read_only
}
else
{
// Not a read-only channel but user has been muted.
R
.
string
.
msg_muted_on_this_channel
}
)
}
else
if
(!
isSubscribed
&&
roomTypeOf
(
chatRoomType
)
!
is
RoomType
.
DirectMessage
)
{
input_container
.
isVisible
=
false
button_join_chat
.
isVisible
=
true
button_join_chat
.
setOnClickListener
{
presenter
.
joinChat
(
chatRoomId
)
}
}
else
{
input_container
.
isVisible
=
true
text_room_is_read_only
.
isVisible
=
false
button_send
.
isVisible
=
false
button_show_attachment_options
.
alpha
=
1f
button_show_attachment_options
.
isVisible
=
true
...
...
app/src/main/java/chat/rocket/android/chatroom/uimodel/ReactionUiModel.kt
View file @
ee16b416
...
...
@@ -6,5 +6,6 @@ data class ReactionUiModel(
val
unicode
:
CharSequence
,
val
count
:
Int
,
val
usernames
:
List
<
String
>
=
emptyList
(),
var
url
:
String
?
=
null
var
url
:
String
?
=
null
,
val
isCustom
:
Boolean
=
false
)
app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt
View file @
ee16b416
...
...
@@ -19,6 +19,7 @@ import chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiRepository
import
chat.rocket.android.emoji.internal.isCustom
import
chat.rocket.android.helper.MessageHelper
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.helper.UserHelper
...
...
@@ -31,6 +32,7 @@ import chat.rocket.android.server.domain.messageReadReceiptEnabled
import
chat.rocket.android.server.domain.messageReadReceiptStoreUsers
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.util.extension.orFalse
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.ifNotNullNorEmpty
import
chat.rocket.android.util.extensions.isNotNullNorEmpty
...
...
@@ -469,7 +471,7 @@ class UiModelMapper @Inject constructor(
val
list
=
mutableListOf
<
ReactionUiModel
>()
val
customEmojis
=
EmojiRepository
.
getCustomEmojis
()
it
.
getShortNames
().
forEach
{
shortname
->
val
usernames
=
it
.
getUsernames
(
shortname
)
?:
emptyList
()
val
usernames
=
it
.
getUsernames
(
shortname
)
.
orEmpty
()
val
count
=
usernames
.
size
val
custom
=
customEmojis
.
firstOrNull
{
emoji
->
emoji
.
shortname
==
shortname
}
list
.
add
(
...
...
@@ -478,7 +480,8 @@ class UiModelMapper @Inject constructor(
unicode
=
EmojiParser
.
parse
(
context
,
shortname
),
count
=
count
,
usernames
=
usernames
,
url
=
custom
?.
url
)
url
=
custom
?.
url
,
isCustom
=
custom
!=
null
)
)
}
list
...
...
app/src/main/java/chat/rocket/android/chatrooms/adapter/RoomUiModelMapper.kt
View file @
ee16b416
...
...
@@ -8,9 +8,8 @@ import androidx.core.text.color
import
chat.rocket.android.R
import
chat.rocket.android.chatrooms.adapter.model.RoomUiModel
import
chat.rocket.android.db.model.ChatRoom
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.checkIfMyself
import
chat.rocket.android.server.domain.GetCurrentUserInteractor
import
chat.rocket.android.server.domain.PermissionsInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.showLastMessage
import
chat.rocket.android.server.domain.useRealName
...
...
@@ -30,7 +29,8 @@ class RoomUiModelMapper(
private
val
context
:
Application
,
private
val
settings
:
PublicSettings
,
private
val
userInteractor
:
GetCurrentUserInteractor
,
private
val
serverUrl
:
String
private
val
serverUrl
:
String
,
private
val
permissions
:
PermissionsInteractor
)
{
private
val
nameUnreadColor
=
ContextCompat
.
getColor
(
context
,
R
.
color
.
colorPrimaryText
)
private
val
nameColor
=
ContextCompat
.
getColor
(
context
,
R
.
color
.
colorSecondaryText
)
...
...
@@ -97,7 +97,9 @@ class RoomUiModelMapper(
avatar
=
serverUrl
.
avatarUrl
(
name
!!
,
isGroupOrChannel
=
true
),
lastMessage
=
if
(
showLastMessage
)
{
mapLastMessage
(
lastMessage
?.
sender
?.
id
,
lastMessage
?.
sender
?.
username
,
lastMessage
?.
sender
?.
name
,
lastMessage
?.
message
,
isDirectMessage
=
type
is
RoomType
.
DirectMessage
)}
else
{
null
}
isDirectMessage
=
type
is
RoomType
.
DirectMessage
)}
else
{
null
},
muted
=
muted
.
orEmpty
(),
writable
=
isChannelWritable
(
muted
)
)
}
}
...
...
@@ -133,11 +135,18 @@ class RoomUiModelMapper(
alert
=
isUnread
,
lastMessage
=
lastMessageMarkdown
,
status
=
status
,
username
=
if
(
type
is
RoomType
.
DirectMessage
)
name
else
null
username
=
if
(
type
is
RoomType
.
DirectMessage
)
name
else
null
,
muted
=
muted
.
orEmpty
(),
writable
=
isChannelWritable
(
muted
)
)
}
}
private
fun
isChannelWritable
(
muted
:
List
<
String
>?):
Boolean
{
val
canWriteToReadOnlyChannels
=
permissions
.
canPostToReadOnlyChannels
()
return
canWriteToReadOnlyChannels
||
!
muted
.
orEmpty
().
contains
(
currentUser
?.
username
)
}
private
fun
roomType
(
type
:
String
):
String
{
val
resources
=
context
.
resources
return
when
(
type
)
{
...
...
app/src/main/java/chat/rocket/android/chatrooms/adapter/model/RoomUiModel.kt
View file @
ee16b416
...
...
@@ -14,5 +14,9 @@ data class RoomUiModel(
val
alert
:
Boolean
=
false
,
val
lastMessage
:
CharSequence
?
=
null
,
val
status
:
UserStatus
?
=
null
,
val
username
:
String
?
=
null
val
username
:
String
?
=
null
,
val
broadcast
:
Boolean
=
false
,
val
canModerate
:
Boolean
=
false
,
val
writable
:
Boolean
=
true
,
val
muted
:
List
<
String
>
=
emptyList
()
)
app/src/main/java/chat/rocket/android/chatrooms/di/ChatRoomsFragmentModule.kt
View file @
ee16b416
...
...
@@ -12,6 +12,7 @@ import chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.db.UserDao
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentUserInteractor
import
chat.rocket.android.server.domain.PermissionsInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.TokenRepository
...
...
@@ -88,9 +89,10 @@ class ChatRoomsFragmentModule {
context
:
Application
,
repository
:
SettingsRepository
,
userInteractor
:
GetCurrentUserInteractor
,
@Named
(
"currentServer"
)
serverUrl
:
String
@Named
(
"currentServer"
)
serverUrl
:
String
,
permissionsInteractor
:
PermissionsInteractor
):
RoomUiModelMapper
{
return
RoomUiModelMapper
(
context
,
repository
.
get
(
serverUrl
),
userInteractor
,
serverUrl
)
return
RoomUiModelMapper
(
context
,
repository
.
get
(
serverUrl
),
userInteractor
,
serverUrl
,
permissionsInteractor
)
}
@Provides
...
...
app/src/main/java/chat/rocket/android/chatrooms/infrastructure/ChatRoomsRepository.kt
View file @
ee16b416
...
...
@@ -3,9 +3,12 @@ package chat.rocket.android.chatrooms.infrastructure
import
androidx.lifecycle.LiveData
import
chat.rocket.android.db.ChatRoomDao
import
chat.rocket.android.db.model.ChatRoom
import
chat.rocket.android.util.retryDB
import
javax.inject.Inject
class
ChatRoomsRepository
@Inject
constructor
(
private
val
dao
:
ChatRoomDao
){
class
ChatRoomsRepository
@Inject
constructor
(
private
val
dao
:
ChatRoomDao
)
{
// TODO - check how to use retryDB here - suspend
fun
getChatRooms
(
order
:
Order
):
LiveData
<
List
<
ChatRoom
>>
{
return
when
(
order
)
{
Order
.
ACTIVITY
->
dao
.
getAll
()
...
...
@@ -15,9 +18,10 @@ class ChatRoomsRepository @Inject constructor(private val dao: ChatRoomDao){
}
}
fun
search
(
query
:
String
)
=
dao
.
searchSync
(
query
)
suspend
fun
search
(
query
:
String
)
=
retryDB
(
"roomSearch($query)"
)
{
dao
.
searchSync
(
query
)
}
fun
count
()
=
dao
.
count
()
suspend
fun
count
()
=
retryDB
(
"roomsCount"
)
{
dao
.
count
()
}
enum
class
Order
{
ACTIVITY
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
ee16b416
...
...
@@ -13,6 +13,7 @@ import chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.server.domain.useSpecialCharsOnRoom
import
chat.rocket.android.server.infraestructure.ConnectionManager
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.retryDB
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.RoomType
...
...
@@ -41,11 +42,31 @@ class ChatRoomsPresenter @Inject constructor(
private
val
client
=
manager
.
client
private
val
settings
=
settingsRepository
.
get
(
currentServer
)
fun
loadChatRoom
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
view
.
showLoadingRoom
(
""
)
try
{
val
room
=
dbManager
.
getRoom
(
roomId
)
if
(
room
!=
null
)
{
loadChatRoom
(
room
.
chatRoom
,
true
)
}
else
{
Timber
.
d
(
"Error loading channel"
)
view
.
showGenericErrorMessage
()
}
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error loading channel"
)
view
.
showGenericErrorMessage
()
}
finally
{
view
.
hideLoadingRoom
()
}
}
}
fun
loadChatRoom
(
chatRoom
:
RoomUiModel
)
{
launchUI
(
strategy
)
{
view
.
showLoadingRoom
(
chatRoom
.
name
)
try
{
val
room
=
dbManager
.
getRoom
(
chatRoom
.
id
)
val
room
=
retryDB
(
"getRoom(${chatRoom.id}"
)
{
dbManager
.
getRoom
(
chatRoom
.
id
)
}
if
(
room
!=
null
)
{
loadChatRoom
(
room
.
chatRoom
,
true
)
}
else
{
...
...
@@ -56,7 +77,8 @@ class ChatRoomsPresenter @Inject constructor(
type
=
type
.
toString
(),
name
=
username
?:
name
.
toString
(),
fullname
=
name
.
toString
(),
open
=
open
open
=
open
,
muted
=
muted
)
loadChatRoom
(
entity
,
false
)
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
ee16b416
...
...
@@ -84,8 +84,7 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
if
(
bundle
!=
null
)
{
chatRoomId
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_ID
)
chatRoomId
?.
let
{
// TODO - bring back support to load a room from id.
//presenter.goToChatRoomWithId(it)
presenter
.
loadChatRoom
(
it
)
chatRoomId
=
null
}
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/viewmodel/ChatRoomsViewModel.kt
View file @
ee16b416
...
...
@@ -27,7 +27,7 @@ import kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.newSingleThreadContext
import
kotlinx.coroutines.experimental.withContext
import
timber.log.Timber
import
java.
security.InvalidParameter
Exception
import
java.
lang.IllegalArgument
Exception
import
kotlin.coroutines.experimental.coroutineContext
...
...
@@ -171,6 +171,6 @@ fun Query.asSortingOrder(): ChatRoomsRepository.Order {
ChatRoomsRepository
.
Order
.
ACTIVITY
}
}
else
->
throw
I
nvalidParameter
Exception
(
"Should be ByName or ByActivity"
)
else
->
throw
I
llegalArgument
Exception
(
"Should be ByName or ByActivity"
)
}
}
app/src/main/java/chat/rocket/android/db/DatabaseManager.kt
View file @
ee16b416
...
...
@@ -19,6 +19,7 @@ import chat.rocket.android.util.extensions.exhaustive
import
chat.rocket.android.util.extensions.removeTrailingSlash
import
chat.rocket.android.util.extensions.toEntity
import
chat.rocket.android.util.extensions.userId
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.SimpleUser
...
...
@@ -41,8 +42,7 @@ import timber.log.Timber
import
java.util.HashSet
import
kotlin.system.measureTimeMillis
class
DatabaseManager
(
val
context
:
Application
,
val
serverUrl
:
String
)
{
class
DatabaseManager
(
val
context
:
Application
,
val
serverUrl
:
String
)
{
private
val
database
:
RCDatabase
=
androidx
.
room
.
Room
.
databaseBuilder
(
context
,
RCDatabase
::
class
.
java
,
serverUrl
.
databaseName
())
...
...
@@ -92,13 +92,15 @@ class DatabaseManager(val context: Application,
}
}
fun
logout
()
{
database
.
clearAllTables
()
suspend
fun
logout
()
{
retryDB
(
"clearAllTables"
)
{
database
.
clearAllTables
()
}
}
suspend
fun
getRoom
(
id
:
String
)
=
withContext
(
dbManagerContext
)
{
retryDB
(
"getRoom($id)"
)
{
chatRoomDao
().
get
(
id
)
}
}
fun
processUsersBatch
(
users
:
List
<
User
>)
{
launch
(
dbManagerContext
)
{
...
...
@@ -124,11 +126,11 @@ class DatabaseManager(val context: Application,
val
toInsert
=
ArrayList
<
ChatRoomEntity
>(
batch
.
size
/
2
)
val
toUpdate
=
ArrayList
<
ChatRoomEntity
>(
batch
.
size
)
batch
.
forEach
{
when
(
it
.
type
)
{
when
(
it
.
type
)
{
is
Type
.
Removed
->
toRemove
.
add
(
removeChatRoom
(
it
.
data
))
is
Type
.
Inserted
->
insertChatRoom
(
it
.
data
)
?.
let
{
toInsert
.
add
(
it
)
}
is
Type
.
Updated
->
{
when
(
it
.
data
)
{
when
(
it
.
data
)
{
is
Subscription
->
updateSubs
[(
it
.
data
as
Subscription
).
roomId
]
=
it
.
data
as
Subscription
is
Room
->
updateRooms
[(
it
.
data
as
Room
).
id
]
=
it
.
data
as
Room
}
...
...
@@ -152,7 +154,7 @@ class DatabaseManager(val context: Application,
fun
updateSelfUser
(
myself
:
Myself
)
{
launch
(
dbManagerContext
)
{
val
user
=
userDao
().
getUser
(
myself
.
id
)
val
user
=
retryDB
(
"getUser(${myself.id})"
)
{
userDao
().
getUser
(
myself
.
id
)
}
val
entity
=
user
?.
copy
(
name
=
myself
.
name
?:
user
.
name
,
username
=
myself
.
username
?:
user
.
username
,
...
...
@@ -329,14 +331,14 @@ class DatabaseManager(val context: Application,
}
private
fun
removeChatRoom
(
data
:
BaseRoom
):
String
{
return
when
(
data
)
{
return
when
(
data
)
{
is
Subscription
->
data
.
roomId
else
->
data
.
id
}
}
private
suspend
fun
updateRoom
(
data
:
Room
):
ChatRoomEntity
?
{
return
chatRoomDao
().
get
(
data
.
id
)
?.
let
{
current
->
return
retryDB
(
"getChatRoom(${data.id})"
)
{
chatRoomDao
().
get
(
data
.
id
)
}
?.
let
{
current
->
with
(
data
)
{
val
chatRoom
=
current
.
chatRoom
...
...
@@ -351,7 +353,8 @@ class DatabaseManager(val context: Application,
updatedAt
=
updatedAt
?:
chatRoom
.
updatedAt
,
lastMessageText
=
mapLastMessageText
(
lastMessage
),
lastMessageUserId
=
lastMessage
?.
sender
?.
id
,
lastMessageTimestamp
=
lastMessage
?.
timestamp
lastMessageTimestamp
=
lastMessage
?.
timestamp
,
muted
=
muted
)
}
}
...
...
@@ -373,7 +376,7 @@ class DatabaseManager(val context: Application,
context
.
getString
(
R
.
string
.
msg_sent_attachment
)
private
suspend
fun
updateSubscription
(
data
:
Subscription
):
ChatRoomEntity
?
{
return
chatRoomDao
().
get
(
data
.
roomId
)
?.
let
{
current
->
return
retryDB
(
"getRoom(${data.roomId}"
)
{
chatRoomDao
().
get
(
data
.
roomId
)
}
?.
let
{
current
->
with
(
data
)
{
val
userId
=
if
(
type
is
RoomType
.
DirectMessage
)
{
...
...
@@ -389,7 +392,8 @@ class DatabaseManager(val context: Application,
id
=
roomId
,
subscriptionId
=
id
,
type
=
type
.
toString
(),
name
=
name
?:
throw
NullPointerException
(),
// this should be filtered on the SDK
name
=
name
?:
throw
NullPointerException
(),
// this should be filtered on the SDK
fullname
=
fullName
?:
chatRoom
.
fullname
,
userId
=
userId
?:
chatRoom
.
userId
,
readonly
=
readonly
?:
chatRoom
.
readonly
,
...
...
@@ -402,14 +406,15 @@ class DatabaseManager(val context: Application,
groupMentions
=
groupMentions
?:
chatRoom
.
groupMentions
,
updatedAt
=
updatedAt
?:
chatRoom
.
updatedAt
,
timestamp
=
timestamp
?:
chatRoom
.
timestamp
,
lastSeen
=
lastSeen
?:
chatRoom
.
lastSeen
lastSeen
=
lastSeen
?:
chatRoom
.
lastSeen
,
muted
=
chatRoom
.
muted
)
}
}
}
private
suspend
fun
insertChatRoom
(
data
:
BaseRoom
):
ChatRoomEntity
?
{
return
when
(
data
)
{
return
when
(
data
)
{
is
Room
->
insertRoom
(
data
)
is
Subscription
->
insertSubscription
(
data
)
else
->
null
...
...
@@ -451,7 +456,8 @@ class DatabaseManager(val context: Application,
id
=
room
.
id
,
subscriptionId
=
subscription
.
id
,
type
=
room
.
type
.
toString
(),
name
=
room
.
name
?:
subscription
.
name
?:
throw
NullPointerException
(),
// this should be filtered on the SDK
name
=
room
.
name
?:
subscription
.
name
?:
throw
NullPointerException
(),
// this should be filtered on the SDK
fullname
=
subscription
.
fullName
?:
room
.
fullName
,
userId
=
userId
,
ownerId
=
room
.
user
?.
id
,
...
...
@@ -506,7 +512,8 @@ class DatabaseManager(val context: Application,
lastMessageText
=
mapLastMessageText
(
lastMessage
),
lastMessageUserId
=
lastMessage
?.
sender
?.
id
,
lastMessageTimestamp
=
lastMessage
?.
timestamp
,
broadcast
=
broadcast
broadcast
=
broadcast
,
muted
=
room
.
muted
)
}
}
...
...
@@ -535,9 +542,11 @@ class DatabaseManager(val context: Application,
}
}
private
fun
findUser
(
userId
:
String
):
String
?
=
userDao
().
findUser
(
userId
)
private
suspend
fun
findUser
(
userId
:
String
):
String
?
=
retryDB
(
"findUser($userId)"
)
{
userDao
().
findUser
(
userId
)
}
private
fun
doOperation
(
operation
:
Operation
)
{
private
suspend
fun
doOperation
(
operation
:
Operation
)
{
retryDB
(
description
=
"doOperation($operation)"
)
{
when
(
operation
)
{
is
Operation
.
ClearStatus
->
userDao
().
clearStatus
()
is
Operation
.
UpdateRooms
->
{
...
...
@@ -569,6 +578,7 @@ class DatabaseManager(val context: Application,
}
}.
exhaustive
}
}
}
sealed
class
Operation
{
...
...
@@ -579,6 +589,7 @@ sealed class Operation {
val
toUpdate
:
List
<
ChatRoomEntity
>,
val
toRemove
:
List
<
String
>
)
:
Operation
()
data class
InsertRooms
(
val
chatRooms
:
List
<
ChatRoomEntity
>)
:
Operation
()
data class
CleanInsertRooms
(
val
chatRooms
:
List
<
ChatRoomEntity
>)
:
Operation
()
...
...
@@ -609,7 +620,7 @@ private fun String.databaseName(): String {
val
tmp
=
this
.
removePrefix
(
"https://"
)
.
removePrefix
(
"http://"
)
.
removeTrailingSlash
()
.
replace
(
"/"
,
"-"
)
.
replace
(
"/"
,
"-"
)
.
replace
(
"."
,
"_"
)
return
"$tmp.db"
...
...
app/src/main/java/chat/rocket/android/db/RCDatabase.kt
View file @
ee16b416
...
...
@@ -2,6 +2,7 @@ package chat.rocket.android.db
import
androidx.room.Database
import
androidx.room.RoomDatabase
import
androidx.room.TypeConverters
import
chat.rocket.android.db.model.AttachmentActionEntity
import
chat.rocket.android.db.model.AttachmentEntity
import
chat.rocket.android.db.model.AttachmentFieldEntity
...
...
@@ -14,6 +15,7 @@ import chat.rocket.android.db.model.MessagesSync
import
chat.rocket.android.db.model.ReactionEntity
import
chat.rocket.android.db.model.UrlEntity
import
chat.rocket.android.db.model.UserEntity
import
chat.rocket.android.emoji.internal.db.StringListConverter
@Database
(
entities
=
[
...
...
@@ -23,9 +25,10 @@ import chat.rocket.android.db.model.UserEntity
AttachmentFieldEntity
::
class
,
AttachmentActionEntity
::
class
,
UrlEntity
::
class
,
ReactionEntity
::
class
,
MessagesSync
::
class
],
version
=
1
0
,
version
=
1
2
,
exportSchema
=
true
)
@TypeConverters
(
StringListConverter
::
class
)
abstract
class
RCDatabase
:
RoomDatabase
()
{
abstract
fun
userDao
():
UserDao
abstract
fun
chatRoomDao
():
ChatRoomDao
...
...
app/src/main/java/chat/rocket/android/db/model/ChatRoomEntity.kt
View file @
ee16b416
...
...
@@ -5,6 +5,8 @@ import androidx.room.Entity
import
androidx.room.ForeignKey
import
androidx.room.Index
import
androidx.room.PrimaryKey
import
androidx.room.TypeConverters
import
chat.rocket.android.emoji.internal.db.StringListConverter
@Entity
(
tableName
=
"chatrooms"
,
indices
=
[
...
...
@@ -20,6 +22,7 @@ import androidx.room.PrimaryKey
ForeignKey
(
entity
=
UserEntity
::
class
,
parentColumns
=
[
"id"
],
childColumns
=
[
"lastMessageUserId"
])
]
)
@TypeConverters
(
StringListConverter
::
class
)
data class
ChatRoomEntity
(
@PrimaryKey
var
id
:
String
,
var
subscriptionId
:
String
,
...
...
@@ -42,7 +45,8 @@ data class ChatRoomEntity(
var
lastMessageText
:
String
?
=
null
,
var
lastMessageUserId
:
String
?
=
null
,
var
lastMessageTimestamp
:
Long
?
=
null
,
var
broadcast
:
Boolean
?
=
false
var
broadcast
:
Boolean
?
=
false
,
var
muted
:
List
<
String
>?
=
null
)
data class
ChatRoom
(
...
...
app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt
View file @
ee16b416
...
...
@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
app/src/main/java/chat/rocket/android/files/presentation/FilesPresenter.kt
View file @
ee16b416
...
...
@@ -7,6 +7,7 @@ import chat.rocket.android.files.uimodel.FileUiModel
import
chat.rocket.android.files.uimodel.FileUiModelMapper
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
app/src/main/java/chat/rocket/android/members/presentation/MembersPresenter.kt
View file @
ee16b416
...
...
@@ -6,6 +6,7 @@ import chat.rocket.android.members.uimodel.MemberUiModel
import
chat.rocket.android.members.uimodel.MemberUiModelMapper
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt
View file @
ee16b416
...
...
@@ -5,6 +5,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
ee16b416
...
...
@@ -296,11 +296,8 @@ class PushManager @Inject constructor(
}
private
fun
getContentIntent
(
context
:
Context
,
notificationId
:
Int
,
pushMessage
:
PushMessage
,
grouped
:
Boolean
=
false
):
PendingIntent
{
val
notificationIntent
=
context
.
changeServerIntent
(
pushMessage
.
info
.
host
,
chatRoomId
=
pushMessage
.
info
.
roomId
)
// TODO - add support to go directly to the chatroom
/*if (!isGrouped) {
notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId)
}*/
val
roomId
=
if
(!
grouped
)
pushMessage
.
info
.
roomId
else
null
val
notificationIntent
=
context
.
changeServerIntent
(
pushMessage
.
info
.
host
,
chatRoomId
=
roomId
)
return
PendingIntent
.
getActivity
(
context
,
random
.
nextInt
(),
notificationIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
)
}
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
View file @
ee16b416
...
...
@@ -22,6 +22,7 @@ import chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Myself
import
chat.rocket.core.model.Room
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.channels.Channel
...
...
@@ -45,6 +46,7 @@ class ConnectionManager(
private
val
roomMessagesChannels
=
LinkedHashMap
<
String
,
Channel
<
Message
>>()
private
val
userDataChannels
=
ArrayList
<
Channel
<
Myself
>>()
private
val
roomsChannels
=
LinkedHashMap
<
String
,
Channel
<
Room
>>()
private
val
subscriptionIdMap
=
HashMap
<
String
,
String
>()
private
var
subscriptionId
:
String
?
=
null
...
...
@@ -127,6 +129,18 @@ class ConnectionManager(
maxSize
=
10
)
{
batch
->
Timber
.
d
(
"processing Stream batch: ${batch.size} - $batch"
)
dbManager
.
processChatRoomsBatch
(
batch
)
batch
.
forEach
{
//TODO - Do we need to handle Type.Removed and Type.Inserted here?
if
(
it
.
type
==
Type
.
Updated
)
{
if
(
it
.
data
is
Room
)
{
val
room
=
it
.
data
as
Room
roomsChannels
[
it
.
data
.
id
]
?.
let
{
channel
->
channel
.
offer
(
room
)
}
}
}
}
}
val
messagesActor
=
createBatchActor
<
Message
>(
messagesContext
,
parent
=
connectJob
,
...
...
@@ -241,6 +255,14 @@ class ConnectionManager(
fun
removeUserDataChannel
(
channel
:
Channel
<
Myself
>)
=
userDataChannels
.
remove
(
channel
)
fun
addRoomChannel
(
roomId
:
String
,
channel
:
Channel
<
Room
>)
{
roomsChannels
[
roomId
]
=
channel
}
fun
removeRoomChannel
(
roomId
:
String
)
{
roomsChannels
.
remove
(
roomId
)
}
fun
subscribeRoomMessages
(
roomId
:
String
,
channel
:
Channel
<
Message
>)
{
val
oldSub
=
roomMessagesChannels
.
put
(
roomId
,
channel
)
if
(
oldSub
!=
null
)
{
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/DatabaseMessageMapper.kt
View file @
ee16b416
...
...
@@ -7,6 +7,7 @@ import chat.rocket.android.db.model.FullMessage
import
chat.rocket.android.db.model.ReactionEntity
import
chat.rocket.android.db.model.UrlEntity
import
chat.rocket.android.db.model.UserEntity
import
chat.rocket.android.util.retryDB
import
chat.rocket.common.model.SimpleRoom
import
chat.rocket.common.model.SimpleUser
import
chat.rocket.core.model.Message
...
...
@@ -135,14 +136,18 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
with
(
attachment
)
{
val
fields
=
if
(
hasFields
)
{
withContext
(
CommonPool
)
{
retryDB
(
"getAttachmentFields(${attachment._id})"
)
{
dbManager
.
messageDao
().
getAttachmentFields
(
attachment
.
_id
)
}
}.
map
{
Field
(
it
.
title
,
it
.
value
)
}
}
else
{
null
}
val
actions
=
if
(
hasActions
)
{
withContext
(
CommonPool
)
{
retryDB
(
"getAttachmentActions(${attachment._id})"
)
{
dbManager
.
messageDao
().
getAttachmentActions
(
attachment
.
_id
)
}
}.
mapNotNull
{
mapAction
(
it
)
}
}
else
{
null
...
...
@@ -183,29 +188,6 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
return
list
}
/*private suspend fun mapColorAttachmentWithFields(entity: AttachmentEntity): ColorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(entity._id)
}.map { Field(it.title, it.value) }
return with(entity) {
ColorAttachment(
color = Color.Custom(color ?: DEFAULT_COLOR_STR),
text = text ?: "",
fallback = fallback,
fields = fields)
}
}
private suspend fun mapActionAttachment(attachment: AttachmentEntity): ActionsAttachment {
val actions = withContext(CommonPool) {
dbManager.messageDao().getAttachmentActions(attachment._id)
}.mapNotNull { mapAction(it) }
return with(attachment) {
// TODO - remove the default "vertical" value from here...
ActionsAttachment(title, actions, buttonAlignment ?: "vertical")
}
}*/
private
fun
mapAction
(
action
:
AttachmentActionEntity
):
Action
?
{
return
when
(
action
.
type
)
{
"button"
->
ButtonAction
(
action
.
type
,
action
.
text
,
action
.
url
,
action
.
isWebView
,
...
...
@@ -214,13 +196,4 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
else
->
null
}
}
/*private suspend fun mapAuthorAttachment(attachment: AttachmentEntity): AuthorAttachment {
val fields = withContext(CommonPool) {
dbManager.messageDao().getAttachmentFields(attachment._id)
}.map { Field(it.title, it.value) }
return with(attachment) {
AuthorAttachment(authorLink!!, authorIcon, authorName, fields)
}
}*/
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/DatabaseMessagesRepository.kt
View file @
ee16b416
...
...
@@ -4,6 +4,7 @@ import chat.rocket.android.db.DatabaseManager
import
chat.rocket.android.db.Operation
import
chat.rocket.android.db.model.MessagesSync
import
chat.rocket.android.server.domain.MessagesRepository
import
chat.rocket.android.util.retryDB
import
chat.rocket.core.model.Message
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.withContext
...
...
@@ -14,26 +15,32 @@ class DatabaseMessagesRepository(
)
:
MessagesRepository
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
retryDB
(
"getMessageById($id)"
)
{
dbManager
.
messageDao
().
getMessageById
(
id
)
?.
let
{
message
->
mapper
.
map
(
message
)
}
}
}
override
suspend
fun
getByRoomId
(
roomId
:
String
):
List
<
Message
>
=
withContext
(
CommonPool
)
{
// FIXME - investigate how to avoid this distinctBy here, since DAO is returning a lot of
// duplicate rows (something related to our JOINS and relations on Room)
retryDB
(
"getMessagesByRoomId($roomId)"
)
{
dbManager
.
messageDao
().
getMessagesByRoomId
(
roomId
)
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
messages
->
mapper
.
map
(
messages
)
}
}
}
override
suspend
fun
getRecentMessages
(
roomId
:
String
,
count
:
Long
):
List
<
Message
>
=
withContext
(
CommonPool
)
{
retryDB
(
"getRecentMessagesByRoomId($roomId, $count)"
)
{
dbManager
.
messageDao
().
getRecentMessagesByRoomId
(
roomId
,
count
)
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
messages
->
mapper
.
map
(
messages
)
}
}
}
override
suspend
fun
save
(
message
:
Message
)
{
dbManager
.
processMessagesBatch
(
listOf
(
message
)).
join
()
...
...
@@ -45,27 +52,33 @@ class DatabaseMessagesRepository(
override
suspend
fun
removeById
(
id
:
String
)
{
withContext
(
CommonPool
)
{
dbManager
.
messageDao
().
delete
(
id
)
retryDB
(
"delete($id)"
)
{
dbManager
.
messageDao
().
delete
(
id
)
}
}
}
override
suspend
fun
removeByRoomId
(
roomId
:
String
)
{
withContext
(
CommonPool
)
{
retryDB
(
"deleteByRoomId($roomId)"
)
{
dbManager
.
messageDao
().
deleteByRoomId
(
roomId
)
}
}
}
override
suspend
fun
getAllUnsent
():
List
<
Message
>
=
withContext
(
CommonPool
)
{
retryDB
(
"getUnsentMessages"
)
{
dbManager
.
messageDao
().
getUnsentMessages
()
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
mapper
.
map
(
it
)
}
}
}
override
suspend
fun
saveLastSyncDate
(
roomId
:
String
,
timeMillis
:
Long
)
{
dbManager
.
sendOperation
(
Operation
.
SaveLastSync
(
MessagesSync
(
roomId
,
timeMillis
)))
}
override
suspend
fun
getLastSyncDate
(
roomId
:
String
):
Long
?
=
withContext
(
CommonPool
)
{
retryDB
(
"getLastSync($roomId)"
)
{
dbManager
.
messageDao
().
getLastSync
(
roomId
)
?.
let
{
it
.
timestamp
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/settings/password/presentation/PasswordPresenter.kt
View file @
ee16b416
package
chat.rocket.android.settings.password.presentation
import
chat.rocket.android.analytics.AnalyticsManager
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
...
...
@@ -14,6 +15,7 @@ import javax.inject.Inject
class
PasswordPresenter
@Inject
constructor
(
private
val
view
:
PasswordView
,
private
val
strategy
:
CancelStrategy
,
private
val
analyticsManager
:
AnalyticsManager
,
serverInteractor
:
GetCurrentServerInteractor
,
factory
:
RocketChatClientFactory
)
{
...
...
@@ -30,10 +32,12 @@ class PasswordPresenter @Inject constructor(
client
.
updateProfile
(
me
.
id
,
null
,
null
,
password
,
null
)
}
analyticsManager
.
logResetPassword
(
true
)
view
.
showPasswordSuccessfullyUpdatedMessage
()
view
.
hideLoading
()
}
catch
(
exception
:
RocketChatException
)
{
analyticsManager
.
logResetPassword
(
false
)
view
.
showPasswordFailsUpdateMessage
(
exception
.
message
)
}
finally
{
view
.
hideLoading
()
}
}
...
...
app/src/main/java/chat/rocket/android/settings/ui/SettingsFragment.kt
View file @
ee16b416
...
...
@@ -8,6 +8,7 @@ import android.view.View
import
android.view.ViewGroup
import
android.widget.AdapterView
import
androidx.appcompat.app.AppCompatActivity
import
androidx.core.net.toUri
import
androidx.fragment.app.Fragment
import
chat.rocket.android.R
import
chat.rocket.android.about.ui.AboutFragment
...
...
@@ -72,25 +73,35 @@ class SettingsFragment : Fragment(), SettingsView, AdapterView.OnItemClickListen
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
1
]
->
activity
?.
startActivity
(
Intent
(
activity
,
PasswordActivity
::
class
.
java
))
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
2
]
->
{
(
activity
as
AppCompatActivity
).
addFragmentBackStack
(
TAG_ABOUT_FRAGMENT
,
R
.
id
.
fragment_container
)
{
AboutFragment
.
newInstance
()
}
}
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
2
]
->
shareApp
()
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
3
]
->
showAppOnStore
()
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
3
]
->
shareApp
()
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
4
]
->
contactSupport
()
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
4
]
->
activity
?.
startActivity
(
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
5
]
->
activity
?.
startActivity
(
context
?.
webViewIntent
(
getString
(
R
.
string
.
license_url
),
getString
(
R
.
string
.
title_licence
)
)
)
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
5
]
->
contactSupport
()
resources
.
getStringArray
(
R
.
array
.
settings_actions
)[
6
]
->
{
(
activity
as
AppCompatActivity
).
addFragmentBackStack
(
TAG_ABOUT_FRAGMENT
,
R
.
id
.
fragment_container
)
{
AboutFragment
.
newInstance
()
}
}
}
}
private
fun
showAppOnStore
()
{
try
{
startActivity
(
Intent
(
Intent
.
ACTION_VIEW
,
getString
(
R
.
string
.
market_link
).
toUri
()))
}
catch
(
error
:
ActivityNotFoundException
)
{
startActivity
(
Intent
(
Intent
.
ACTION_VIEW
,
getString
(
R
.
string
.
play_store_link
).
toUri
()))
}
}
...
...
app/src/main/java/chat/rocket/android/util/IO.kt
View file @
ee16b416
package
chat.rocket.android.util
import
android.database.sqlite.SQLiteDatabaseLockedException
import
chat.rocket.common.RocketChatNetworkErrorException
import
kotlinx.coroutines.experimental.TimeoutCancellationException
import
kotlinx.coroutines.experimental.delay
...
...
@@ -8,6 +9,7 @@ import timber.log.Timber
import
kotlin.coroutines.experimental.coroutineContext
const
val
DEFAULT_RETRY
=
3
private
const
val
DEFAULT_DB_RETRY
=
5
suspend
fun
<
T
>
retryIO
(
description
:
String
=
"<missing description>"
,
...
...
@@ -35,3 +37,30 @@ suspend fun <T> retryIO(
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled"
)
return
block
()
// last attempt
}
suspend
fun
<
T
>
retryDB
(
description
:
String
=
"<missing description>"
,
times
:
Int
=
DEFAULT_DB_RETRY
,
initialDelay
:
Long
=
100
,
// 0.1 second
maxDelay
:
Long
=
500
,
// 0.5 second
factor
:
Double
=
1.2
,
block
:
suspend
()
->
T
):
T
{
var
currentDelay
=
initialDelay
repeat
(
times
-
1
)
{
currentTry
->
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled"
)
try
{
return
block
()
}
catch
(
e
:
SQLiteDatabaseLockedException
)
{
Timber
.
d
(
e
,
"failed call($currentTry): $description"
)
e
.
printStackTrace
()
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled"
)
delay
(
currentDelay
)
currentDelay
=
(
currentDelay
*
factor
).
toLong
().
coerceAtMost
(
maxDelay
)
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled"
)
return
block
()
// last attempt
}
\ No newline at end of file
app/src/main/res/layout/emoji_image_row_item.xml
0 → 100644
View file @
ee16b416
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:layout_gravity=
"center"
>
<ImageView
android:id=
"@+id/emoji_image_view"
android:layout_width=
"32dp"
android:layout_height=
"32dp"
android:layout_gravity=
"center"
tools:src=
"@tools:sample/avatars"
/>
</FrameLayout>
app/src/main/res/layout/emoji_row_item.xml
0 → 100644
View file @
ee16b416
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/emoji_view"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:foreground=
"?selectableItemBackground"
android:layout_gravity=
"center"
android:gravity=
"center"
android:textColor=
"#000000"
android:textSize=
"24sp"
tools:text=
"😀"
/>
app/src/main/res/layout/reaction_praises_list_item.xml
0 → 100644
View file @
ee16b416
<?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=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"@color/colorWhite"
android:padding=
"16dp"
>
<ViewFlipper
android:id=
"@+id/view_flipper"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:foregroundGravity=
"center"
app:layout_constraintTop_toTopOf=
"parent"
>
<include
layout=
"@layout/emoji_row_item"
/>
<include
layout=
"@layout/emoji_image_row_item"
/>
</ViewFlipper>
<TextView
android:id=
"@+id/text_view_usernames"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:fontFamily=
"sans-serif-light"
android:textColor=
"@color/darkGray"
android:textSize=
"14sp"
app:layout_constraintBottom_toBottomOf=
"@+id/view_flipper"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@+id/view_flipper"
app:layout_constraintTop_toTopOf=
"@+id/view_flipper"
tools:text=
"Ann reacted with :grin:"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
app/src/main/res/values-de/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Einstellungen
</string>
<string
name=
"title_preferences"
>
Preferences
</string>
<!-- TODO Add translation -->
<string
name=
"title_change_password"
>
Ändere Passwort
</string>
<string
name=
"title_rate_us"
>
Bewerten Sie uns
</string>
<string
name=
"title_admin_panel"
>
Admin panel
</string>
<!-- TODO Add translation -->
<string
name=
"title_password"
>
Ändere Passwort
</string>
<string
name=
"title_update_profile"
>
Update Profil
</string>
...
...
@@ -58,11 +59,12 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Ändere Passwort
</item>
<item
name=
"item_password"
>
Über
</item>
<item
name=
"item_share_app"
>
App teilen
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -151,6 +153,13 @@
<string
name=
"msg_continue_with_wordpress"
>
Continue with
<b>
WordPress
</b></string>
<!-- TODO Add translation -->
<string
name=
"msg_two_factor_authentication"
>
Two-factor Authentication
</string>
<!-- TODO Add translation -->
<string
name=
"msg__your_2fa_code"
>
What’s your 2FA code?
</string>
<!-- TODO Add translation -->
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Privat
</string>
...
...
app/src/main/res/values-es/strings.xml
View file @
ee16b416
...
...
@@ -15,6 +15,7 @@
<string
name=
"title_settings"
>
Configuraciones
</string>
<string
name=
"title_preferences"
>
Preferences
</string>
<!-- TODO Add translation -->
<string
name=
"title_change_password"
>
Cambia la contraseña
</string>
<string
name=
"title_rate_us"
>
Nos califica
</string>
<string
name=
"title_admin_panel"
>
Admin panel
</string>
<!-- TODO Add translation -->
<string
name=
"title_password"
>
Cambia la contraseña
</string>
<string
name=
"title_update_profile"
>
Actualización del perfil
</string>
...
...
@@ -57,11 +58,12 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Cambia la contraseña
</item>
<item
name=
"item_password"
>
Acerca de
</item>
<item
name=
"item_share_app"
>
Compartir aplicación
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -168,6 +170,11 @@
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Preferences messages -->
<string
name=
"msg_analytics_tracking"
>
Analytics tracking
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values-fr/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Paramètres
</string>
<string
name=
"title_preferences"
>
Préférences
</string>
<string
name=
"title_change_password"
>
Changer le mot de passe
</string>
<string
name=
"title_rate_us"
>
évaluez nous
</string>
<string
name=
"title_admin_panel"
>
Administration
</string>
<string
name=
"title_password"
>
Changer le mot de passe
</string>
<string
name=
"title_update_profile"
>
Mettre à jour le profil
</string>
...
...
@@ -57,12 +58,13 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Préférences
</item>
<item
name=
"item_password"
>
Changer le mot de passe
</item>
<item
name=
"item_password"
>
À propos
</item>
<item
name=
"item_share_app"
>
Partager l\'application
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -160,6 +162,11 @@
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Privé
</string>
...
...
app/src/main/res/values-hi-rIN/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
सेटिंग्स
</string>
<string
name=
"title_preferences"
>
प्राथमिकताएँ
</string>
<string
name=
"title_change_password"
>
पासवर्ड बदलें
</string>
<string
name=
"title_rate_us"
>
हमें रेटिंग दें
</string>
<string
name=
"title_admin_panel"
>
एडमिन पैनल
</string>
<string
name=
"title_password"
>
पासवर्ड बदलें
</string>
<string
name=
"title_update_profile"
>
प्रोफ़ाइल अपडेट करें
</string>
...
...
@@ -57,12 +58,13 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
प्राथमिकताएँ
</item>
<item
name=
"item_password"
>
पासवर्ड बदलें
</item>
<item
name=
"item_password"
>
परिचय
</item>
<item
name=
"item_share_app"
>
ऐप शेयर करें
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -174,6 +176,12 @@
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"few"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"many"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Preferences messages -->
<string
name=
"msg_analytics_tracking"
>
एनालिटिक्स ट्रैकिंग
</string>
...
...
app/src/main/res/values-it/strings.xml
0 → 100644
View file @
ee16b416
This diff is collapsed.
Click to expand it.
app/src/main/res/values-ja/strings.xml
View file @
ee16b416
...
...
@@ -18,12 +18,14 @@
<string
name=
"title_settings"
>
設定
</string>
<string
name=
"title_preferences"
>
環境設定
</string>
<string
name=
"title_change_password"
>
パスワードの変更
</string>
<string
name=
"title_rate_us"
>
私たちを評価してください
</string>
<string
name=
"title_admin_panel"
>
管理パネル
</string>
<string
name=
"title_password"
>
パスワードの変更
</string>
<string
name=
"title_update_profile"
>
プロフィールの更新
</string>
<string
name=
"title_about"
>
About
</string>
<string
name=
"title_licence"
>
Licence
</string>
<!-- TODO Add translation -->
<string
name=
"title_create_channel"
>
新しいチャネルを作成
</string>
<string
name=
"title_are_you_sure"
>
本気ですか?
</string>
<!-- Actions -->
<string
name=
"action_connect"
>
接続
</string>
...
...
@@ -54,16 +56,17 @@
<string
name=
"action_create_server"
>
新規サーバーを作成
</string>
<string
name=
"action_register"
>
登録
</string>
<string
name=
"action_confirm"
>
確認
</string>
<string
name=
"action_delete_account"
>
Delete account
</string>
<!-- TODO Add translation --
>
<string
name=
"action_delete_account"
>
アカウントを削除する
</string
>
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
環境設定
</item>
<item
name=
"item_password"
>
パスワードの変更
</item>
<item
name=
"item_password"
>
アプリ情報
</item>
<item
name=
"item_share_app"
>
アプリを共有する
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -155,10 +158,14 @@
<string
name=
"msg_continue_with_wordpress"
><b>
WordPress
</b>
でログイン
</string>
<string
name=
"msg_two_factor_authentication"
>
二要素認証
</string>
<string
name=
"msg__your_2fa_code"
>
あなたの 2FA コードは何ですか?
</string>
<string
name=
"msg_view_more"
>
更に表示
</string>
<string
name=
"msg_view_less"
>
隠す
</string>
<string
name=
"msg_muted_on_this_channel"
>
あなたはこのチャンネルでミュートされています
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
プライベート
</string>
...
...
@@ -213,7 +220,6 @@
<string
name=
"action_msg_share"
>
Share
</string>
<string
name=
"action_title_editing"
>
メッセージの編集
</string>
<string
name=
"action_msg_add_reaction"
>
リアクションする
</string>
<!-- TODO - Add proper translation -->
<string
name=
"action_msg_copy_permalink"
>
パーマリンクのコピー
</string>
<!-- Permission messages -->
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Configurações
</string>
<string
name=
"title_preferences"
>
Preferencias
</string>
<string
name=
"title_change_password"
>
Alterar senha
</string>
<string
name=
"title_rate_us"
>
nos avalie
</string>
<string
name=
"title_admin_panel"
>
Painel administrativo
</string>
<string
name=
"title_password"
>
Alterar senha
</string>
<string
name=
"title_update_profile"
>
Editar perfil
</string>
...
...
@@ -57,12 +58,13 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Prefer
e
ncias
</item>
<item
name=
"item_preferences"
>
Prefer
ê
ncias
</item>
<item
name=
"item_password"
>
Alterar senha
</item>
<item
name=
"item_password"
>
Sobre
</item>
<item
name=
"item_share_app"
>
Compartilhe o aplicativo
</item>
<item
name=
"item_share_app_licence"
>
Licença
</item>
<item
name=
"item_share_app"
>
Compartilhar app
</item>
<item
name=
"item_rate_us"
>
Classifique-nos
</item>
<item
name=
"item_contact_us"
>
Contate-nos
</item>
<item
name=
"item_licence"
>
Licença
</item>
<item
name=
"item_about"
>
Sobre
</item>
</string-array>
<!-- Regular information messages -->
...
...
@@ -161,6 +163,11 @@
<string
name=
"msg_permalink_copied"
>
Permalink copiado
</string>
<string
name=
"msg_send_email"
>
Enviar e-mail
</string>
<string
name=
"msg_android_app_support"
>
Suporte ao aplicativo Android
</string>
<string
name=
"msg_muted_on_this_channel"
>
Você está silenciado neste canal
</string>
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reagiu com %2$s
</item>
<item
quantity=
"other"
>
%1$s reagiram com %2$s
</item>
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Privado
</string>
...
...
@@ -189,8 +196,8 @@
<string
name=
"message_welcome"
>
Bem-vindo, %s
</string>
<string
name=
"message_removed"
>
Mensagem removida
</string>
<string
name=
"message_pinned"
>
Pinou uma mensagem:
</string>
<string
name=
"message_muted"
>
Usuário %1$s
entrou no modo mu
do por %2$s
</string>
<string
name=
"message_unmuted"
>
Usuário %1$s saiu do modo
mu
do por %2$s
</string>
<string
name=
"message_muted"
>
Usuário %1$s
foi silencia
do por %2$s
</string>
<string
name=
"message_unmuted"
>
Usuário %1$s saiu do modo
silencia
do por %2$s
</string>
<string
name=
"message_role_add"
>
%1$s foi definido %2$s por %3$s
</string>
<string
name=
"message_role_removed"
>
%1$s não é mais %2$s por %3$s
</string>
// TODO:Add proper translation.
...
...
app/src/main/res/values-ru-rRU/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Настройки
</string>
<string
name=
"title_preferences"
>
Персональные
</string>
<string
name=
"title_change_password"
>
Изменить пароль
</string>
<string
name=
"title_rate_us"
>
оцените нас
</string>
<string
name=
"title_admin_panel"
>
Панель админа
</string>
<string
name=
"title_password"
>
Изменить пароль
</string>
<string
name=
"title_update_profile"
>
Обновить профиль
</string>
...
...
@@ -57,12 +58,13 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Персональные
</item>
<item
name=
"item_password"
>
Изменить пароль
</item>
<item
name=
"item_password"
>
О программе
</item>
<item
name=
"item_share_app"
>
добавить приложение
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -159,6 +161,12 @@
<string
name=
"msg_permalink_copied"
>
Ссылка скопирована
</string>
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"few"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"many"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Приватный
</string>
...
...
app/src/main/res/values-tr/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Ayarlar
</string>
<string
name=
"title_preferences"
>
Tercihler
</string>
<string
name=
"title_change_password"
>
Şifre Değişikliği
</string>
<string
name=
"title_rate_us"
>
Bizi değerlendirin
</string>
<string
name=
"title_admin_panel"
>
Yönetici Paneli
</string>
<string
name=
"title_password"
>
Şifrenizi Değiştirin
</string>
<string
name=
"title_update_profile"
>
Profilinizi Düzenleyin
</string>
...
...
@@ -57,12 +58,13 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Tercihler
</item>
<item
name=
"item_password"
>
Şifre Değiştir
</item>
<item
name=
"item_password"
>
Hakkında
</item>
<item
name=
"item_share_app"
>
uygulamayı Paylaş
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -175,7 +177,11 @@
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Preferences messages -->
<string
name=
"msg_analytics_tracking"
>
İstatistik takibi
</string>
<string
name=
"msg_send_analytics_tracking"
>
Uygulamanın gelişmesine katkıda bulunmak için anonim istatistik bilgisi gönder
</string>
...
...
app/src/main/res/values-uk/strings.xml
View file @
ee16b416
...
...
@@ -16,6 +16,7 @@
<string
name=
"title_settings"
>
Налаштування
</string>
<string
name=
"title_preferences"
>
Персональні
</string>
<string
name=
"title_change_password"
>
Змінити пароль
</string>
<string
name=
"title_rate_us"
>
Оцініть нас
</string>
<string
name=
"title_admin_panel"
>
Панель адміністратора
</string>
<string
name=
"title_password"
>
Змінити пароль
</string>
<string
name=
"title_update_profile"
>
Оновити профіль
</string>
...
...
@@ -58,11 +59,12 @@
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Preferences
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change Password
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
About
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
поділитися прикладом
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_password"
>
Change password
</item>
<!-- TODO Add translation -->
<item
name=
"item_share_app"
>
Share app
</item>
<!-- TODO Add translation -->
<item
name=
"item_rate_us"
>
Rate us
</item>
<!-- TODO Add translation -->
<item
name=
"item_contact_us"
>
Contact us
</item>
<!-- TODO Add translation -->
<item
name=
"item_licence"
>
Licence
</item>
<!-- TODO Add translation -->
<item
name=
"item_about"
>
About
</item>
<!-- TODO Add translation -->
</string-array>
<!-- Regular information messages -->
...
...
@@ -158,6 +160,12 @@
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_send_email"
>
Send email
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<!-- TODO - Add proper translation -->
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- TODO - Add proper translation -->
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"few"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
<item
quantity=
"many"
>
%1$s reacted with %2$s
</item>
<!-- TODO - Add proper translation -->
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Приватний
</string>
...
...
app/src/main/res/values/defaults.xml
View file @
ee16b416
...
...
@@ -5,5 +5,6 @@
<string
name=
"community_server_url"
translatable=
"false"
>
open.rocket.chat
</string>
<string
name=
"create_server_url"
translatable=
"false"
>
cloud.rocket.chat/trial
</string>
<string
name=
"play_store_link"
translatable=
"false"
>
https://play.google.com/store/apps/details?id=chat.rocket.android
</string>
<string
name=
"market_link"
translatable=
"false"
>
market://details?id=chat.rocket.android
</string>
<string
name=
"license_url"
translatable=
"false"
>
https://github.com/RocketChat/Rocket.Chat.Android/blob/develop/LICENSE
</string>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
ee16b416
...
...
@@ -28,6 +28,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"title_settings"
>
Settings
</string>
<string
name=
"title_preferences"
>
Preferences
</string>
<string
name=
"title_change_password"
>
Change Password
</string>
<string
name=
"title_rate_us"
>
Rate Us
</string>
<string
name=
"title_admin_panel"
>
Admin panel
</string>
<string
name=
"title_password"
>
Change Password
</string>
<string
name=
"title_update_profile"
>
Update profile
</string>
...
...
@@ -70,11 +71,12 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
<item
name=
"item_preferences"
>
Preferences
</item>
<item
name=
"item_password"
>
Change Password
</item>
<item
name=
"item_password"
>
About
</item>
<item
name=
"item_share_app"
>
Share App
</item>
<item
name=
"item_share_app_licence"
>
Licence
</item>
<item
name=
"item_password"
>
Change password
</item>
<item
name=
"item_share_app"
>
Share app
</item>
<item
name=
"item_rate_us"
>
Rate us
</item>
<item
name=
"item_contact_us"
>
Contact us
</item>
<item
name=
"item_licence"
>
Licence
</item>
<item
name=
"item_about"
>
About
</item>
</string-array>
<!-- Regular information messages -->
...
...
@@ -171,6 +173,10 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"msg_permalink_copied"
>
Permalink copied
</string>
<string
name=
"msg_send_email"
>
Send email
</string>
<string
name=
"msg_android_app_support"
>
Android app support
</string>
<plurals
name=
"msg_reacted_with_"
>
<item
quantity=
"one"
>
%1$s reacted with %2$s
</item>
<item
quantity=
"other"
>
%1$s reacted with %2$s
</item>
</plurals>
<!-- Create channel messages -->
<string
name=
"msg_private_channel"
>
Private
</string>
...
...
@@ -188,6 +194,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"msg_delete_description"
>
Are you sure you want to delete this message
</string>
<string
name=
"msg_view_more"
>
view more
</string>
<string
name=
"msg_view_less"
>
view less
</string>
<string
name=
"msg_muted_on_this_channel"
>
You are muted on this channel
</string>
<!-- Preferences messages -->
<string
name=
"msg_analytics_tracking"
>
Analytics tracking
</string>
...
...
app/src/play/java/chat/rocket/android/analytics/AnswersAnalytics.kt
View file @
ee16b416
...
...
@@ -67,4 +67,11 @@ class AnswersAnalytics : Analytics {
)
override
fun
logOpenAdmin
()
=
Answers
.
getInstance
().
logCustom
(
CustomEvent
(
"open_admin"
))
override
fun
logResetPassword
(
resetPasswordSucceeded
:
Boolean
)
=
Answers
.
getInstance
()
.
logCustom
(
CustomEvent
(
"reset_password"
)
.
putCustomAttribute
(
"resetPasswordSucceeded"
,
resetPasswordSucceeded
.
toString
())
)
}
app/src/play/java/chat/rocket/android/analytics/GoogleAnalyticsForFirebase.kt
View file @
ee16b416
...
...
@@ -60,4 +60,9 @@ class GoogleAnalyticsForFirebase @Inject constructor(val context: Context) :
}
override
fun
logOpenAdmin
()
=
firebaseAnalytics
.
logEvent
(
"open_admin"
,
null
)
override
fun
logResetPassword
(
resetPasswordSucceeded
:
Boolean
)
=
firebaseAnalytics
.
logEvent
(
"reset_password"
,
Bundle
(
1
).
apply
{
putBoolean
(
"resetPasswordSucceeded"
,
resetPasswordSucceeded
)
})
}
app/src/play/java/chat/rocket/android/push/FirebaseMessagingService.kt
View file @
ee16b416
package
chat.rocket.android.push
import
android.os.Bundle
import
androidx.core.os.bundleOf
import
androidx.work.Constraints
import
androidx.work.NetworkType
...
...
@@ -23,9 +24,12 @@ class FirebaseMessagingService : FirebaseMessagingService() {
}
override
fun
onMessageReceived
(
message
:
RemoteMessage
)
{
// XXX - for now this is ok, if we start to do network calls, use a Worker instead
message
.
data
?.
let
{
pushManager
.
handle
(
bundleOf
(*(
it
.
map
{
Pair
(
it
.
key
,
it
.
value
)
}).
toTypedArray
()))
message
.
data
?.
let
{
data
->
val
bundle
=
Bundle
()
data
.
entries
.
forEach
{
entry
->
bundle
.
putString
(
entry
.
key
,
entry
.
value
)
}
pushManager
.
handle
(
bundle
)
}
}
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
View file @
ee16b416
...
...
@@ -10,7 +10,6 @@ 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
...
...
@@ -22,6 +21,7 @@ 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
import
kotlinx.android.synthetic.main.dialog_skin_tone_chooser.view.*
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.launch
...
...
@@ -77,59 +77,50 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
}
private
fun
showSkinToneChooser
()
{
val
view
=
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
color_select_popup
,
null
)
val
view
=
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
dialog_skin_tone_chooser
,
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
(
context
,
it
.
text
)
}.
setOnClickListener
{
with
(
view
)
{
image_view_default_tone
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
Default
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
light_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
context
,
it
.
text
)
}.
setOnClickListener
{
image_view_light_tone
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
LightTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_light_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
context
,
it
.
text
)
}.
setOnClickListener
{
image_view_medium_light
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumLightTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
context
,
it
.
text
)
}.
setOnClickListener
{
image_view_medium_tone
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
medium_dark_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
context
,
it
.
text
)
}.
setOnClickListener
{
image_view_medium_dark_tone
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumDarkTone
)
}
view
.
findViewById
<
TextView
>(
R
.
id
.
dark_tone_text
).
also
{
it
.
text
=
EmojiParser
.
parse
(
context
,
it
.
text
)
}.
setOnClickListener
{
image_view_dark_tone
.
setOnClickListener
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
DarkTone
)
}
}
dialog
.
show
()
}
private
fun
changeSkinTone
(
tone
:
Fitzpatrick
)
{
val
drawable
=
ContextCompat
.
getDrawable
(
context
,
R
.
drawable
.
color_change_circl
e
)
!!
val
drawable
=
ContextCompat
.
getDrawable
(
context
,
R
.
drawable
.
bg_skin_ton
e
)
!!
val
wrappedDrawable
=
DrawableCompat
.
wrap
(
drawable
)
DrawableCompat
.
setTint
(
wrappedDrawable
,
getFitzpatrickColor
(
tone
))
(
changeColorView
as
ImageView
).
setImageDrawable
(
wrappedDrawable
)
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiReactionListener.kt
View file @
ee16b416
...
...
@@ -16,4 +16,14 @@ interface EmojiReactionListener {
* @param emojiShortname The shortname of the emoji (:grin:, :smiley:, etc).
*/
fun
onReactionTouched
(
messageId
:
String
,
emojiShortname
:
String
)
/**
* Callback when an added reaction is long-clicked.
*
* @param shortname The shortname of the emoji (:grin:, :smiley:, etc).
* @param isCustom Whether the reaction is custom or one of the defaults.
* @param url In case of a custom emoji, this is the url to find it. Can be null if not a custom.
* @param usernames The list of usernames of users who added the reaction.
*/
fun
onReactionLongClicked
(
shortname
:
String
,
isCustom
:
Boolean
,
url
:
String
?,
usernames
:
List
<
String
>)
}
emoji/src/main/res/drawable/
color_change_circl
e.xml
→
emoji/src/main/res/drawable/
bg_skin_ton
e.xml
View file @
ee16b416
<?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"
/>
...
...
emoji/src/main/res/layout/
color_select_popup
.xml
→
emoji/src/main/res/layout/
dialog_skin_tone_chooser
.xml
View file @
ee16b416
<?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_width=
"match_parent"
android:layout_height=
"wrap_content"
>
<
Text
View
android:id=
"@+id/
default_tone_text
"
<
Image
View
android:id=
"@+id/
image_view_default_tone
"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"24dp"
android:layout_marginTop=
"16dp"
android:text=
"👌"
android:textSize=
"32sp"
android:layout_marginBottom=
"16dp"
android:tint=
"@color/tone_default"
app:layout_constraintEnd_toStartOf=
"@+id/light_tone_text"
app:layout_constraintBottom_toTopOf=
"@+id/image_view_medium_tone"
app:layout_constraintEnd_toStartOf=
"@+id/image_view_light_tone"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintHorizontal_chainStyle=
"
packed
"
app:layout_constraintHorizontal_chainStyle=
"
spread_inside
"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/
color_change_circl
e"
/>
app:srcCompat=
"@drawable/
bg_skin_ton
e"
/>
<
Text
View
android:id=
"@+id/
light_tone_text
"
<
Image
View
android:id=
"@+id/
image_view_light_tone
"
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_tex
t"
app:layout_constraintEnd_toStartOf=
"@+id/
image_view_medium_ligh
t"
app:layout_constraintHorizontal_bias=
"0.5"
app:layout_constraintStart_toEndOf=
"@+id/
default_tone_text
"
app:layout_constraintStart_toEndOf=
"@+id/
image_view_default_tone
"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
app:srcCompat=
"@drawable/bg_skin_tone"
/>
<
Text
View
android:id=
"@+id/
medium_light_tex
t"
<
Image
View
android:id=
"@+id/
image_view_medium_ligh
t"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginTop=
"16dp"
android:text=
"👌🏼"
android:textSize=
"32sp"
android:layout_marginEnd=
"24dp"
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_constraintStart_toEndOf=
"@+id/
image_view_light_tone
"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/color_change_circle"
tools:text=
"👌"
/>
app:srcCompat=
"@drawable/bg_skin_tone"
/>
<
Text
View
android:id=
"@+id/
medium_tone_text
"
<
Image
View
android:id=
"@+id/
image_view_medium_tone
"
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=
"👌"
/>
app:layout_constraintEnd_toStartOf=
"@+id/image_view_medium_dark_tone"
app:layout_constraintHorizontal_chainStyle=
"spread_inside"
app:layout_constraintStart_toStartOf=
"@+id/image_view_default_tone"
app:layout_constraintTop_toBottomOf=
"@+id/image_view_default_tone"
app:srcCompat=
"@drawable/bg_skin_tone"
/>
<
Text
View
android:id=
"@+id/
medium_dark_tone_text
"
<
Image
View
android:id=
"@+id/
image_view_medium_dark_tone
"
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_constraintBottom_toBottomOf=
"
@+id/image_view_medium_tone
"
app:layout_constraintEnd_toStartOf=
"@+id/
image_view_dark_tone
"
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=
"👌"
/>
app:layout_constraintStart_toEndOf=
"@+id/image_view_medium_tone"
app:layout_constraintTop_toTopOf=
"@+id/image_view_medium_tone"
app:srcCompat=
"@drawable/bg_skin_tone"
/>
<
Text
View
android:id=
"@+id/
dark_tone_text
"
<
Image
View
android:id=
"@+id/
image_view_dark_tone
"
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=
"👌"
/>
app:layout_constraintBottom_toBottomOf=
"@+id/image_view_medium_dark_tone"
app:layout_constraintEnd_toEndOf=
"@+id/image_view_medium_light"
app:layout_constraintStart_toEndOf=
"@+id/image_view_medium_dark_tone"
app:layout_constraintTop_toTopOf=
"@+id/image_view_medium_dark_tone"
app:srcCompat=
"@drawable/bg_skin_tone"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
emoji/src/main/res/layout/emoji_keyboard.xml
View file @
ee16b416
...
...
@@ -43,7 +43,7 @@
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:srcCompat=
"@drawable/
color_change_circl
e"
/>
app:srcCompat=
"@drawable/
bg_skin_ton
e"
/>
<ImageView
android:id=
"@+id/emoji_search"
...
...
emoji/src/main/res/values/strings.xml
View file @
ee16b416
<resources>
<string
name=
"msg_no_recent_emoji"
>
No recent emoji
</string>
<string
name=
"alert_title_default_skin_tone"
>
Default skin tone
</string>
<string
name=
"msg_reactions"
translatable=
"false"
>
Reactions
</string>
</resources>
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