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
b9f79237
Unverified
Commit
b9f79237
authored
Nov 24, 2018
by
Lucio Maciel
Committed by
GitHub
Nov 24, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into fix-ghost-composer
parents
8706d0bc
c9147d73
Changes
46
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
46 changed files
with
667 additions
and
326 deletions
+667
-326
file_paths.xml
app/src/debug/res/xml/file_paths.xml
+6
-0
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+10
-1
ServerFragment.kt
...rocket/android/authentication/server/ui/ServerFragment.kt
+37
-8
ChatRoomFragmentModule.kt
...chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
+33
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+112
-78
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+3
-10
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+66
-22
RoomUiModelMapper.kt
...hat/rocket/android/chatrooms/adapter/RoomUiModelMapper.kt
+15
-6
RoomUiModel.kt
...hat/rocket/android/chatrooms/adapter/model/RoomUiModel.kt
+3
-0
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
+22
-1
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+1
-2
DatabaseManager.kt
app/src/main/java/chat/rocket/android/db/DatabaseManager.kt
+43
-37
FavoriteMessagesPresenter.kt
...avoritemessages/presentation/FavoriteMessagesPresenter.kt
+2
-1
FilesPresenter.kt
.../chat/rocket/android/files/presentation/FilesPresenter.kt
+1
-0
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+0
-3
MembersPresenter.kt
...t/rocket/android/members/presentation/MembersPresenter.kt
+2
-1
PinnedMessagesPresenter.kt
...id/pinnedmessages/presentation/PinnedMessagesPresenter.kt
+2
-1
ProfilePresenter.kt
...t/rocket/android/profile/presentation/ProfilePresenter.kt
+22
-21
ProfileFragment.kt
...in/java/chat/rocket/android/profile/ui/ProfileFragment.kt
+1
-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
IO.kt
app/src/main/java/chat/rocket/android/util/IO.kt
+29
-0
message_attachment_options.xml
app/src/main/res/layout/message_attachment_options.xml
+12
-2
update_avatar_options.xml
app/src/main/res/layout/update_avatar_options.xml
+2
-2
strings.xml
app/src/main/res/values-de/strings.xml
+2
-2
strings.xml
app/src/main/res/values-es/strings.xml
+2
-2
strings.xml
app/src/main/res/values-fr/strings.xml
+2
-2
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+2
-2
strings.xml
app/src/main/res/values-it/strings.xml
+2
-2
strings.xml
app/src/main/res/values-ja/strings.xml
+2
-5
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+2
-2
strings.xml
app/src/main/res/values-ru-rRU/strings.xml
+2
-2
strings.xml
app/src/main/res/values-tr/strings.xml
+2
-2
strings.xml
app/src/main/res/values-uk/strings.xml
+2
-2
strings.xml
app/src/main/res/values/strings.xml
+2
-2
file_paths.xml
app/src/main/res/xml/file_paths.xml
+6
-0
FirebaseMessagingService.kt
...java/chat/rocket/android/push/FirebaseMessagingService.kt
+7
-3
EmojiKeyboardPopup.kt
...main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
+29
-38
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
Image.kt
...src/main/java/chat/rocket/android/util/extension/Image.kt
+18
-0
No files found.
app/src/debug/res/xml/file_paths.xml
0 → 100644
View file @
b9f79237
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name=
"rocket_chat_images"
path=
"Android/data/chat.rocket.android.dev/files/Pictures"
/>
</paths>
\ No newline at end of file
app/src/main/AndroidManifest.xml
View file @
b9f79237
...
...
@@ -105,6 +105,15 @@
<meta-data
android:name=
"io.fabric.ApiKey"
android:value=
"12ac6e94f850aaffcdff52001af77ca415d06a43"
/>
</application>
<provider
android:name=
"androidx.core.content.FileProvider"
android:authorities=
"chat.rocket.android.fileprovider"
android:exported=
"false"
android:grantUriPermissions=
"true"
>
<meta-data
android:name=
"android.support.FILE_PROVIDER_PATHS"
android:resource=
"@xml/file_paths"
/>
</provider>
</application>
</manifest>
app/src/main/java/chat/rocket/android/authentication/server/ui/ServerFragment.kt
View file @
b9f79237
package
chat.rocket.android.authentication.server.ui
import
android.os.Bundle
import
android.text.SpannableStringBuilder
import
android.view.LayoutInflater
import
android.view.View
import
android.view.ViewGroup
...
...
@@ -11,6 +12,7 @@ import android.widget.ScrollView
import
android.widget.Toast
import
androidx.core.content.ContextCompat
import
androidx.core.net.toUri
import
androidx.core.text.color
import
androidx.core.view.ViewCompat
import
androidx.core.view.isVisible
import
androidx.fragment.app.Fragment
...
...
@@ -51,6 +53,8 @@ class ServerFragment : Fragment(), ServerView {
lateinit
var
analyticsManager
:
AnalyticsManager
private
var
deepLinkInfo
:
LoginDeepLinkInfo
?
=
null
private
var
protocol
=
"https://"
private
var
isDomainAppended
=
false
private
var
appendedText
=
""
private
lateinit
var
serverUrlDisposable
:
Disposable
private
val
layoutListener
=
ViewTreeObserver
.
OnGlobalLayoutListener
{
if
(
KeyboardHelper
.
isSoftKeyboardShown
(
scroll_view
.
rootView
))
{
...
...
@@ -131,7 +135,7 @@ class ServerFragment : Fragment(), ServerView {
}
private
fun
setupOnClickListener
()
=
ui
{
_
->
ui
{
button_connect
.
setOnClickListener
{
presenter
.
checkServer
(
"$protocol${text_server_url.textContent.sanitize()}"
)
}
...
...
@@ -244,17 +248,42 @@ class ServerFragment : Fragment(), ServerView {
private
fun
subscribeEditText
()
{
serverUrlDisposable
=
text_server_url
.
asObservable
()
.
filter
{
it
.
isNotBlank
()
}
.
subscribe
{
if
(
"$protocol${it.toString()}"
.
isValidUrl
())
{
enableButtonConnect
()
}
else
{
disableButtonConnect
()
}
}
.
subscribe
{
processUserInput
(
it
.
toString
())
}
}
private
fun
unsubscribeEditText
()
=
serverUrlDisposable
.
dispose
()
private
fun
processUserInput
(
text
:
String
)
{
if
(
text
.
last
().
toString
()
==
"."
&&
!
isDomainAppended
)
{
addDomain
()
}
else
if
(
isDomainAppended
&&
text
!=
appendedText
)
{
removeDomain
()
}
if
(
"$protocol$text"
.
isValidUrl
())
{
enableButtonConnect
()
}
else
{
disableButtonConnect
()
}
}
private
fun
addDomain
()
{
val
cursorPosition
=
text_server_url
.
length
()
text_server_url
.
append
(
SpannableStringBuilder
()
.
color
(
R
.
color
.
colorAuthenticationSecondaryText
)
{
append
(
"rocket.chat"
)
})
text_server_url
.
setSelection
(
cursorPosition
)
appendedText
=
text_server_url
.
text
.
toString
()
isDomainAppended
=
true
}
private
fun
removeDomain
()
{
text_server_url
.
setText
(
text_server_url
.
text
.
toString
().
substring
(
0
,
text_server_url
.
selectionEnd
)
)
text_server_url
.
setSelection
(
text_server_url
.
length
())
isDomainAppended
=
false
}
private
fun
enableUserInput
()
{
enableButtonConnect
()
text_server_url
.
isEnabled
=
true
...
...
app/src/main/java/chat/rocket/android/chatroom/di/ChatRoomFragmentModule.kt
View file @
b9f79237
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 @
b9f79237
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
b9f79237
...
...
@@ -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 @
b9f79237
...
...
@@ -8,8 +8,10 @@ import android.content.Context
import
android.content.Intent
import
android.content.res.Configuration
import
android.graphics.drawable.Drawable
import
android.net.Uri
import
android.os.Bundle
import
android.os.Handler
import
android.provider.MediaStore
import
android.text.SpannableStringBuilder
import
android.view.KeyEvent
import
android.view.LayoutInflater
...
...
@@ -23,6 +25,7 @@ import android.widget.FrameLayout
import
android.widget.ImageView
import
android.widget.TextView
import
androidx.annotation.DrawableRes
import
androidx.core.content.FileProvider
import
androidx.core.text.bold
import
androidx.core.view.isVisible
import
androidx.fragment.app.Fragment
...
...
@@ -48,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
...
...
@@ -73,6 +77,8 @@ import chat.rocket.android.util.extensions.rotateBy
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.textContent
import
chat.rocket.android.util.extensions.ui
import
chat.rocket.android.util.extension.createImageFile
import
chat.rocket.android.util.extensions.getBitmpap
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.core.internal.realtime.socket.model.State
...
...
@@ -86,6 +92,9 @@ import kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.message_attachment_options.*
import
kotlinx.android.synthetic.main.message_composer.*
import
kotlinx.android.synthetic.main.message_list.*
import
timber.log.Timber
import
java.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
...
...
@@ -125,6 +134,7 @@ private const val BUNDLE_CHAT_ROOM_TYPE = "chat_room_type"
private
const
val
BUNDLE_IS_CHAT_ROOM_READ_ONLY
=
"is_chat_room_read_only"
private
const
val
REQUEST_CODE_FOR_PERFORM_SAF
=
42
private
const
val
REQUEST_CODE_FOR_DRAW
=
101
private
const
val
REQUEST_CODE_FOR_PERFORM_CAMERA
=
102
private
const
val
BUNDLE_CHAT_ROOM_LAST_SEEN
=
"chat_room_last_seen"
private
const
val
BUNDLE_CHAT_ROOM_IS_SUBSCRIBED
=
"chat_room_is_subscribed"
private
const
val
BUNDLE_CHAT_ROOM_IS_CREATOR
=
"chat_room_is_creator"
...
...
@@ -196,6 +206,7 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
internal
val
description
by
lazy
{
dialogView
.
findViewById
<
EditText
>(
R
.
id
.
text_file_description
)
}
internal
val
audioVideoAttachment
by
lazy
{
dialogView
.
findViewById
<
FrameLayout
>(
R
.
id
.
audio_video_attachment
)
}
internal
val
textFile
by
lazy
{
dialogView
.
findViewById
<
TextView
>(
R
.
id
.
text_file_name
)
}
private
var
takenPhotoUri
:
Uri
?
=
null
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
...
...
@@ -284,12 +295,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
resultData
:
Intent
?)
{
if
(
result
Data
!=
null
&&
result
Code
==
Activity
.
RESULT_OK
)
{
if
(
resultCode
==
Activity
.
RESULT_OK
)
{
when
(
requestCode
)
{
REQUEST_CODE_FOR_PERFORM_SAF
->
showFileAttachmentDialog
(
resultData
.
data
)
REQUEST_CODE_FOR_DRAW
->
showDrawAttachmentDialog
(
resultData
.
getByteArrayExtra
(
DRAWING_BYTE_ARRAY_EXTRA_DATA
)
)
REQUEST_CODE_FOR_PERFORM_CAMERA
->
takenPhotoUri
?.
let
{
uri
->
uri
.
getBitmpap
(
requireContext
())
?.
let
{
bitmap
->
presenter
.
uploadImage
(
chatRoomId
,
"image/png"
,
uri
,
bitmap
,
""
)
}
}
REQUEST_CODE_FOR_PERFORM_SAF
->
resultData
?.
data
?.
let
{
showFileAttachmentDialog
(
it
)
}
REQUEST_CODE_FOR_DRAW
->
resultData
?.
getByteArrayExtra
(
DRAWING_BYTE_ARRAY_EXTRA_DATA
)
?.
let
{
showDrawAttachmentDialog
(
it
)
}
}
}
}
...
...
@@ -384,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
()
}
...
...
@@ -772,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
)
}
}
...
...
@@ -810,8 +824,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
fun
setupMessageComposer
(
canPost
:
Boolean
)
{
if
(
!
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
(
...
...
@@ -827,6 +841,8 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
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
...
...
@@ -883,7 +899,19 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
hideAttachmentOptions
()
}
button_files
.
setOnClickListener
{
button_add_reaction
.
setOnClickListener
{
_
->
openEmojiKeyboardPopup
()
}
button_take_a_photo
.
setOnClickListener
{
dispatchTakePictureIntent
()
handler
.
postDelayed
({
hideAttachmentOptions
()
},
400
)
}
button_attach_a_file
.
setOnClickListener
{
handler
.
postDelayed
({
presenter
.
selectFile
()
},
200
)
...
...
@@ -893,10 +921,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
},
400
)
}
button_add_reaction
.
setOnClickListener
{
openEmojiKeyboardPopup
()
}
button_drawing
.
setOnClickListener
{
activity
?.
let
{
fragmentActivity
->
if
(!
ImageHelper
.
canWriteToExternalStorage
(
fragmentActivity
))
{
...
...
@@ -914,6 +938,26 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
fun
dispatchTakePictureIntent
()
{
Intent
(
MediaStore
.
ACTION_IMAGE_CAPTURE
).
also
{
takePictureIntent
->
// Create the File where the photo should go
val
photoFile
:
File
?
=
try
{
activity
?.
createImageFile
()
}
catch
(
ex
:
IOException
)
{
Timber
.
e
(
ex
)
null
}
// Continue only if the File was successfully created
photoFile
?.
also
{
takenPhotoUri
=
FileProvider
.
getUriForFile
(
requireContext
(),
"chat.rocket.android.fileprovider"
,
it
)
takePictureIntent
.
putExtra
(
MediaStore
.
EXTRA_OUTPUT
,
takenPhotoUri
)
startActivityForResult
(
takePictureIntent
,
REQUEST_CODE_FOR_PERFORM_CAMERA
)
}
}
}
private
fun
getUnfinishedMessage
()
{
val
unfinishedMessage
=
presenter
.
getUnfinishedMessage
(
chatRoomId
)
if
(
unfinishedMessage
.
isNotBlank
())
{
...
...
app/src/main/java/chat/rocket/android/chatrooms/adapter/RoomUiModelMapper.kt
View file @
b9f79237
...
...
@@ -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
)
{
...
...
@@ -205,4 +214,4 @@ class RoomUiModelMapper(
}
}
}
}
\ No newline at end of file
}
app/src/main/java/chat/rocket/android/chatrooms/adapter/model/RoomUiModel.kt
View file @
b9f79237
...
...
@@ -15,5 +15,8 @@ data class RoomUiModel(
val
lastMessage
:
CharSequence
?
=
null
,
val
status
:
UserStatus
?
=
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 @
b9f79237
...
...
@@ -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 @
b9f79237
...
...
@@ -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 @
b9f79237
...
...
@@ -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
{
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
b9f79237
...
...
@@ -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/db/DatabaseManager.kt
View file @
b9f79237
...
...
@@ -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
...
...
@@ -91,12 +92,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
}
fun
logout
()
{
database
.
clearAllTables
()
suspend
fun
logout
()
{
retryDB
(
"clearAllTables"
)
{
database
.
clearAllTables
()
}
}
suspend
fun
getRoom
(
id
:
String
)
=
withContext
(
dbManagerContext
)
{
chatRoomDao
().
get
(
id
)
retryDB
(
"getRoom($id)"
)
{
chatRoomDao
().
get
(
id
)
}
}
fun
processUsersBatch
(
users
:
List
<
User
>)
{
...
...
@@ -151,7 +154,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
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
,
...
...
@@ -335,7 +338,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
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
...
...
@@ -373,7 +376,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
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
)
{
...
...
@@ -539,39 +542,42 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
}
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
)
{
when
(
operation
)
{
is
Operation
.
ClearStatus
->
userDao
().
clearStatus
()
is
Operation
.
UpdateRooms
->
{
Timber
.
d
(
"Running ChatRooms transaction: remove: ${operation.toRemove} - insert: ${operation.toInsert} - update: ${operation.toUpdate}"
)
private
suspend
fun
doOperation
(
operation
:
Operation
)
{
retryDB
(
description
=
"doOperation($operation)"
)
{
when
(
operation
)
{
is
Operation
.
ClearStatus
->
userDao
().
clearStatus
()
is
Operation
.
UpdateRooms
->
{
Timber
.
d
(
"Running ChatRooms transaction: remove: ${operation.toRemove} - insert: ${operation.toInsert} - update: ${operation.toUpdate}"
)
chatRoomDao
().
update
(
operation
.
toInsert
,
operation
.
toUpdate
,
operation
.
toRemove
)
}
is
Operation
.
InsertRooms
->
{
chatRoomDao
().
insertOrReplace
(
operation
.
chatRooms
)
}
is
Operation
.
CleanInsertRooms
->
{
chatRoomDao
().
cleanInsert
(
operation
.
chatRooms
)
}
is
Operation
.
InsertUsers
->
{
val
time
=
measureTimeMillis
{
userDao
().
upsert
(
operation
.
users
)
}
Timber
.
d
(
"Upserted users batch(${operation.users.size}) in $time MS"
)
}
is
Operation
.
InsertUser
->
{
userDao
().
insert
(
operation
.
user
)
}
is
Operation
.
UpsertUser
->
{
userDao
().
upsert
(
operation
.
user
)
}
is
Operation
.
InsertMessages
->
{
messageDao
().
insert
(
operation
.
list
)
}
is
Operation
.
SaveLastSync
->
{
messageDao
().
saveLastSync
(
operation
.
sync
)
}
}.
exhaustive
chatRoomDao
().
update
(
operation
.
toInsert
,
operation
.
toUpdate
,
operation
.
toRemove
)
}
is
Operation
.
InsertRooms
->
{
chatRoomDao
().
insertOrReplace
(
operation
.
chatRooms
)
}
is
Operation
.
CleanInsertRooms
->
{
chatRoomDao
().
cleanInsert
(
operation
.
chatRooms
)
}
is
Operation
.
InsertUsers
->
{
val
time
=
measureTimeMillis
{
userDao
().
upsert
(
operation
.
users
)
}
Timber
.
d
(
"Upserted users batch(${operation.users.size}) in $time MS"
)
}
is
Operation
.
InsertUser
->
{
userDao
().
insert
(
operation
.
user
)
}
is
Operation
.
UpsertUser
->
{
userDao
().
upsert
(
operation
.
user
)
}
is
Operation
.
InsertMessages
->
{
messageDao
().
insert
(
operation
.
list
)
}
is
Operation
.
SaveLastSync
->
{
messageDao
().
saveLastSync
(
operation
.
sync
)
}
}.
exhaustive
}
}
}
...
...
app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt
View file @
b9f79237
...
...
@@ -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
...
...
@@ -36,7 +37,7 @@ class FavoriteMessagesPresenter @Inject constructor(
view
.
showLoading
()
dbManager
.
getRoom
(
roomId
)
?.
let
{
val
favoriteMessages
=
client
.
getFavoriteMessages
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
)
client
.
getFavoriteMessages
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
)
val
messageList
=
mapper
.
map
(
favoriteMessages
.
result
,
asNotReversed
=
true
)
view
.
showFavoriteMessages
(
messageList
)
offset
+=
1
*
30
...
...
app/src/main/java/chat/rocket/android/files/presentation/FilesPresenter.kt
View file @
b9f79237
...
...
@@ -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/main/ui/MainActivity.kt
View file @
b9f79237
...
...
@@ -62,10 +62,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector,
setContentView
(
R
.
layout
.
activity_main
)
refreshPushToken
()
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
println
(
"ChatRoomId: $chatRoomId"
)
presenter
.
clearNotificationsForChatroom
(
chatRoomId
)
presenter
.
connect
()
...
...
app/src/main/java/chat/rocket/android/members/presentation/MembersPresenter.kt
View file @
b9f79237
...
...
@@ -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
...
...
@@ -38,7 +39,7 @@ class MembersPresenter @Inject constructor(
view
.
showLoading
()
dbManager
.
getRoom
(
roomId
)
?.
let
{
val
members
=
client
.
getMembers
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
,
60
)
client
.
getMembers
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
,
60
)
val
memberUiModels
=
mapper
.
mapToUiModelList
(
members
.
result
)
view
.
showMembers
(
memberUiModels
,
members
.
total
)
offset
+=
1
*
60L
...
...
app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt
View file @
b9f79237
...
...
@@ -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
...
...
@@ -36,7 +37,7 @@ class PinnedMessagesPresenter @Inject constructor(
view
.
showLoading
()
dbManager
.
getRoom
(
roomId
)
?.
let
{
val
pinnedMessages
=
client
.
getPinnedMessages
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
)
client
.
getPinnedMessages
(
roomId
,
roomTypeOf
(
it
.
chatRoom
.
type
),
offset
)
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
,
asNotReversed
=
true
)
view
.
showPinnedMessages
(
messageList
)
offset
+=
1
*
30
...
...
app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt
View file @
b9f79237
...
...
@@ -29,7 +29,6 @@ import chat.rocket.core.internal.rest.setAvatar
import
chat.rocket.core.internal.rest.updateProfile
import
kotlinx.coroutines.experimental.DefaultDispatcher
import
kotlinx.coroutines.experimental.withContext
import
java.lang.Exception
import
java.util.*
import
javax.inject.Inject
...
...
@@ -58,20 +57,17 @@ class ProfilePresenter @Inject constructor(
)
{
private
val
serverUrl
=
serverInteractor
.
get
()
!!
private
val
client
:
RocketChatClient
=
factory
.
create
(
serverUrl
)
private
val
myselfId
=
userHelper
.
user
()
?.
id
?:
""
private
var
myselfName
=
userHelper
.
user
()
?.
name
?:
""
private
var
myselfUsername
=
userHelper
.
username
()
?:
""
private
var
myselfEmailAddress
=
userHelper
.
user
()
?.
emails
?.
getOrNull
(
0
)
?.
address
?:
""
private
val
user
=
userHelper
.
user
()
fun
loadUserProfile
()
{
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
view
.
showProfile
(
serverUrl
.
avatarUrl
(
myselfUsername
),
myselfName
,
myselfUsername
,
myselfEmailAddress
serverUrl
.
avatarUrl
(
user
?.
username
?:
""
),
user
?.
name
?:
""
,
user
?.
username
?:
""
,
user
?.
emails
?.
getOrNull
(
0
)
?.
address
?:
""
)
}
catch
(
exception
:
RocketChatException
)
{
view
.
showMessage
(
exception
)
...
...
@@ -85,14 +81,16 @@ class ProfilePresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
retryIO
{
client
.
updateProfile
(
myselfId
,
email
,
name
,
username
)
}
myselfEmailAddress
=
email
myselfName
=
name
myselfUsername
=
username
view
.
showProfileUpdateSuccessfullyMessage
()
loadUserProfile
()
user
?.
id
?.
let
{
id
->
retryIO
{
client
.
updateProfile
(
id
,
email
,
name
,
username
)
}
view
.
showProfileUpdateSuccessfullyMessage
()
view
.
showProfile
(
serverUrl
.
avatarUrl
(
user
.
username
?:
""
),
name
,
username
,
email
)
}
}
catch
(
exception
:
RocketChatException
)
{
exception
.
message
?.
let
{
view
.
showMessage
(
it
)
...
...
@@ -117,7 +115,7 @@ class ProfilePresenter @Inject constructor(
uriInteractor
.
getInputStream
(
uri
)
}
}
view
.
reloadUserAvatar
(
serverUrl
.
avatarUrl
(
myselfUsername
))
user
?.
username
?.
let
{
view
.
reloadUserAvatar
(
it
)
}
}
catch
(
exception
:
RocketChatException
)
{
exception
.
message
?.
let
{
view
.
showMessage
(
it
)
...
...
@@ -144,7 +142,8 @@ class ProfilePresenter @Inject constructor(
byteArray
?.
inputStream
()
}
}
view
.
reloadUserAvatar
(
serverUrl
.
avatarUrl
(
myselfUsername
))
user
?.
username
?.
let
{
view
.
reloadUserAvatar
(
it
)
}
}
catch
(
exception
:
RocketChatException
)
{
exception
.
message
?.
let
{
view
.
showMessage
(
it
)
...
...
@@ -161,8 +160,10 @@ class ProfilePresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
retryIO
{
client
.
resetAvatar
(
myselfId
)
}
view
.
reloadUserAvatar
(
serverUrl
.
avatarUrl
(
myselfUsername
))
user
?.
id
?.
let
{
id
->
retryIO
{
client
.
resetAvatar
(
id
)
}
}
user
?.
username
?.
let
{
view
.
reloadUserAvatar
(
it
)
}
}
catch
(
exception
:
RocketChatException
)
{
exception
.
message
?.
let
{
view
.
showMessage
(
it
)
...
...
app/src/main/java/chat/rocket/android/profile/ui/ProfileFragment.kt
View file @
b9f79237
...
...
@@ -217,7 +217,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
hideUpdateAvatarOptions
()
}
button_take_photo
.
setOnClickListener
{
button_take_
a_
photo
.
setOnClickListener
{
dispatchTakePicture
(
REQUEST_CODE_FOR_PERFORM_CAMERA
)
hideUpdateAvatarOptions
()
}
...
...
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
b9f79237
...
...
@@ -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 @
b9f79237
...
...
@@ -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 @
b9f79237
...
...
@@ -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
)
{
dbManager
.
messageDao
().
getAttachmentFields
(
attachment
.
_id
)
retryDB
(
"getAttachmentFields(${attachment._id})"
)
{
dbManager
.
messageDao
().
getAttachmentFields
(
attachment
.
_id
)
}
}.
map
{
Field
(
it
.
title
,
it
.
value
)
}
}
else
{
null
}
val
actions
=
if
(
hasActions
)
{
withContext
(
CommonPool
)
{
dbManager
.
messageDao
().
getAttachmentActions
(
attachment
.
_id
)
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 @
b9f79237
...
...
@@ -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,25 +15,31 @@ class DatabaseMessagesRepository(
)
:
MessagesRepository
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
dbManager
.
messageDao
().
getMessageById
(
id
)
?.
let
{
message
->
mapper
.
map
(
message
)
}
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)
dbManager
.
messageDao
().
getMessagesByRoomId
(
roomId
)
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
messages
->
mapper
.
map
(
messages
)
}
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
)
{
dbManager
.
messageDao
().
getRecentMessagesByRoomId
(
roomId
,
count
)
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
messages
->
mapper
.
map
(
messages
)
}
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
)
{
...
...
@@ -45,20 +52,24 @@ 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
)
{
dbManager
.
messageDao
().
deleteByRoomId
(
roomId
)
retryDB
(
"deleteByRoomId($roomId)"
)
{
dbManager
.
messageDao
().
deleteByRoomId
(
roomId
)
}
}
}
override
suspend
fun
getAllUnsent
():
List
<
Message
>
=
withContext
(
CommonPool
)
{
dbManager
.
messageDao
().
getUnsentMessages
()
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
mapper
.
map
(
it
)
}
retryDB
(
"getUnsentMessages"
)
{
dbManager
.
messageDao
().
getUnsentMessages
()
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
mapper
.
map
(
it
)
}
}
}
override
suspend
fun
saveLastSyncDate
(
roomId
:
String
,
timeMillis
:
Long
)
{
...
...
@@ -66,6 +77,8 @@ class DatabaseMessagesRepository(
}
override
suspend
fun
getLastSyncDate
(
roomId
:
String
):
Long
?
=
withContext
(
CommonPool
)
{
dbManager
.
messageDao
().
getLastSync
(
roomId
)
?.
let
{
it
.
timestamp
}
retryDB
(
"getLastSync($roomId)"
)
{
dbManager
.
messageDao
().
getLastSync
(
roomId
)
?.
let
{
it
.
timestamp
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/IO.kt
View file @
b9f79237
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>"
,
...
...
@@ -32,6 +34,33 @@ suspend fun <T> retryIO(
currentDelay
=
(
currentDelay
*
factor
).
toLong
().
coerceAtMost
(
maxDelay
)
}
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/message_attachment_options.xml
View file @
b9f79237
...
...
@@ -6,14 +6,24 @@
android:orientation=
"vertical"
>
<Button
android:id=
"@+id/button_files"
android:id=
"@+id/button_take_a_photo"
style=
"?android:attr/borderlessButtonStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:drawablePadding=
"20dp"
android:drawableStart=
"@drawable/ic_photo_camera_black_24dp"
android:gravity=
"start|center"
android:text=
"@string/action_take_a_photo"
/>
<Button
android:id=
"@+id/button_attach_a_file"
style=
"?android:attr/borderlessButtonStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:drawablePadding=
"20dp"
android:drawableStart=
"@drawable/ic_files_24dp"
android:gravity=
"start|center"
android:text=
"@string/action_files"
/>
android:text=
"@string/action_
attach_a_
files"
/>
<Button
android:id=
"@+id/button_drawing"
...
...
app/src/main/res/layout/update_avatar_options.xml
View file @
b9f79237
...
...
@@ -16,14 +16,14 @@
android:text=
"@string/action_select_photo_from_gallery"
/>
<Button
android:id=
"@+id/button_take_photo"
android:id=
"@+id/button_take_
a_
photo"
style=
"?android:attr/borderlessButtonStyle"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:drawableStart=
"@drawable/ic_photo_camera_black_24dp"
android:drawablePadding=
"20dp"
android:gravity=
"start|center"
android:text=
"@string/action_take_photo"
/>
android:text=
"@string/action_take_
a_
photo"
/>
<Button
android:id=
"@+id/button_reset_avatar"
...
...
app/src/main/res/values-de/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Erstelle Raum
</string>
<string
name=
"action_create"
>
Erstelle
</string>
<string
name=
"action_logout"
>
Abmelden
</string>
<string
name=
"action_
files"
>
Dateien
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Bestätige Passwort Änderung
</string>
<string
name=
"action_join_chat"
>
Trete Chat bei
</string>
<string
name=
"action_add_account"
>
Erstelle Account
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Zeichnung
</string>
<string
name=
"action_save_to_gallery"
>
Sichern in Gallerie
</string>
<string
name=
"action_select_photo_from_gallery"
>
Select photo from gallery
</string>
<!-- TODO Add translation -->
<string
name=
"action_take_
photo"
>
Select photo from gal
lery
</string>
<!-- TODO Add translation -->
<string
name=
"action_take_
a_photo"
>
Take a photo from ga
lery
</string>
<!-- TODO Add translation -->
<string
name=
"action_reset_avatar"
>
Reset avatar
</string>
<!-- TODO Add translation -->
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<!-- TODO Add translation -->
<string
name=
"action_join_community"
>
Join in the community
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values-es/strings.xml
View file @
b9f79237
...
...
@@ -34,7 +34,7 @@
<string
name=
"action_create_channel"
>
Crear canal
</string>
<string
name=
"action_create"
>
Create
</string>
<string
name=
"action_logout"
>
Cerrar sesión
</string>
<string
name=
"action_
files"
>
Archivos
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Confirmar cambio de contraseña
</string>
<string
name=
"action_join_chat"
>
Unirse al chat
</string>
<string
name=
"action_add_account"
>
Añadir cuenta
</string>
...
...
@@ -45,7 +45,7 @@
<string
name=
"action_drawing"
>
Dibujo
</string>
<string
name=
"action_save_to_gallery"
>
Guardar en la galería
</string>
<string
name=
"action_select_photo_from_gallery"
>
Select photo from gallery
</string>
<!-- TODO Add translation -->
<string
name=
"action_take_
photo"
>
Select photo from gallery
</string>
<!-- TODO Add translation -->
<string
name=
"action_take_
a_photo"
>
Take a photo
</string>
<!-- TODO Add translation -->
<string
name=
"action_reset_avatar"
>
Reset avatar
</string>
<!-- TODO Add translation -->
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<!-- TODO Add translation -->
<string
name=
"action_join_community"
>
Join in the community
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values-fr/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Créer salon
</string>
<string
name=
"action_create"
>
Créer
</string>
<string
name=
"action_logout"
>
Se déconnecter
</string>
<string
name=
"action_
files"
>
Fichiers
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Confirmer le mot de passe
</string>
<string
name=
"action_join_chat"
>
Rejoignez le chat
</string>
<string
name=
"action_add_account"
>
Ajouter un compte
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Dessin
</string>
<string
name=
"action_save_to_gallery"
>
Sauvegarder vers la gallerie
</string>
<string
name=
"action_select_photo_from_gallery"
>
Sélectionner depuis la gallerie
</string>
<string
name=
"action_take_photo"
>
Prendre une photo
</string>
<string
name=
"action_take_
a_
photo"
>
Prendre une photo
</string>
<string
name=
"action_reset_avatar"
>
Réinitialiser l\'avatar
</string>
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<!-- TODO Add translation -->
<string
name=
"action_join_community"
>
Join in the community
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values-hi-rIN/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
चैनल बनाएं
</string>
<string
name=
"action_create"
>
बनाएं
</string>
<string
name=
"action_logout"
>
लोग आउट करें
</string>
<string
name=
"action_
files"
>
फ़ाइलें
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
पासवर्ड परिवर्तन की पुष्टि करें
</string>
<string
name=
"action_join_chat"
>
चैट में शामिल हों
</string>
<string
name=
"action_add_account"
>
खाता जोड़ो
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_save_to_gallery"
>
गैलरी में सहेजें
</string>
<string
name=
"action_drawing"
>
चित्रकारी
</string>
<string
name=
"action_select_photo_from_gallery"
>
गैलरी से फोटो का चयन करें
</string>
<string
name=
"action_take_photo"
>
फोटो खेचिये
</string>
<string
name=
"action_take_
a_
photo"
>
फोटो खेचिये
</string>
<string
name=
"action_reset_avatar"
>
अवतार रीसेट करें
</string>
<string
name=
"action_connect_server"
>
सर्वर से कनेक्ट करें
</string>
<string
name=
"action_join_community"
>
समुदाय में शामिल हों
</string>
...
...
app/src/main/res/values-it/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Crea canale
</string>
<string
name=
"action_create"
>
Crea
</string>
<string
name=
"action_logout"
>
Disconnettersi
</string>
<string
name=
"action_
files"
>
Documenti
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO ADD translation --
>
<string
name=
"action_confirm_password"
>
Conferma Cambio Password
</string>
<string
name=
"action_join_chat"
>
Iscriviti alla stanza
</string>
<string
name=
"action_add_account"
>
Aggiungi utente
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Disegno
</string>
<string
name=
"action_save_to_gallery"
>
Salva in galleria
</string>
<string
name=
"action_select_photo_from_gallery"
>
Seleziona foto dalla galleria
</string>
<string
name=
"action_take_
photo"
>
Fare foto
</string
>
<string
name=
"action_take_
a_photo"
>
Take a photo
</string>
<!-- TODO ADD translation --
>
<string
name=
"action_reset_avatar"
>
Reimposta avatar
</string>
<string
name=
"action_connect_server"
>
Collegati a un server
</string>
<string
name=
"action_join_community"
>
Entra nella community
</string>
...
...
app/src/main/res/values-ja/strings.xml
View file @
b9f79237
...
...
@@ -26,9 +26,6 @@
<string
name=
"title_create_channel"
>
新しいチャネルを作成
</string>
<string
name=
"title_are_you_sure"
>
本気ですか?
</string>
<!-- Actions -->
<string
name=
"action_connect"
>
接続
</string>
<string
name=
"action_use_this_username"
>
このユーザー名を使用する
</string>
...
...
@@ -40,7 +37,7 @@
<string
name=
"action_create_channel"
>
チャンネル作成
</string>
<string
name=
"action_create"
>
作ります
</string>
<string
name=
"action_logout"
>
ログアウト
</string>
<string
name=
"action_
files"
>
ファイル
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
変更したパスワードの確認
</string>
<string
name=
"action_join_chat"
>
チャットに参加
</string>
<string
name=
"action_add_account"
>
サーバーの追加
</string>
...
...
@@ -51,7 +48,7 @@
<string
name=
"action_drawing"
>
絵を描く
</string>
<string
name=
"action_save_to_gallery"
>
ギャラリーに保存
</string>
<string
name=
"action_select_photo_from_gallery"
>
ギャラリーの写真を選択
</string>
<string
name=
"action_take_photo"
>
写真を撮る
</string>
<string
name=
"action_take_
a_
photo"
>
写真を撮る
</string>
<string
name=
"action_reset_avatar"
>
アバターをリセット
</string>
<string
name=
"action_connect_server"
>
サーバーに接続
</string>
<string
name=
"action_join_community"
>
コミュニティに参加
</string>
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Criar chat
</string>
<string
name=
"action_create"
>
Criar
</string>
<string
name=
"action_logout"
>
Sair
</string>
<string
name=
"action_
files"
>
Arquivos
</string>
<string
name=
"action_
attach_a_files"
>
Anexar um arquivo
</string>
<string
name=
"action_confirm_password"
>
Confirme a nova senha
</string>
<string
name=
"action_join_chat"
>
Entrar no Chat
</string>
<string
name=
"action_add_account"
>
Adicionar conta
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Desenhando
</string>
<string
name=
"action_save_to_gallery"
>
Salvar na galeria
</string>
<string
name=
"action_select_photo_from_gallery"
>
Escolher foto da galeria
</string>
<string
name=
"action_take_
photo"
>
Tirar
foto
</string>
<string
name=
"action_take_
a_photo"
>
Tirar uma
foto
</string>
<string
name=
"action_reset_avatar"
>
Resetar avatar
</string>
<string
name=
"action_connect_server"
>
Conectar com um servidor
</string>
<string
name=
"action_join_community"
>
Junte-se à comunidade
</string>
...
...
app/src/main/res/values-ru-rRU/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Создать канал
</string>
<string
name=
"action_create"
>
Создать
</string>
<string
name=
"action_logout"
>
Выйти
</string>
<string
name=
"action_
files"
>
Файлы
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Подтверждение изменения пароля
</string>
<string
name=
"action_join_chat"
>
Присоединиться к чату
</string>
<string
name=
"action_add_account"
>
Добавить аккаунт
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Рисунок
</string>
<string
name=
"action_save_to_gallery"
>
Сохранить в галерею
</string>
<string
name=
"action_select_photo_from_gallery"
>
Выбрать из галереи
</string>
<string
name=
"action_take_photo"
>
Сделать снимок
</string>
<string
name=
"action_take_
a_
photo"
>
Сделать снимок
</string>
<string
name=
"action_reset_avatar"
>
Восстановить аватар
</string>
<string
name=
"action_connect_server"
>
Соединиться с сервером
</string>
<string
name=
"action_join_community"
>
Сообщество
</string>
...
...
app/src/main/res/values-tr/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Yeni Kanal Oluştur
</string>
<string
name=
"action_create"
>
Oluştur
</string>
<string
name=
"action_logout"
>
Çıkış Yap
</string>
<string
name=
"action_
files"
>
Dosyalar
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Şifre Değişikliğini Onaylayın
</string>
<string
name=
"action_join_chat"
>
Sohbete Bağlan
</string>
<string
name=
"action_add_account"
>
Hesap Ekle
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Çizim
</string>
<string
name=
"action_save_to_gallery"
>
Galeriye kaydet
</string>
<string
name=
"action_select_photo_from_gallery"
>
Galeriden resim seç
</string>
<string
name=
"action_take_photo"
>
Fotoğraf çek
</string>
<string
name=
"action_take_
a_
photo"
>
Fotoğraf çek
</string>
<string
name=
"action_reset_avatar"
>
Avatarı kaldır
</string>
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<!-- TODO Add translation -->
<string
name=
"action_join_community"
>
Join in the community
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values-uk/strings.xml
View file @
b9f79237
...
...
@@ -35,7 +35,7 @@
<string
name=
"action_create_channel"
>
Створити канал
</string>
<string
name=
"action_create"
>
Створити
</string>
<string
name=
"action_logout"
>
Вийти
</string>
<string
name=
"action_
files"
>
Файли
</string
>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<!-- TODO Add translation --
>
<string
name=
"action_confirm_password"
>
Підтвердження зміни пароля
</string>
<string
name=
"action_join_chat"
>
Приєднатися до чату
</string>
<string
name=
"action_add_account"
>
Додати аккаунт
</string>
...
...
@@ -46,7 +46,7 @@
<string
name=
"action_drawing"
>
Малюнок
</string>
<string
name=
"action_save_to_gallery"
>
Зберегти до галереї
</string>
<string
name=
"action_select_photo_from_gallery"
>
Вибрати з галереї
</string>
<string
name=
"action_take_photo"
>
Зробити знімок
</string>
<string
name=
"action_take_
a_
photo"
>
Зробити знімок
</string>
<string
name=
"action_reset_avatar"
>
Відновити аватар
</string>
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<!-- TODO Add translation -->
<string
name=
"action_join_community"
>
Join in the community
</string>
<!-- TODO Add translation -->
...
...
app/src/main/res/values/strings.xml
View file @
b9f79237
...
...
@@ -47,7 +47,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"action_create_channel"
>
Create channel
</string>
<string
name=
"action_create"
>
Create
</string>
<string
name=
"action_logout"
>
Logout
</string>
<string
name=
"action_
files"
>
Files
</string>
<string
name=
"action_
attach_a_files"
>
Attach a file
</string>
<string
name=
"action_confirm_password"
>
Confirm Password Change
</string>
<string
name=
"action_join_chat"
>
Join Chat
</string>
<string
name=
"action_add_account"
>
Add account
</string>
...
...
@@ -58,7 +58,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"action_drawing"
>
Drawing
</string>
<string
name=
"action_save_to_gallery"
>
Save to gallery
</string>
<string
name=
"action_select_photo_from_gallery"
>
Select photo from gallery
</string>
<string
name=
"action_take_
photo"
>
Take
photo
</string>
<string
name=
"action_take_
a_photo"
>
Take a
photo
</string>
<string
name=
"action_reset_avatar"
>
Reset avatar
</string>
<string
name=
"action_connect_server"
>
Connect with a server
</string>
<string
name=
"action_join_community"
>
Join in the community
</string>
...
...
app/src/main/res/xml/file_paths.xml
0 → 100644
View file @
b9f79237
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name=
"rocket_chat_images"
path=
"Android/data/chat.rocket.android/files/Pictures"
/>
</paths>
\ No newline at end of file
app/src/play/java/chat/rocket/android/push/FirebaseMessagingService.kt
View file @
b9f79237
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 @
b9f79237
...
...
@@ -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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
Default
)
}
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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
LightTone
)
}
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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumLightTone
)
}
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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumTone
)
}
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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
MediumDarkTone
)
}
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
{
dialog
.
dismiss
()
changeSkinTone
(
Fitzpatrick
.
DarkTone
)
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/res/drawable/
color_change_circl
e.xml
→
emoji/src/main/res/drawable/
bg_skin_ton
e.xml
View file @
b9f79237
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"oval"
>
<solid
android:color=
"@color/tone_default"
/>
<size
android:width=
"24dp"
android:height=
"24dp"
/>
</shape>
\ No newline at end of file
</shape>
emoji/src/main/res/layout/
color_select_popup
.xml
→
emoji/src/main/res/layout/
dialog_skin_tone_chooser
.xml
View file @
b9f79237
<?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>
\ No newline at end of file
</androidx.constraintlayout.widget.ConstraintLayout>
emoji/src/main/res/layout/emoji_keyboard.xml
View file @
b9f79237
...
...
@@ -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"
...
...
util/src/main/java/chat/rocket/android/util/extension/Image.kt
View file @
b9f79237
...
...
@@ -2,13 +2,19 @@ package chat.rocket.android.util.extension
import
android.content.Intent
import
android.graphics.Bitmap
import
android.os.Environment
import
android.provider.MediaStore
import
androidx.fragment.app.Fragment
import
androidx.fragment.app.FragmentActivity
import
kotlinx.coroutines.experimental.DefaultDispatcher
import
kotlinx.coroutines.experimental.withContext
import
java.io.ByteArrayInputStream
import
java.io.ByteArrayOutputStream
import
java.io.InputStream
import
java.io.IOException
import
java.io.File
import
java.text.SimpleDateFormat
import
java.util.*
/**
* Compress a [Bitmap] image.
...
...
@@ -104,4 +110,16 @@ fun Fragment.dispatchTakePicture(requestCode: Int) {
if
(
takePictureIntent
.
resolveActivity
(
context
?.
packageManager
)
!=
null
)
{
startActivityForResult
(
takePictureIntent
,
requestCode
)
}
}
@Throws
(
IOException
::
class
)
fun
FragmentActivity
.
createImageFile
():
File
{
// Create an image file name
val
timeStamp
:
String
=
SimpleDateFormat
(
"yyyyMMdd_HHmmss"
).
format
(
Date
())
val
storageDir
:
File
=
getExternalFilesDir
(
Environment
.
DIRECTORY_PICTURES
)
return
File
.
createTempFile
(
"PNG_${timeStamp}_"
,
/* prefix */
".png"
,
/* suffix */
storageDir
/* directory */
)
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment