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
f4a743d4
Commit
f4a743d4
authored
Mar 14, 2019
by
Filipe de Lima Brito
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add Jitsi SDK
parent
fa0cc587
Changes
98
Hide whitespace changes
Inline
Side-by-side
Showing
98 changed files
with
743 additions
and
503 deletions
+743
-503
build.gradle
app/build.gradle
+31
-23
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+7
-0
AppLifecycleObserver.kt
...main/java/chat/rocket/android/app/AppLifecycleObserver.kt
+3
-7
RocketChatApplication.kt
...ain/java/chat/rocket/android/app/RocketChatApplication.kt
+3
-2
AuthenticationModule.kt
.../rocket/android/authentication/di/AuthenticationModule.kt
+1
-1
LoginOptionsPresenter.kt
...cation/loginoptions/presentation/LoginOptionsPresenter.kt
+3
-3
OnBoardingPresenter.kt
...entication/onboarding/presentation/OnBoardingPresenter.kt
+4
-4
ServerPresenter.kt
...oid/authentication/server/presentation/ServerPresenter.kt
+3
-3
ChatDetailsFragment.kt
...chat/rocket/android/chatdetails/ui/ChatDetailsFragment.kt
+0
-1
Menu.kt
app/src/main/java/chat/rocket/android/chatdetails/ui/Menu.kt
+4
-2
MessageInfoFragmentModule.kt
...t/android/chatinformation/di/MessageInfoFragmentModule.kt
+1
-1
MessageInfoActivity.kt
.../rocket/android/chatinformation/ui/MessageInfoActivity.kt
+0
-1
ChatRoomAdapter.kt
...a/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
+5
-8
MessageReactionsAdapter.kt
...ocket/android/chatroom/adapter/MessageReactionsAdapter.kt
+3
-3
MessageViewHolder.kt
...chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
+6
-1
UrlPreviewViewHolder.kt
...t/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
+5
-4
ChatRoomModule.kt
...in/java/chat/rocket/android/chatroom/di/ChatRoomModule.kt
+1
-1
ChatRoomNavigator.kt
...rocket/android/chatroom/presentation/ChatRoomNavigator.kt
+5
-0
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+19
-21
MessageService.kt
...va/chat/rocket/android/chatroom/service/MessageService.kt
+6
-4
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+3
-4
Dialog.kt
app/src/main/java/chat/rocket/android/chatroom/ui/Dialog.kt
+6
-5
BaseUiModel.kt
.../java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt
+1
-1
MessageUiModel.kt
...va/chat/rocket/android/chatroom/uimodel/MessageUiModel.kt
+0
-1
UiModelMapper.kt
...ava/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt
+9
-9
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+3
-2
ChatRoomsViewModel.kt
.../rocket/android/chatrooms/viewmodel/ChatRoomsViewModel.kt
+10
-10
ActivityBuilder.kt
...java/chat/rocket/android/dagger/module/ActivityBuilder.kt
+6
-0
AppModule.kt
.../main/java/chat/rocket/android/dagger/module/AppModule.kt
+0
-1
LocalModule.kt
...ain/java/chat/rocket/android/dagger/module/LocalModule.kt
+0
-1
DatabaseManager.kt
app/src/main/java/chat/rocket/android/db/DatabaseManager.kt
+12
-11
AndroidPermissionsHelper.kt
...va/chat/rocket/android/helper/AndroidPermissionsHelper.kt
+4
-2
JitsiHelper.kt
app/src/main/java/chat/rocket/android/helper/JitsiHelper.kt
+34
-0
MainModule.kt
app/src/main/java/chat/rocket/android/main/di/MainModule.kt
+1
-1
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+1
-1
ProfilePresenter.kt
...t/rocket/android/profile/presentation/ProfilePresenter.kt
+3
-3
DirectReplyReceiver.kt
...main/java/chat/rocket/android/push/DirectReplyReceiver.kt
+3
-3
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+1
-1
ChangeServerModule.kt
.../java/chat/rocket/android/server/di/ChangeServerModule.kt
+1
-2
ChatRoomsInteractor.kt
.../chat/rocket/android/server/domain/ChatRoomsInteractor.kt
+16
-14
RefreshPermissionsInteractor.kt
...ket/android/server/domain/RefreshPermissionsInteractor.kt
+4
-3
RefreshSettingsInteractor.kt
...rocket/android/server/domain/RefreshSettingsInteractor.kt
+8
-5
SettingsRepository.kt
...a/chat/rocket/android/server/domain/SettingsRepository.kt
+4
-0
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+47
-40
DatabaseMessageMapper.kt
...t/android/server/infraestructure/DatabaseMessageMapper.kt
+54
-44
DatabaseMessagesRepository.kt
...roid/server/infraestructure/DatabaseMessagesRepository.kt
+19
-18
CheckServerPresenter.kt
...ocket/android/server/presentation/CheckServerPresenter.kt
+5
-5
ChangeServerActivity.kt
...ava/chat/rocket/android/server/ui/ChangeServerActivity.kt
+0
-1
SettingsFragmentModule.kt
...chat/rocket/android/settings/di/SettingsFragmentModule.kt
+1
-1
PasswordFragmentModule.kt
...et/android/settings/password/di/PasswordFragmentModule.kt
+1
-1
UserDetailsPresenter.kt
.../android/userdetails/presentation/UserDetailsPresenter.kt
+3
-3
IO.kt
app/src/main/java/chat/rocket/android/util/IO.kt
+23
-24
Fragment.kt
...main/java/chat/rocket/android/util/extensions/Fragment.kt
+5
-4
RocketChatClient.kt
...a/chat/rocket/android/util/extensions/RocketChatClient.kt
+3
-3
TransformedLiveData.kt
.../chat/rocket/android/util/livedata/TransformedLiveData.kt
+14
-13
WrappedLiveData.kt
...java/chat/rocket/android/util/livedata/WrappedLiveData.kt
+13
-11
VideoConferencingModule.kt
...t/android/videoconferencing/di/VideoConferencingModule.kt
+33
-0
VideoConferencingPresenter.kt
...videoconferencing/presenter/VideoConferencingPresenter.kt
+61
-0
VideoConferencingView.kt
...roid/videoconferencing/presenter/VideoConferencingView.kt
+13
-0
VideoConferencingActivity.kt
...android/videoconferencing/ui/VideoConferencingActivity.kt
+36
-0
WebViewActivity.kt
...in/java/chat/rocket/android/webview/ui/WebViewActivity.kt
+1
-1
item_message.xml
app/src/main/res/layout/item_message.xml
+10
-0
strings.xml
app/src/main/res/values-de/strings.xml
+1
-0
strings.xml
app/src/main/res/values-es/strings.xml
+1
-0
strings.xml
app/src/main/res/values-fa/strings.xml
+1
-0
strings.xml
app/src/main/res/values-fr/strings.xml
+1
-0
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+1
-0
strings.xml
app/src/main/res/values-it/strings.xml
+1
-0
strings.xml
app/src/main/res/values-ja/strings.xml
+1
-0
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+1
-0
strings.xml
app/src/main/res/values-ru-rRU/strings.xml
+1
-0
strings.xml
app/src/main/res/values-tr/strings.xml
+1
-0
strings.xml
app/src/main/res/values-uk/strings.xml
+1
-0
strings.xml
app/src/main/res/values-zh-rCN/strings.xml
+1
-0
strings.xml
app/src/main/res/values/strings.xml
+1
-0
GooglePlayServices.kt
...java/chat/rocket/android/extensions/GooglePlayServices.kt
+3
-1
SmartLockHelper.kt
...c/play/java/chat/rocket/android/helper/SmartLockHelper.kt
+1
-1
CrashlyticsWrapper.kt
.../chat/rocket/android/infrastructure/CrashlyticsWrapper.kt
+21
-14
TokenRegistrationWorker.kt
...hat/rocket/android/push/worker/TokenRegistrationWorker.kt
+1
-1
build.gradle
build.gradle
+4
-4
build.gradle
core/build.gradle
+1
-1
CancelStrategy.kt
...java/chat/rocket/android/core/lifecycle/CancelStrategy.kt
+1
-1
dependencies.gradle
dependencies.gradle
+21
-27
build.gradle
draw/build.gradle
+1
-1
DrawModule.kt
.../main/java/chat/rocket/android/draw/main/di/DrawModule.kt
+1
-1
build.gradle
emoji/build.gradle
+9
-14
EmojiKeyboardPopup.kt
...main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
+4
-3
EmojiParser.kt
emoji/src/main/java/chat/rocket/android/emoji/EmojiParser.kt
+10
-8
EmojiPickerPopup.kt
...c/main/java/chat/rocket/android/emoji/EmojiPickerPopup.kt
+4
-3
EmojiRepository.kt
...rc/main/java/chat/rocket/android/emoji/EmojiRepository.kt
+39
-35
EmojiGlideModule.kt
...va/chat/rocket/android/emoji/internal/EmojiGlideModule.kt
+0
-15
EmojiPagerAdapter.kt
...a/chat/rocket/android/emoji/internal/EmojiPagerAdapter.kt
+10
-10
build.gradle
player/build.gradle
+1
-0
settings.gradle
settings.gradle
+1
-1
TrieNode.kt
...rocket/android/suggestions/strategy/trie/data/TrieNode.kt
+2
-7
build.gradle
util/build.gradle
+1
-1
Coroutines.kt
...ain/java/chat/rocket/android/util/extension/Coroutines.kt
+6
-7
Image.kt
...src/main/java/chat/rocket/android/util/extension/Image.kt
+6
-8
No files found.
app/build.gradle
View file @
f4a743d4
...
...
@@ -2,7 +2,9 @@ def taskRequests = getGradle().getStartParameter().getTaskRequests().toString()
def
isPlay
=
!(
taskRequests
.
contains
(
"Foss"
)
||
taskRequests
.
contains
(
"foss"
))
apply
plugin:
'com.android.application'
if
(
isPlay
)
{
apply
plugin:
'io.fabric'
}
if
(
isPlay
)
{
apply
plugin:
'io.fabric'
}
apply
plugin:
'kotlin-android'
apply
plugin:
'kotlin-android-extensions'
apply
plugin:
'kotlin-kapt'
...
...
@@ -16,13 +18,12 @@ android {
applicationId
"chat.rocket.android"
minSdkVersion
versions
.
minSdk
targetSdkVersion
versions
.
targetSdk
versionCode
205
7
versionName
"3.
2
.0"
versionCode
205
8
versionName
"3.
3
.0"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
def
gitSha
=
'git rev-parse --short HEAD'
.
execute
([],
project
.
rootDir
).
text
.
trim
()
def
buildTime
=
new
GregorianCalendar
().
format
(
"MM-dd-yyyy' 'h:mm:ss a z"
)
buildConfigField
"String"
,
"GIT_SHA"
,
"\"${gitSha}\""
javaCompileOptions
{
...
...
@@ -30,6 +31,17 @@ android {
arguments
=
[
"room.schemaLocation"
:
"$projectDir/schemas"
.
toString
()]
}
}
// For Jitsi
compileOptions
{
sourceCompatibility
JavaVersion
.
VERSION_1_8
targetCompatibility
JavaVersion
.
VERSION_1_8
}
// For Jitsi
ndk
{
abiFilters
"armeabi-v7a"
,
"x86"
}
}
signingConfigs
{
...
...
@@ -73,7 +85,7 @@ android {
dimension
"type"
}
// only
foss
// only
FOSS
foss
{
dimension
"type"
}
...
...
@@ -96,7 +108,7 @@ dependencies {
implementation
project
(
':suggestions'
)
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutines
Core
implementation
libraries
.
coroutinesAndroid
implementation
libraries
.
appCompat
...
...
@@ -123,6 +135,8 @@ dependencies {
implementation
libraries
.
viewmodelKtx
implementation
libraries
.
workmanager
implementation
libraries
.
livedataKtx
implementation
libraries
.
rxKotlin
implementation
libraries
.
rxAndroid
...
...
@@ -133,25 +147,25 @@ dependencies {
implementation
libraries
.
timber
implementation
libraries
.
threeTenABP
kapt
libraries
.
kotshiCompiler
implementation
libraries
.
kotshiApi
implementation
libraries
.
fresco
api
libraries
.
frescoOkHttp
implementation
libraries
.
frescoAnimatedGif
implementation
libraries
.
frescoWebP
implementation
libraries
.
frescoAnimatedWebP
implementation
libraries
.
glide
implementation
libraries
.
glideTransformations
kapt
libraries
.
kotshiCompiler
implementation
libraries
.
kotshiApi
implementation
libraries
.
frescoImageViewer
implementation
libraries
.
markwon
implementation
libraries
.
aVLoadingIndicatorView
implementation
libraries
.
livedataKtx
implementation
libraries
.
glide
implementation
libraries
.
glideTransformations
implementation
(
libraries
.
jitsi
)
{
transitive
=
true
}
implementation
'com.google.code.findbugs:jsr305:3.0.2'
...
...
@@ -159,8 +173,8 @@ dependencies {
playImplementation
libraries
.
fcm
playImplementation
libraries
.
firebaseAnalytics
playImplementation
libraries
.
playServicesAuth
playImplementation
(
'com.crashlytics.sdk.android:crashlytics:2.9.
5
@aar'
)
{
transitive
=
true
}
playImplementation
(
'com.crashlytics.sdk.android:answers:1.4.
3
@aar'
)
{
transitive
=
true
}
playImplementation
(
'com.crashlytics.sdk.android:crashlytics:2.9.
8
@aar'
)
{
transitive
=
true
}
playImplementation
(
'com.crashlytics.sdk.android:answers:1.4.
6
@aar'
)
{
transitive
=
true
}
testImplementation
libraries
.
junit
testImplementation
libraries
.
truth
...
...
@@ -168,12 +182,6 @@ dependencies {
androidTestImplementation
libraries
.
espressoIntents
}
kotlin
{
experimental
{
coroutines
"enable"
}
}
androidExtensions
{
experimental
=
true
}
...
...
@@ -181,8 +189,8 @@ androidExtensions {
// FIXME - build and install the sdk into the app/libs directory
// We were having some issues with the kapt generated files from the sdk when importing as a module
def
sdk_location
=
project
.
properties
[
'sdk_location'
]
?:
""
task
compileSdk
(
type:
Exec
)
{
def
sdk_location
=
project
.
properties
[
'sdk_location'
]
?:
""
task
compileSdk
(
type:
Exec
)
{
if
(
System
.
getProperty
(
'os.name'
).
toLowerCase
(
Locale
.
ROOT
).
contains
(
'windows'
))
{
commandLine
'cmd'
,
'/c'
,
'build-sdk.sh'
,
sdk_location
}
else
{
...
...
app/src/main/AndroidManifest.xml
View file @
f4a743d4
...
...
@@ -6,6 +6,8 @@
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.VIBRATE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.CAMERA"
/>
<uses-permission
android:name=
"android.permission.RECORD_AUDIO"
/>
<application
android:name=
".app.RocketChatApplication"
...
...
@@ -73,6 +75,11 @@
android:theme=
"@style/AppTheme"
android:windowSoftInputMode=
"adjustResize|stateAlwaysHidden"
/>
<activity
android:name=
".videoconferencing.ui.VideoConferencingActivity"
android:theme=
"@style/AppTheme"
android:windowSoftInputMode=
"adjustResize|stateAlwaysHidden"
/>
<activity
android:name=
".chatroom.ui.ChatRoomActivity"
android:theme=
"@style/AppTheme"
...
...
app/src/main/java/chat/rocket/android/app/AppLifecycleObserver.kt
View file @
f4a743d4
...
...
@@ -3,15 +3,11 @@ package chat.rocket.android.app
import
androidx.lifecycle.Lifecycle
import
androidx.lifecycle.LifecycleObserver
import
androidx.lifecycle.OnLifecycleEvent
import
chat.rocket.android.server.domain.GetAccountInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.UserStatus
import
chat.rocket.core.internal.realtime.setTemporaryStatus
import
kotlinx.coroutines.experimental.launch
import
timber.log.Timber
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
javax.inject.Inject
class
AppLifecycleObserver
@Inject
constructor
(
...
...
@@ -33,7 +29,7 @@ class AppLifecycleObserver @Inject constructor(
}
private
fun
changeTemporaryStatus
(
userStatus
:
UserStatus
)
{
launch
{
GlobalScope
.
launch
{
serverInteractor
.
get
()
?.
let
{
currentServer
->
factory
.
create
(
currentServer
).
setTemporaryStatus
(
userStatus
)
}
...
...
app/src/main/java/chat/rocket/android/app/RocketChatApplication.kt
View file @
f4a743d4
...
...
@@ -37,7 +37,8 @@ import dagger.android.DispatchingAndroidInjector
import
dagger.android.HasActivityInjector
import
dagger.android.HasBroadcastReceiverInjector
import
dagger.android.HasServiceInjector
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
import
java.lang.ref.WeakReference
import
javax.inject.Inject
...
...
@@ -174,7 +175,7 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
EmojiRepository
.
init
(
this
)
val
currentServer
=
getCurrentServerInteractor
.
get
()
currentServer
?.
let
{
server
->
launch
{
GlobalScope
.
launch
{
val
client
=
factory
.
create
(
server
)
EmojiRepository
.
setCurrentServerUrl
(
server
)
val
customEmojiList
=
mutableListOf
<
Emoji
>()
...
...
app/src/main/java/chat/rocket/android/authentication/di/AuthenticationModule.kt
View file @
f4a743d4
...
...
@@ -7,7 +7,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
AuthenticationModule
{
...
...
app/src/main/java/chat/rocket/android/authentication/loginoptions/presentation/LoginOptionsPresenter.kt
View file @
f4a743d4
...
...
@@ -31,7 +31,7 @@ import chat.rocket.core.internal.rest.loginWithCas
import
chat.rocket.core.internal.rest.loginWithOauth
import
chat.rocket.core.internal.rest.loginWithSaml
import
chat.rocket.core.internal.rest.me
import
kotlinx.coroutines.
experimental.
delay
import
kotlinx.coroutines.delay
import
java.util.concurrent.TimeUnit
import
javax.inject.Inject
...
...
@@ -109,11 +109,11 @@ class LoginOptionsPresenter @Inject constructor(
when
(
loginType
)
{
TYPE_LOGIN_OAUTH
->
client
.
loginWithOauth
(
credentialToken
,
credentialSecret
)
TYPE_LOGIN_CAS
->
{
delay
(
3
,
TimeUnit
.
SECONDS
)
delay
(
3
000
)
client
.
loginWithCas
(
credentialToken
)
}
TYPE_LOGIN_SAML
->
{
delay
(
3
,
TimeUnit
.
SECONDS
)
delay
(
3
000
)
client
.
loginWithSaml
(
credentialToken
)
}
TYPE_LOGIN_DEEP_LINK
->
{
...
...
app/src/main/java/chat/rocket/android/authentication/onboarding/presentation/OnBoardingPresenter.kt
View file @
f4a743d4
...
...
@@ -10,8 +10,8 @@ import chat.rocket.android.server.domain.SaveConnectingServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.presentation.CheckServerPresenter
import
chat.rocket.android.util.extension.launchUI
import
kotlinx.coroutines.
experimental.DefaultDispatcher
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
javax.inject.Inject
class
OnBoardingPresenter
@Inject
constructor
(
...
...
@@ -19,7 +19,7 @@ class OnBoardingPresenter @Inject constructor(
private
val
strategy
:
CancelStrategy
,
private
val
navigator
:
AuthenticationNavigator
,
private
val
serverInteractor
:
SaveConnectingServerInteractor
,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
getAccountsInteractor
:
GetAccountsInteractor
,
val
settingsInteractor
:
GetSettingsInteractor
,
val
factory
:
RocketChatClientFactory
...
...
@@ -80,7 +80,7 @@ class OnBoardingPresenter @Inject constructor(
}
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
setupConnectionInfo
(
serverUrl
)
// preparing next fragment before showing it
...
...
app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt
View file @
f4a743d4
...
...
@@ -12,8 +12,8 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.presentation.CheckServerPresenter
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.android.util.extensions.isValidUrl
import
kotlinx.coroutines.
experimental.DefaultDispatcher
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
javax.inject.Inject
class
ServerPresenter
@Inject
constructor
(
...
...
@@ -98,7 +98,7 @@ class ServerPresenter @Inject constructor(
}
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
// preparing next fragment before showing it
refreshServerAccounts
()
checkEnabledAccounts
(
serverUrl
)
...
...
app/src/main/java/chat/rocket/android/chatdetails/ui/ChatDetailsFragment.kt
View file @
f4a743d4
...
...
@@ -21,7 +21,6 @@ import chat.rocket.android.chatdetails.viewmodel.ChatDetailsViewModelFactory
import
chat.rocket.android.chatroom.ui.ChatRoomActivity
import
chat.rocket.android.server.domain.CurrentServerRepository
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.ui
...
...
app/src/main/java/chat/rocket/android/chatdetails/ui/Menu.kt
View file @
f4a743d4
...
...
@@ -5,6 +5,7 @@ import android.view.MenuItem
import
chat.rocket.android.R
import
chat.rocket.android.server.domain.isJitsiEnabled
import
chat.rocket.android.server.domain.isJitsiEnabledForChannels
import
chat.rocket.android.videoconferencing.ui.videoConferencingIntent
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
...
...
@@ -38,7 +39,8 @@ internal fun ChatDetailsFragment.setupMenu(menu: Menu) {
MENU_ACTION_FAVORITE_REMOVE_FAVORITE
,
Menu
.
NONE
,
R
.
string
.
action_favorite
).
setIcon
(
R
.
drawable
.
ic_star_border_white_24dp
).
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_IF_ROOM
)
).
setIcon
(
R
.
drawable
.
ic_star_border_white_24dp
)
.
setShowAsAction
(
MenuItem
.
SHOW_AS_ACTION_IF_ROOM
)
}
}
...
...
@@ -46,6 +48,6 @@ internal fun ChatDetailsFragment.setOnMenuItemClickListener(item: MenuItem) {
if
(
item
.
itemId
==
MENU_ACTION_FAVORITE_REMOVE_FAVORITE
)
{
presenter
.
toggleFavoriteChatRoom
(
chatRoomId
,
isFavorite
)
}
else
if
(
item
.
itemId
==
MENU_ACTION_VIDEO_CALL
)
{
// TODO
startActivity
(
activity
?.
videoConferencingIntent
(
chatRoomId
))
}
}
app/src/main/java/chat/rocket/android/chatinformation/di/MessageInfoFragmentModule.kt
View file @
f4a743d4
...
...
@@ -7,7 +7,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerFragment
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
MessageInfoFragmentModule
{
...
...
app/src/main/java/chat/rocket/android/chatinformation/ui/MessageInfoActivity.kt
View file @
f4a743d4
...
...
@@ -24,7 +24,6 @@ fun Context.messageInformationIntent(messageId: String): Intent {
private
const
val
INTENT_MESSAGE_ID
=
"message_id"
class
MessageInfoActivity
:
AppCompatActivity
(),
HasSupportFragmentInjector
{
@Inject
lateinit
var
fragmentDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Fragment
>
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ChatRoomAdapter.kt
View file @
f4a743d4
...
...
@@ -6,12 +6,7 @@ import android.view.ViewGroup
import
androidx.recyclerview.widget.RecyclerView
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.presentation.ChatRoomNavigator
import
chat.rocket.android.chatroom.uimodel.AttachmentUiModel
import
chat.rocket.android.chatroom.uimodel.BaseUiModel
import
chat.rocket.android.chatroom.uimodel.MessageReplyUiModel
import
chat.rocket.android.chatroom.uimodel.MessageUiModel
import
chat.rocket.android.chatroom.uimodel.UrlPreviewUiModel
import
chat.rocket.android.chatroom.uimodel.toViewType
import
chat.rocket.android.chatroom.uimodel.*
import
chat.rocket.android.emoji.EmojiReactionListener
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.openTabbedUrl
...
...
@@ -44,8 +39,10 @@ class ChatRoomAdapter(
MessageViewHolder
(
view
,
actionsListener
,
reactionListener
)
{
userId
->
navigator
?.
toUserDetails
(
userId
)
}
reactionListener
,
{
userId
->
navigator
?.
toUserDetails
(
userId
)
},
{
roomId
?.
let
{
navigator
?.
toVideoConferencing
(
it
)
}
}
)
}
BaseUiModel
.
ViewType
.
URL_PREVIEW
->
{
val
view
=
parent
.
inflate
(
R
.
layout
.
message_url_preview
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageReactionsAdapter.kt
View file @
f4a743d4
...
...
@@ -13,8 +13,8 @@ import chat.rocket.android.emoji.Emoji
import
chat.rocket.android.emoji.EmojiKeyboardListener
import
chat.rocket.android.emoji.EmojiPickerPopup
import
chat.rocket.android.emoji.EmojiReactionListener
import
chat.rocket.android.emoji.internal.GlideApp
import
chat.rocket.android.infrastructure.LocalRepository
import
com.bumptech.glide.Glide
import
kotlinx.android.synthetic.main.item_reaction.view.*
import
java.util.concurrent.CopyOnWriteArrayList
import
javax.inject.Inject
...
...
@@ -103,9 +103,9 @@ class MessageReactionsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>()
// The view at index 1 corresponds to the one to display custom emojis which are images.
view_flipper_reaction
.
displayedChild
=
1
val
glideRequest
=
if
(
reaction
.
url
!!
.
endsWith
(
"gif"
,
true
))
{
Glide
App
.
with
(
context
).
asGif
()
Glide
.
with
(
context
).
asGif
()
}
else
{
Glide
App
.
with
(
context
).
asBitmap
()
Glide
.
with
(
context
).
asBitmap
()
}
glideRequest
.
load
(
reaction
.
url
).
into
(
image_emoji
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/MessageViewHolder.kt
View file @
f4a743d4
...
...
@@ -10,6 +10,7 @@ import androidx.core.view.isVisible
import
chat.rocket.android.R
import
chat.rocket.android.chatroom.uimodel.MessageUiModel
import
chat.rocket.android.emoji.EmojiReactionListener
import
chat.rocket.core.model.MessageType
import
chat.rocket.core.model.isSystemMessage
import
com.bumptech.glide.load.resource.gif.GifDrawable
import
kotlinx.android.synthetic.main.avatar.view.*
...
...
@@ -19,7 +20,8 @@ class MessageViewHolder(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
,
private
val
avatarListener
:
(
String
)
->
Unit
private
val
avatarListener
:
(
String
)
->
Unit
,
private
val
joinVideoCallListener
:
(
View
)
->
Unit
)
:
BaseViewHolder
<
MessageUiModel
>(
itemView
,
listener
,
reactionListener
),
Drawable
.
Callback
{
init
{
...
...
@@ -51,6 +53,9 @@ class MessageViewHolder(
text_content
.
text_content
.
text
=
data
.
content
button_join_video_call
.
isVisible
=
data
.
message
.
type
is
MessageType
.
JitsiCallStarted
button_join_video_call
.
setOnClickListener
{
joinVideoCallListener
(
it
)
}
image_avatar
.
setImageURI
(
data
.
avatar
)
text_content
.
setTextColor
(
if
(
data
.
isTemporary
)
Color
.
GRAY
else
Color
.
BLACK
)
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/UrlPreviewViewHolder.kt
View file @
f4a743d4
...
...
@@ -8,10 +8,11 @@ import chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.openTabbedUrl
import
kotlinx.android.synthetic.main.message_url_preview.view.*
class
UrlPreviewViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
UrlPreviewUiModel
>(
itemView
,
listener
,
reactionListener
)
{
class
UrlPreviewViewHolder
(
itemView
:
View
,
listener
:
ActionsListener
,
reactionListener
:
EmojiReactionListener
?
=
null
)
:
BaseViewHolder
<
UrlPreviewUiModel
>(
itemView
,
listener
,
reactionListener
)
{
init
{
with
(
itemView
)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/di/ChatRoomModule.kt
View file @
f4a743d4
...
...
@@ -7,7 +7,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
ChatRoomModule
{
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomNavigator.kt
View file @
f4a743d4
...
...
@@ -13,6 +13,7 @@ import chat.rocket.android.pinnedmessages.ui.TAG_PINNED_MESSAGES_FRAGMENT
import
chat.rocket.android.server.ui.changeServerIntent
import
chat.rocket.android.userdetails.ui.TAG_USER_DETAILS_FRAGMENT
import
chat.rocket.android.util.extensions.addFragmentBackStack
import
chat.rocket.android.videoconferencing.ui.videoConferencingIntent
class
ChatRoomNavigator
(
internal
val
activity
:
ChatRoomActivity
)
{
...
...
@@ -22,6 +23,10 @@ class ChatRoomNavigator(internal val activity: ChatRoomActivity) {
}
}
fun
toVideoConferencing
(
chatRoomId
:
String
)
{
activity
.
startActivity
(
activity
.
videoConferencingIntent
(
chatRoomId
))
}
fun
toChatRoom
(
chatRoomId
:
String
,
chatRoomName
:
String
,
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
f4a743d4
...
...
@@ -53,7 +53,6 @@ import chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.chatRoomRoles
import
chat.rocket.core.internal.rest.commands
import
chat.rocket.core.internal.rest.deleteMessage
import
chat.rocket.core.internal.rest.favorite
import
chat.rocket.core.internal.rest.getMembers
import
chat.rocket.core.internal.rest.history
import
chat.rocket.core.internal.rest.joinChat
...
...
@@ -76,12 +75,11 @@ 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
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.channels.Channel
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
org.threeten.bp.Instant
import
timber.log.Timber
import
java.util.*
...
...
@@ -134,7 +132,7 @@ class ChatRoomPresenter @Inject constructor(
draftKey
=
"${currentServer}_${LocalRepository.DRAFT_KEY}$roomId"
chatRoomId
=
roomId
chatRoomType
=
roomType
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
try
{
chatRoles
=
if
(
roomTypeOf
(
roomType
)
!
is
RoomType
.
DirectMessage
)
{
client
.
chatRoomRoles
(
roomType
=
roomTypeOf
(
roomType
),
roomName
=
roomName
)
...
...
@@ -178,7 +176,7 @@ class ChatRoomPresenter @Inject constructor(
}
private
suspend
fun
subscribeRoomChanges
()
{
withContext
(
CommonPool
+
strategy
.
jobs
)
{
withContext
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
chatRoomId
?.
let
{
manager
.
addRoomChannel
(
it
,
roomChangesChannel
)
for
(
room
in
roomChangesChannel
)
{
...
...
@@ -403,7 +401,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
fileName
=
uriInteractor
.
getFileName
(
uri
)
?:
uri
.
toString
()
if
(
fileName
.
isEmpty
())
{
view
.
showInvalidFileMessage
()
...
...
@@ -441,7 +439,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
fileName
=
uriInteractor
.
getFileName
(
uri
)
?:
uri
.
toString
()
val
fileSize
=
uriInteractor
.
getFileSize
(
uri
)
val
maxFileSizeAllowed
=
settings
.
uploadMaxFileSize
()
...
...
@@ -482,7 +480,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
fileName
=
UUID
.
randomUUID
().
toString
()
+
".png"
val
fileSize
=
byteArray
.
size
val
mimeType
=
"image/png"
...
...
@@ -520,7 +518,7 @@ class ChatRoomPresenter @Inject constructor(
}
fun
sendTyping
()
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
currentLoggedUsername
!=
null
)
{
client
.
setTypingStatus
(
chatRoomId
.
toString
(),
currentLoggedUsername
,
true
)
}
...
...
@@ -528,7 +526,7 @@ class ChatRoomPresenter @Inject constructor(
}
fun
sendNotTyping
()
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
if
(
chatRoomId
!=
null
&&
currentLoggedUsername
!=
null
)
{
client
.
setTypingStatus
(
chatRoomId
.
toString
(),
currentLoggedUsername
,
false
)
}
...
...
@@ -550,11 +548,11 @@ class ChatRoomPresenter @Inject constructor(
Timber
.
d
(
"Subscribing to Status changes"
)
lastState
=
manager
.
state
manager
.
addStatusChannel
(
stateChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
for
(
state
in
stateChannel
)
{
Timber
.
d
(
"Got new state: $state - last: $lastState"
)
if
(
state
!=
lastState
)
{
launch
(
UI
)
{
launch
(
Dispatchers
.
Main
)
{
view
.
showConnectionState
(
state
)
}
...
...
@@ -571,7 +569,7 @@ class ChatRoomPresenter @Inject constructor(
private
fun
subscribeMessages
(
roomId
:
String
)
{
manager
.
subscribeRoomMessages
(
roomId
,
messagesChannel
)
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
for
(
message
in
messagesChannel
)
{
Timber
.
d
(
"New message for room ${message.roomId}"
)
updateMessage
(
message
)
...
...
@@ -580,7 +578,7 @@ class ChatRoomPresenter @Inject constructor(
}
private
fun
loadMissingMessages
()
{
launch
(
parent
=
strategy
.
jobs
)
{
GlobalScope
.
launch
(
strategy
.
jobs
)
{
chatRoomId
?.
let
{
chatRoomId
->
val
roomType
=
roomTypeOf
(
chatRoomType
)
val
lastSyncDate
=
messagesRepository
.
getLastSyncDate
(
chatRoomId
)
...
...
@@ -924,7 +922,7 @@ class ChatRoomPresenter @Inject constructor(
}
// TODO: move this to new interactor or FetchChatRoomsInteractor?
private
suspend
fun
getChatRoomAsync
(
roomId
:
String
):
ChatRoom
?
=
withContext
(
CommonPool
)
{
private
suspend
fun
getChatRoomAsync
(
roomId
:
String
):
ChatRoom
?
=
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getRoom($roomId)"
)
{
dbManager
.
chatRoomDao
().
getSync
(
roomId
)
?.
let
{
with
(
it
.
chatRoom
)
{
...
...
@@ -962,7 +960,7 @@ class ChatRoomPresenter @Inject constructor(
}
// TODO: move this to new interactor or FetchChatRoomsInteractor?
private
suspend
fun
getChatRoomsAsync
(
name
:
String
?
=
null
):
List
<
ChatRoom
>
=
withContext
(
CommonPool
)
{
private
suspend
fun
getChatRoomsAsync
(
name
:
String
?
=
null
):
List
<
ChatRoom
>
=
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getAllSync()"
)
{
dbManager
.
chatRoomDao
().
getAllSync
().
filter
{
if
(
name
==
null
)
{
...
...
@@ -1192,7 +1190,7 @@ class ChatRoomPresenter @Inject constructor(
}
private
fun
subscribeTypingStatus
()
{
launch
(
CommonPool
+
strategy
.
jobs
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
client
.
subscribeTypingStatus
(
chatRoomId
.
toString
())
{
_
,
id
->
typingStatusSubscriptionId
=
id
}
...
...
app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt
View file @
f4a743d4
...
...
@@ -10,8 +10,9 @@ import chat.rocket.android.server.infraestructure.DatabaseMessagesRepository
import
chat.rocket.core.internal.rest.sendMessage
import
chat.rocket.core.model.Message
import
dagger.android.AndroidInjection
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
import
javax.inject.Inject
...
...
@@ -33,7 +34,7 @@ class MessageService : JobService() {
}
override
fun
onStartJob
(
params
:
JobParameters
?):
Boolean
{
launch
(
CommonPool
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
)
{
getAccountsInteractor
.
get
().
forEach
{
account
->
retrySendingMessages
(
params
,
account
.
serverUrl
)
}
...
...
@@ -44,7 +45,8 @@ class MessageService : JobService() {
private
suspend
fun
retrySendingMessages
(
params
:
JobParameters
?,
serverUrl
:
String
)
{
val
dbManager
=
dbFactory
.
create
(
serverUrl
)
val
messageRepository
=
DatabaseMessagesRepository
(
dbManager
,
DatabaseMessageMapper
(
dbManager
))
val
messageRepository
=
DatabaseMessagesRepository
(
dbManager
,
DatabaseMessageMapper
(
dbManager
))
val
temporaryMessages
=
messageRepository
.
getAllUnsent
()
.
sortedWith
(
compareBy
(
Message
::
timestamp
))
if
(
temporaryMessages
.
isNotEmpty
())
{
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
f4a743d4
...
...
@@ -15,7 +15,6 @@ import android.text.SpannableStringBuilder
import
android.view.KeyEvent
import
android.view.LayoutInflater
import
android.view.Menu
import
android.view.MenuItem
import
android.view.View
import
android.view.ViewGroup
import
android.widget.EditText
...
...
@@ -60,7 +59,6 @@ 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
...
...
@@ -82,6 +80,7 @@ import chat.rocket.android.util.extensions.ui
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.core.internal.realtime.socket.model.State
import
com.bumptech.glide.Glide
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.Observable
import
io.reactivex.disposables.CompositeDisposable
...
...
@@ -679,9 +678,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
view_flipper
.
displayedChild
=
if
(
isCustom
)
1
else
0
if
(
isCustom
&&
url
!=
null
)
{
val
glideRequest
=
if
(
url
.
endsWith
(
"gif"
,
true
))
{
Glide
App
.
with
(
requireContext
()).
asGif
()
Glide
.
with
(
requireContext
()).
asGif
()
}
else
{
Glide
App
.
with
(
requireContext
()).
asBitmap
()
Glide
.
with
(
requireContext
()).
asBitmap
()
}
glideRequest
.
load
(
url
).
into
(
view_flipper
.
emoji_image_view
)
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/Dialog.kt
View file @
f4a743d4
...
...
@@ -4,10 +4,11 @@ import android.graphics.Bitmap
import
android.graphics.drawable.Drawable
import
android.net.Uri
import
androidx.core.view.isVisible
import
chat.rocket.android.emoji.internal.GlideApp
import
chat.rocket.android.util.extensions.getFileName
import
chat.rocket.android.util.extensions.getMimeType
import
chat.rocket.common.util.ifNull
import
com.bumptech.glide.Glide
import
com.bumptech.glide.request.RequestOptions
import
com.bumptech.glide.request.target.SimpleTarget
import
com.bumptech.glide.request.transition.Transition
...
...
@@ -25,18 +26,18 @@ fun ChatRoomFragment.showFileAttachmentDialog(uri: Uri) {
when
{
mimeType
.
startsWith
(
"image"
)
->
{
if
(
mimeType
.
contains
(
"gif"
))
{
Glide
App
Glide
.
with
(
context
)
.
asGif
()
.
load
(
uri
)
.
fitCenter
(
)
.
apply
(
RequestOptions
().
fitCenter
()
)
.
into
(
imagePreview
)
}
else
{
Glide
App
Glide
.
with
(
context
)
.
asBitmap
()
.
load
(
uri
)
.
fitCenter
(
)
.
apply
(
RequestOptions
().
fitCenter
()
)
.
into
(
object
:
SimpleTarget
<
Bitmap
>()
{
override
fun
onResourceReady
(
resource
:
Bitmap
,
...
...
app/src/main/java/chat/rocket/android/chatroom/uimodel/BaseUiModel.kt
View file @
f4a743d4
...
...
@@ -30,5 +30,5 @@ interface BaseUiModel<out T> {
internal
fun
Int
.
toViewType
():
BaseUiModel
.
ViewType
{
return
BaseUiModel
.
ViewType
.
values
().
firstOrNull
{
it
.
viewType
==
this
}
?:
throw
InvalidParameterException
(
"Invalid viewType: $this for BaseUiModel.ViewType"
)
?:
throw
InvalidParameterException
(
"Invalid viewType: $this for BaseUiModel.ViewType"
)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/uimodel/MessageUiModel.kt
View file @
f4a743d4
...
...
@@ -26,7 +26,6 @@ data class MessageUiModel(
)
:
BaseMessageUiModel
<
Message
>
{
override
val
viewType
:
Int
get
()
=
BaseUiModel
.
ViewType
.
MESSAGE
.
viewType
override
val
layoutId
:
Int
get
()
=
R
.
layout
.
item_message
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/uimodel/UiModelMapper.kt
View file @
f4a743d4
...
...
@@ -44,8 +44,8 @@ import chat.rocket.core.model.attachment.Attachment
import
chat.rocket.core.model.attachment.Field
import
chat.rocket.core.model.isSystemMessage
import
chat.rocket.core.model.url.Url
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
okhttp3.HttpUrl
import
java.security.InvalidParameterException
import
java.util.*
...
...
@@ -79,7 +79,7 @@ class UiModelMapper @Inject constructor(
message
:
Message
,
roomUiModel
:
RoomUiModel
=
RoomUiModel
(
roles
=
emptyList
(),
isBroadcast
=
true
)
):
List
<
BaseUiModel
<*
>>
=
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
return
@withContext
translate
(
message
,
roomUiModel
)
}
...
...
@@ -88,7 +88,7 @@ class UiModelMapper @Inject constructor(
roomUiModel
:
RoomUiModel
=
RoomUiModel
(
roles
=
emptyList
(),
isBroadcast
=
true
),
asNotReversed
:
Boolean
=
false
):
List
<
BaseUiModel
<*
>>
=
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
val
list
=
ArrayList
<
BaseUiModel
<*>>(
messages
.
size
)
messages
.
forEach
{
...
...
@@ -102,7 +102,7 @@ class UiModelMapper @Inject constructor(
suspend
fun
map
(
readReceipts
:
List
<
ReadReceipt
>
):
List
<
ReadReceiptViewModel
>
=
withContext
(
CommonPool
)
{
):
List
<
ReadReceiptViewModel
>
=
withContext
(
Dispatchers
.
IO
)
{
val
list
=
arrayListOf
<
ReadReceiptViewModel
>()
readReceipts
.
forEach
{
...
...
@@ -121,7 +121,7 @@ class UiModelMapper @Inject constructor(
message
:
Message
,
roomUiModel
:
RoomUiModel
):
List
<
BaseUiModel
<*
>>
=
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
val
list
=
ArrayList
<
BaseUiModel
<*>>()
getChatRoomAsync
(
message
.
roomId
)
?.
let
{
chatRoom
->
...
...
@@ -167,7 +167,7 @@ class UiModelMapper @Inject constructor(
}
// TODO: move this to new interactor or FetchChatRoomsInteractor?
private
suspend
fun
getChatRoomAsync
(
roomId
:
String
):
ChatRoom
?
=
withContext
(
CommonPool
)
{
private
suspend
fun
getChatRoomAsync
(
roomId
:
String
):
ChatRoom
?
=
withContext
(
Dispatchers
.
IO
)
{
return
@withContext
dbManager
.
getRoom
(
id
=
roomId
)
?.
let
{
with
(
it
.
chatRoom
)
{
ChatRoom
(
...
...
@@ -212,7 +212,7 @@ class UiModelMapper @Inject constructor(
message
:
Message
,
roomUiModel
:
RoomUiModel
):
List
<
BaseUiModel
<*
>>
=
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
val
list
=
ArrayList
<
BaseUiModel
<*>>()
getChatRoomAsync
(
message
.
roomId
)
?.
let
{
chatRoom
->
...
...
@@ -439,7 +439,7 @@ class UiModelMapper @Inject constructor(
private
suspend
fun
mapMessage
(
message
:
Message
,
chatRoom
:
ChatRoom
):
MessageUiModel
=
withContext
(
CommonPool
)
{
):
MessageUiModel
=
withContext
(
Dispatchers
.
IO
)
{
val
sender
=
getSenderName
(
message
)
val
time
=
getTime
(
message
.
timestamp
)
val
avatar
=
getUserAvatar
(
message
)
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
f4a743d4
...
...
@@ -22,11 +22,12 @@ import chat.rocket.common.model.roomTypeOf
import
chat.rocket.core.internal.realtime.createDirectMessage
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.show
import
kotlinx.coroutines.
experimental.
withTimeout
import
kotlinx.coroutines.withTimeout
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Named
import
kotlin.coroutines.experimental.suspendCoroutine
import
kotlin.coroutines.resume
import
kotlin.coroutines.suspendCoroutine
class
ChatRoomsPresenter
@Inject
constructor
(
private
val
view
:
ChatRoomsView
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/viewmodel/ChatRoomsViewModel.kt
View file @
f4a743d4
...
...
@@ -20,16 +20,16 @@ import chat.rocket.core.model.SpotlightResult
import
com.shopify.livedataktx.distinct
import
com.shopify.livedataktx.map
import
com.shopify.livedataktx.nonNull
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.delay
import
kotlinx.coroutines.experimental.isActive
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.newSingleThreadContext
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.delay
import
kotlinx.coroutines.isActive
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.newSingleThreadContext
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
java.lang.IllegalArgumentException
import
kotlin.coroutines.experimental.coroutineContext
import
kotlin.coroutines.coroutineContext
class
ChatRoomsViewModel
(
private
val
connectionManager
:
ConnectionManager
,
...
...
@@ -107,7 +107,7 @@ class ChatRoomsViewModel(
}
private
fun
fetchRooms
()
{
launch
{
GlobalScope
.
launch
{
setLoadingState
(
LoadingState
.
Loading
(
repository
.
count
()))
try
{
interactor
.
refreshChatRooms
()
...
...
@@ -125,7 +125,7 @@ class ChatRoomsViewModel(
}
private
suspend
fun
setLoadingState
(
state
:
LoadingState
)
{
withContext
(
UI
)
{
withContext
(
Dispatchers
.
Main
)
{
loadingState
.
value
=
state
}
}
...
...
app/src/main/java/chat/rocket/android/dagger/module/ActivityBuilder.kt
View file @
f4a743d4
...
...
@@ -37,6 +37,8 @@ import chat.rocket.android.settings.di.SettingsFragmentProvider
import
chat.rocket.android.settings.password.di.PasswordFragmentProvider
import
chat.rocket.android.settings.password.ui.PasswordActivity
import
chat.rocket.android.userdetails.di.UserDetailsFragmentProvider
import
chat.rocket.android.videoconferencing.di.VideoConferencingModule
import
chat.rocket.android.videoconferencing.ui.VideoConferencingActivity
import
chat.rocket.android.webview.adminpanel.di.AdminPanelWebViewFragmentProvider
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
...
...
@@ -103,4 +105,8 @@ abstract class ActivityBuilder {
@PerActivity
@ContributesAndroidInjector
(
modules
=
[
DrawModule
::
class
])
abstract
fun
bindDrawingActivity
():
DrawingActivity
@PerActivity
@ContributesAndroidInjector
(
modules
=
[
VideoConferencingModule
::
class
])
abstract
fun
bindVideoConferencingActivity
():
VideoConferencingActivity
}
app/src/main/java/chat/rocket/android/dagger/module/AppModule.kt
View file @
f4a743d4
...
...
@@ -63,7 +63,6 @@ import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import
chat.rocket.common.internal.ISO8601Date
import
chat.rocket.common.model.TimestampAdapter
import
chat.rocket.common.util.CalendarISO8601Converter
import
chat.rocket.common.util.Logger
import
chat.rocket.common.util.NoOpLogger
import
chat.rocket.common.util.PlatformLogger
import
chat.rocket.core.internal.AttachmentAdapterFactory
...
...
app/src/main/java/chat/rocket/android/dagger/module/LocalModule.kt
View file @
f4a743d4
...
...
@@ -13,7 +13,6 @@ import chat.rocket.common.internal.FallbackSealedClassJsonAdapter
import
chat.rocket.common.internal.ISO8601Date
import
chat.rocket.common.model.TimestampAdapter
import
chat.rocket.common.util.CalendarISO8601Converter
import
chat.rocket.common.util.Logger
import
chat.rocket.common.util.NoOpLogger
import
chat.rocket.common.util.PlatformLogger
import
chat.rocket.core.internal.AttachmentAdapterFactory
...
...
app/src/main/java/chat/rocket/android/db/DatabaseManager.kt
View file @
f4a743d4
...
...
@@ -33,11 +33,12 @@ import chat.rocket.core.model.Myself
import
chat.rocket.core.model.Room
import
chat.rocket.core.model.attachment.Attachment
import
chat.rocket.core.model.userId
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.channels.Channel
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.newSingleThreadContext
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.channels.Channel
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.newSingleThreadContext
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
java.util.HashSet
import
kotlin.system.measureTimeMillis
...
...
@@ -69,7 +70,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
fun
start
()
{
dbJob
?.
cancel
()
dbJob
=
launch
(
dbContext
)
{
dbJob
=
GlobalScope
.
launch
(
dbContext
)
{
for
(
operation
in
writeChannel
)
{
doOperation
(
operation
)
}
...
...
@@ -115,7 +116,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
fun
processUsersBatch
(
users
:
List
<
User
>)
{
launch
(
dbManagerContext
)
{
GlobalScope
.
launch
(
dbManagerContext
)
{
val
list
=
ArrayList
<
BaseUserEntity
>(
users
.
size
)
val
time
=
measureTimeMillis
{
users
.
forEach
{
user
->
...
...
@@ -133,7 +134,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
* Creates a list of data base operations
*/
fun
processChatRoomsBatch
(
batch
:
List
<
StreamMessage
<
BaseRoom
>>)
{
launch
(
dbManagerContext
)
{
GlobalScope
.
launch
(
dbManagerContext
)
{
val
toRemove
=
HashSet
<
String
>()
val
toInsert
=
ArrayList
<
ChatRoomEntity
>(
batch
.
size
/
2
)
val
toUpdate
=
ArrayList
<
ChatRoomEntity
>(
batch
.
size
)
...
...
@@ -165,7 +166,7 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
fun
updateSelfUser
(
myself
:
Myself
)
{
launch
(
dbManagerContext
)
{
GlobalScope
.
launch
(
dbManagerContext
)
{
val
user
=
retryDB
(
"getUser(${myself.id})"
)
{
userDao
().
getUser
(
myself
.
id
)
}
val
entity
=
user
?.
copy
(
name
=
myself
.
name
?:
user
.
name
,
...
...
@@ -180,14 +181,14 @@ class DatabaseManager(val context: Application, val serverUrl: String) {
}
fun
processRooms
(
rooms
:
List
<
ChatRoom
>)
{
launch
(
dbManagerContext
)
{
GlobalScope
.
launch
(
dbManagerContext
)
{
val
entities
=
rooms
.
map
{
mapChatRoom
(
it
)
}
sendOperation
(
Operation
.
CleanInsertRooms
(
entities
))
}
}
fun
processMessagesBatch
(
messages
:
List
<
Message
>):
Job
{
return
launch
(
dbManagerContext
)
{
return
GlobalScope
.
launch
(
dbManagerContext
)
{
val
list
=
mutableListOf
<
Pair
<
MessageEntity
,
List
<
BaseMessageEntity
>>>()
messages
.
forEach
{
message
->
val
pair
=
createMessageEntities
(
message
)
...
...
app/src/main/java/chat/rocket/android/helper/AndroidPermissionsHelper.kt
View file @
f4a743d4
...
...
@@ -11,8 +11,10 @@ object AndroidPermissionsHelper {
const
val
WRITE_EXTERNAL_STORAGE_CODE
=
1
fun
checkPermission
(
context
:
Context
,
permission
:
String
):
Boolean
{
return
ContextCompat
.
checkSelfPermission
(
context
,
permission
)
==
PackageManager
.
PERMISSION_GRANTED
return
ContextCompat
.
checkSelfPermission
(
context
,
permission
)
==
PackageManager
.
PERMISSION_GRANTED
}
fun
requestPermission
(
context
:
Activity
,
permission
:
String
,
requestCode
:
Int
)
{
...
...
app/src/main/java/chat/rocket/android/helper/JitsiHelper.kt
0 → 100644
View file @
f4a743d4
package
chat.rocket.android.helper
import
java.net.URL
object
JitsiHelper
{
/**
* Returns the [URL] for the Jitsi video conferencing.
*
* @param isSecureProtocol True if using SSL, false otherwise - from the public settings.
* @param domain The Jitsi domain - from public settings.
* @param prefix The Jitsi prefix - from public settings.
* @param uniqueIdentifier The server unique identifier - from public settings.
* @param chatRoomId The ChatRoom ID where the video conferencing was started.
*/
fun
getJitsiUrl
(
isSecureProtocol
:
Boolean
,
domain
:
String
?,
prefix
:
String
?,
uniqueIdentifier
:
String
?,
chatRoomId
:
String
?
):
URL
=
URL
(
getJitsiProtocol
(
isSecureProtocol
)
+
domain
+
"/"
+
prefix
+
uniqueIdentifier
+
chatRoomId
)
private
fun
getJitsiProtocol
(
isSecureProtocol
:
Boolean
)
=
if
(
isSecureProtocol
)
"https://"
else
"http://"
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/di/MainModule.kt
View file @
f4a743d4
...
...
@@ -8,7 +8,7 @@ import chat.rocket.android.main.presentation.MainView
import
chat.rocket.android.main.ui.MainActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
MainModule
{
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
f4a743d4
...
...
@@ -37,7 +37,7 @@ import chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.getCustomEmojis
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.model.Myself
import
kotlinx.coroutines.
experimental.
channels.Channel
import
kotlinx.coroutines.channels.Channel
import
timber.log.Timber
import
javax.inject.Inject
...
...
app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt
View file @
f4a743d4
...
...
@@ -27,8 +27,8 @@ import chat.rocket.core.internal.rest.deleteOwnAccount
import
chat.rocket.core.internal.rest.resetAvatar
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
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
java.util.*
import
javax.inject.Inject
...
...
@@ -180,7 +180,7 @@ class ProfilePresenter @Inject constructor(
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
// REMARK: Backend API is only working with a lowercase hash.
// https://github.com/RocketChat/Rocket.Chat/issues/12573
retryIO
{
client
.
deleteOwnAccount
(
password
.
gethash
().
toHex
().
toLowerCase
())
}
...
...
app/src/main/java/chat/rocket/android/push/DirectReplyReceiver.kt
View file @
f4a743d4
...
...
@@ -11,8 +11,8 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.internal.rest.sendMessage
import
dagger.android.AndroidInjection
import
kotlinx.coroutines.
experimental.android.UI
import
kotlinx.coroutines.
experimental.
launch
import
kotlinx.coroutines.
MainScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
import
java.util.*
import
javax.inject.Inject
...
...
@@ -36,7 +36,7 @@ class DirectReplyReceiver : BroadcastReceiver() {
if
(
ACTION_REPLY
==
intent
.
action
)
{
val
message
=
intent
.
getParcelableExtra
<
PushMessage
>(
EXTRA_PUSH_MESSAGE
)
message
?.
let
{
launch
(
UI
)
{
MainScope
().
launch
{
val
notificationId
=
it
.
notificationId
.
toInt
()
val
hostname
=
it
.
info
.
host
try
{
...
...
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
f4a743d4
...
...
@@ -29,7 +29,7 @@ import chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
com.squareup.moshi.Json
import
com.squareup.moshi.Moshi
import
kotlinx.coroutines.
experimental.
runBlocking
import
kotlinx.coroutines.runBlocking
import
se.ansman.kotshi.JsonSerializable
import
se.ansman.kotshi.KotshiConstructor
import
timber.log.Timber
...
...
app/src/main/java/chat/rocket/android/server/di/ChangeServerModule.kt
View file @
f4a743d4
...
...
@@ -3,13 +3,12 @@ package chat.rocket.android.server.di
import
androidx.lifecycle.LifecycleOwner
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerActivity
import
chat.rocket.android.dagger.scope.PerFragment
import
chat.rocket.android.server.presentation.ChangeServerNavigator
import
chat.rocket.android.server.presentation.ChangeServerView
import
chat.rocket.android.server.ui.ChangeServerActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
ChangeServerModule
{
...
...
app/src/main/java/chat/rocket/android/server/domain/ChatRoomsInteractor.kt
View file @
f4a743d4
package
chat.rocket.android.server.domain
import
chat.rocket.core.model.ChatRoom
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
javax.inject.Inject
class
ChatRoomsInteractor
@Inject
constructor
(
private
val
repository
:
ChatRoomsRepository
)
{
...
...
@@ -23,15 +23,16 @@ class ChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsR
* @param name The name of chat room to look for or a chat room that contains this name.
* @return A list of ChatRoom objects with the given name.
*/
suspend
fun
getAllByName
(
url
:
String
,
name
:
String
):
List
<
ChatRoom
>
=
withContext
(
CommonPool
)
{
val
allChatRooms
=
repository
.
get
(
url
)
if
(
name
.
isEmpty
())
{
return
@withContext
allChatRooms
suspend
fun
getAllByName
(
url
:
String
,
name
:
String
):
List
<
ChatRoom
>
=
withContext
(
Dispatchers
.
IO
)
{
val
allChatRooms
=
repository
.
get
(
url
)
if
(
name
.
isEmpty
())
{
return
@withContext
allChatRooms
}
return
@withContext
allChatRooms
.
filter
{
it
.
name
.
contains
(
name
,
true
)
}
}
return
@withContext
allChatRooms
.
filter
{
it
.
name
.
contains
(
name
,
true
)
}
}
/**
* Get a specific [ChatRoom] by its id.
...
...
@@ -40,11 +41,12 @@ class ChatRoomsInteractor @Inject constructor(private val repository: ChatRoomsR
* @param roomId The id of the room to get.
* @return The [ChatRoom] object or null if we couldn't find any.
*/
suspend
fun
getById
(
serverUrl
:
String
,
roomId
:
String
):
ChatRoom
?
=
withContext
(
CommonPool
)
{
return
@withContext
repository
.
get
(
serverUrl
).
find
{
it
.
id
==
roomId
suspend
fun
getById
(
serverUrl
:
String
,
roomId
:
String
):
ChatRoom
?
=
withContext
(
Dispatchers
.
IO
)
{
return
@withContext
repository
.
get
(
serverUrl
).
find
{
it
.
id
==
roomId
}
}
}
/**
* Get a specific [ChatRoom] by its name.
...
...
app/src/main/java/chat/rocket/android/server/domain/RefreshPermissionsInteractor.kt
View file @
f4a743d4
...
...
@@ -3,8 +3,9 @@ package chat.rocket.android.server.domain
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.retryIO
import
chat.rocket.core.internal.rest.permissions
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
timber.log.Timber
import
javax.inject.Inject
...
...
@@ -17,7 +18,7 @@ class RefreshPermissionsInteractor @Inject constructor(
)
{
fun
refreshAsync
(
server
:
String
)
{
launch
(
CommonPool
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
)
{
try
{
factory
.
create
(
server
).
let
{
client
->
val
permissions
=
retryIO
(
...
...
app/src/main/java/chat/rocket/android/server/domain/RefreshSettingsInteractor.kt
View file @
f4a743d4
...
...
@@ -3,9 +3,10 @@ package chat.rocket.android.server.domain
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.retryIO
import
chat.rocket.core.internal.rest.settings
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
javax.inject.Inject
...
...
@@ -18,6 +19,8 @@ class RefreshSettingsInteractor @Inject constructor(
)
{
private
var
settingsFilter
=
arrayOf
(
UNIQUE_IDENTIFIER
,
LDAP_ENABLE
,
CAS_ENABLE
,
CAS_LOGIN_URL
,
...
...
@@ -70,7 +73,7 @@ class RefreshSettingsInteractor @Inject constructor(
)
suspend
fun
refresh
(
server
:
String
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
factory
.
create
(
server
).
let
{
client
->
val
settings
=
retryIO
(
description
=
"settings"
,
...
...
@@ -86,7 +89,7 @@ class RefreshSettingsInteractor @Inject constructor(
}
fun
refreshAsync
(
server
:
String
)
{
launch
(
CommonPool
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
)
{
try
{
refresh
(
server
)
}
catch
(
ex
:
Exception
)
{
...
...
app/src/main/java/chat/rocket/android/server/domain/SettingsRepository.kt
View file @
f4a743d4
...
...
@@ -5,6 +5,8 @@ import chat.rocket.core.model.Value
typealias
PublicSettings
=
Map
<
String
,
Value
<
Any
>>
const
val
UNIQUE_IDENTIFIER
=
"uniqueID"
// Authentication methods
const
val
LDAP_ENABLE
=
"LDAP_Enable"
const
val
CAS_ENABLE
=
"CAS_enabled"
...
...
@@ -64,6 +66,8 @@ const val MESSAGE_READ_RECEIPT_STORE_USERS = "Message_Read_Receipt_Store_Users"
* RefreshSettingsInteractor.kt and a extension function to access it.
*/
fun
PublicSettings
.
uniqueIdentifier
():
String
?
=
this
[
UNIQUE_IDENTIFIER
]
?.
value
as
String
?
// Authentication
fun
PublicSettings
.
isLdapAuthenticationEnabled
():
Boolean
=
this
[
LDAP_ENABLE
]
?.
value
==
true
fun
PublicSettings
.
isCasAuthenticationEnabled
():
Boolean
=
this
[
CAS_ENABLE
]
?.
value
==
true
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
View file @
f4a743d4
...
...
@@ -23,26 +23,31 @@ 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
import
kotlinx.coroutines.experimental.channels.SendChannel
import
kotlinx.coroutines.experimental.channels.actor
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.newSingleThreadContext
import
kotlinx.coroutines.experimental.selects.select
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.channels.Channel
import
kotlinx.coroutines.channels.SendChannel
import
kotlinx.coroutines.channels.actor
import
kotlinx.coroutines.isActive
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.newSingleThreadContext
import
kotlinx.coroutines.selects.select
import
timber.log.Timber
import
java.util.concurrent.CopyOnWriteArrayList
import
kotlin.coroutines.
experimental.
CoroutineContext
import
kotlin.coroutines.CoroutineContext
class
ConnectionManager
(
internal
val
client
:
RocketChatClient
,
private
val
dbManager
:
DatabaseManager
)
{
)
:
CoroutineScope
{
private
var
connectJob
:
Job
?
=
null
override
val
coroutineContext
:
CoroutineContext
get
()
=
Dispatchers
.
IO
val
statusLiveData
=
MutableLiveData
<
State
>()
private
val
statusChannelList
=
CopyOnWriteArrayList
<
Channel
<
State
>>()
private
val
statusChannel
=
Channel
<
State
>(
Channel
.
CONFLATED
)
private
var
connectJob
:
Job
?
=
null
private
val
roomMessagesChannels
=
LinkedHashMap
<
String
,
Channel
<
Message
>>()
private
val
userDataChannels
=
ArrayList
<
Channel
<
Myself
>>()
...
...
@@ -60,7 +65,7 @@ class ConnectionManager(
private
val
messagesContext
=
newSingleThreadContext
(
"messagesContext"
)
fun
connect
()
{
if
(
connectJob
?.
isActive
==
true
&&
(
state
!
is
State
.
Disconnected
)
)
{
if
(
connectJob
?.
isActive
==
true
&&
state
!
is
State
.
Disconnected
)
{
Timber
.
d
(
"Already connected, just returning..."
)
return
}
...
...
@@ -78,32 +83,31 @@ class ConnectionManager(
when
(
status
)
{
is
State
.
Connected
->
{
dbManager
.
clearUsersStatus
()
client
.
subscribeSubscriptions
{
_
,
id
->
Timber
.
d
(
"Subscribed to subscriptions: $id"
)
subscriptionId
=
id
}
client
.
subscribeRooms
{
_
,
id
->
Timber
.
d
(
"Subscribed to rooms: $id"
)
roomsId
=
id
}
client
.
subscribeUserData
{
_
,
id
->
Timber
.
d
(
"Subscribed to the userData id: $id"
)
userDataId
=
id
}
client
.
subscribeActiveUsers
{
_
,
id
->
Timber
.
d
(
"Subscribed to the activeUser id: $id"
)
activeUserId
=
id
}
resubscribeRooms
()
temporaryStatus
?.
let
{
status
->
client
.
setTemporaryStatus
(
status
)
}
}
is
State
.
Waiting
->
{
Timber
.
d
(
"Connection in: ${status.seconds}"
)
temporaryStatus
?.
let
{
client
.
setTemporaryStatus
(
it
)
}
}
is
State
.
Waiting
->
Timber
.
d
(
"Connection in: ${status.seconds}"
)
}
statusLiveData
.
postValue
(
status
)
...
...
@@ -116,8 +120,9 @@ class ConnectionManager(
}
var
totalBatchedUsers
=
0
val
userActor
=
createBatchActor
<
User
>(
activeUsersContext
,
parent
=
connectJob
,
maxSize
=
500
,
maxTime
=
1000
)
{
users
->
val
userActor
=
createBatchActor
<
User
>(
activeUsersContext
,
parent
=
connectJob
,
maxSize
=
500
,
maxTime
=
1000
)
{
users
->
totalBatchedUsers
+=
users
.
size
Timber
.
d
(
"Processing Users batch: ${users.size} - $totalBatchedUsers"
)
...
...
@@ -125,8 +130,9 @@ class ConnectionManager(
dbManager
.
processUsersBatch
(
users
)
}
val
roomsActor
=
createBatchActor
<
StreamMessage
<
BaseRoom
>>(
roomsContext
,
parent
=
connectJob
,
maxSize
=
10
)
{
batch
->
val
roomsActor
=
createBatchActor
<
StreamMessage
<
BaseRoom
>>(
roomsContext
,
parent
=
connectJob
,
maxSize
=
10
)
{
batch
->
Timber
.
d
(
"processing Stream batch: ${batch.size} - $batch"
)
dbManager
.
processChatRoomsBatch
(
batch
)
...
...
@@ -135,16 +141,15 @@ class ConnectionManager(
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
)
}
roomsChannels
[
it
.
data
.
id
]
?.
offer
(
room
)
}
}
}
}
val
messagesActor
=
createBatchActor
<
Message
>(
messagesContext
,
parent
=
connectJob
,
maxSize
=
100
,
maxTime
=
500
)
{
messages
->
val
messagesActor
=
createBatchActor
<
Message
>(
messagesContext
,
parent
=
connectJob
,
maxSize
=
100
,
maxTime
=
500
)
{
messages
->
Timber
.
d
(
"Processing Messages batch: ${messages.size}"
)
dbManager
.
processMessagesBatch
(
messages
.
distinctBy
{
it
.
id
})
...
...
@@ -157,7 +162,7 @@ class ConnectionManager(
}
// stream-notify-user - ${userId}/rooms-changed
launch
(
parent
=
connectJob
)
{
launch
{
for
(
room
in
client
.
roomsChannel
)
{
Timber
.
d
(
"GOT Room streamed"
)
roomsActor
.
send
(
room
)
...
...
@@ -170,7 +175,7 @@ class ConnectionManager(
}
// stream-notify-user - ${userId}/subscriptions-changed
launch
(
parent
=
connectJob
)
{
launch
{
for
(
subscription
in
client
.
subscriptionsChannel
)
{
Timber
.
d
(
"GOT Subscription streamed"
)
roomsActor
.
send
(
subscription
)
...
...
@@ -178,7 +183,7 @@ class ConnectionManager(
}
// stream-room-messages - $roomId
launch
(
parent
=
connectJob
)
{
launch
{
for
(
message
in
client
.
messagesChannel
)
{
Timber
.
d
(
"Received new Message for room ${message.roomId}"
)
messagesActor
.
send
(
message
)
...
...
@@ -186,7 +191,7 @@ class ConnectionManager(
}
// userData
launch
(
parent
=
connectJob
)
{
launch
{
for
(
myself
in
client
.
userDataChannel
)
{
Timber
.
d
(
"Got userData"
)
dbManager
.
updateSelfUser
(
myself
)
...
...
@@ -197,7 +202,7 @@ class ConnectionManager(
}
// activeUsers
launch
(
parent
=
connectJob
)
{
launch
{
for
(
user
in
client
.
activeUsersChannel
)
{
userActor
.
send
(
user
)
}
...
...
@@ -286,16 +291,18 @@ class ConnectionManager(
}
}
private
inline
fun
<
T
>
createBatchActor
(
context
:
CoroutineContext
=
CommonPool
,
parent
:
Job
?
=
null
,
maxSize
:
Int
=
100
,
maxTime
:
Int
=
500
,
crossinline
block
:
(
List
<
T
>)
->
Unit
):
SendChannel
<
T
>
{
return
actor
(
context
,
parent
=
parent
)
{
private
inline
fun
<
T
>
createBatchActor
(
context
:
CoroutineContext
=
Dispatchers
.
IO
,
parent
:
Job
?
=
null
,
maxSize
:
Int
=
100
,
maxTime
:
Int
=
500
,
crossinline
block
:
(
List
<
T
>)
->
Unit
):
SendChannel
<
T
>
{
return
actor
(
context
)
{
val
batch
=
ArrayList
<
T
>(
maxSize
)
var
deadline
=
0L
// deadline for sending this batch to callback block
while
(
true
)
{
while
(
true
)
{
// when deadline is reached or size is exceeded, pass the batch to the callback block
val
remainingTime
=
deadline
-
System
.
currentTimeMillis
()
if
(
batch
.
isNotEmpty
()
&&
remainingTime
<=
0
||
batch
.
size
>=
maxSize
)
{
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/DatabaseMessageMapper.kt
View file @
f4a743d4
...
...
@@ -14,7 +14,6 @@ import chat.rocket.core.model.Message
import
chat.rocket.core.model.Reactions
import
chat.rocket.core.model.attachment.Attachment
import
chat.rocket.core.model.attachment.Color
import
chat.rocket.core.model.attachment.DEFAULT_COLOR_STR
import
chat.rocket.core.model.attachment.Field
import
chat.rocket.core.model.attachment.actions.Action
import
chat.rocket.core.model.attachment.actions.ButtonAction
...
...
@@ -22,8 +21,8 @@ import chat.rocket.core.model.messageTypeOf
import
chat.rocket.core.model.url.Meta
import
chat.rocket.core.model.url.ParsedUrl
import
chat.rocket.core.model.url.Url
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
class
DatabaseMessageMapper
(
private
val
dbManager
:
DatabaseManager
)
{
suspend
fun
map
(
message
:
FullMessage
):
Message
?
=
map
(
listOf
(
message
)).
firstOrNull
()
...
...
@@ -58,7 +57,8 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
val
attachments
=
this
.
attachments
?.
let
{
mapAttachments
(
it
).
asReversed
()
}
val
messageType
=
messageTypeOf
(
this
.
message
.
type
)
list
.
add
(
Message
(
list
.
add
(
Message
(
id
=
this
.
message
.
id
,
roomId
=
this
.
message
.
roomId
,
message
=
this
.
message
.
message
,
...
...
@@ -82,7 +82,8 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
role
=
this
.
message
.
role
,
synced
=
this
.
message
.
synced
,
unread
=
this
.
message
.
unread
))
)
)
}
}
...
...
@@ -106,13 +107,19 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
val
parsedUrl
=
url
.
hostname
?.
let
{
ParsedUrl
(
host
=
it
)
}
val
meta
=
if
(!
url
.
description
.
isNullOrEmpty
()
||
!
url
.
imageUrl
.
isNullOrEmpty
()
||
!
url
.
title
.
isNullOrEmpty
())
{
val
raw
=
HashMap
<
String
,
String
>()
if
(
url
.
description
!=
null
)
raw
[
"ogDescription"
]
=
url
.
description
if
(
url
.
title
!=
null
)
raw
[
"ogTitle"
]
=
url
.
title
if
(
url
.
imageUrl
!=
null
)
raw
[
"ogImage"
]
=
url
.
imageUrl
Meta
(
title
=
url
.
title
,
description
=
url
.
description
,
imageUrl
=
url
.
imageUrl
,
raw
=
raw
)
}
else
null
val
meta
=
if
(!
url
.
description
.
isNullOrEmpty
()
||
!
url
.
imageUrl
.
isNullOrEmpty
()
||
!
url
.
title
.
isNullOrEmpty
())
{
val
raw
=
HashMap
<
String
,
String
>()
if
(
url
.
description
!=
null
)
raw
[
"ogDescription"
]
=
url
.
description
if
(
url
.
title
!=
null
)
raw
[
"ogTitle"
]
=
url
.
title
if
(
url
.
imageUrl
!=
null
)
raw
[
"ogImage"
]
=
url
.
imageUrl
Meta
(
title
=
url
.
title
,
description
=
url
.
description
,
imageUrl
=
url
.
imageUrl
,
raw
=
raw
)
}
else
null
list
.
add
(
Url
(
url
=
url
.
url
,
meta
=
meta
,
parsedUrl
=
parsedUrl
))
}
...
...
@@ -135,7 +142,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
attachments
.
forEach
{
attachment
->
with
(
attachment
)
{
val
fields
=
if
(
hasFields
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getAttachmentFields(${attachment._id})"
)
{
dbManager
.
messageDao
().
getAttachmentFields
(
attachment
.
_id
)
}
...
...
@@ -144,7 +151,7 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
null
}
val
actions
=
if
(
hasActions
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getAttachmentActions(${attachment._id})"
)
{
dbManager
.
messageDao
().
getAttachmentActions
(
attachment
.
_id
)
}
...
...
@@ -154,33 +161,34 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
}
val
attachment
=
Attachment
(
title
=
title
,
type
=
type
,
description
=
description
,
authorName
=
authorName
,
text
=
text
,
thumbUrl
=
thumbUrl
,
color
=
color
?.
let
{
Color
.
Custom
(
color
)
},
titleLink
=
titleLink
,
titleLinkDownload
=
titleLinkDownload
,
imageUrl
=
imageUrl
,
imageType
=
imageType
,
imageSize
=
imageSize
,
videoUrl
=
videoUrl
,
videoType
=
videoType
,
videoSize
=
videoSize
,
audioUrl
=
audioUrl
,
audioType
=
audioType
,
audioSize
=
audioSize
,
messageLink
=
messageLink
,
attachments
=
null
,
// HOW TO MAP THIS
timestamp
=
timestamp
,
authorIcon
=
authorIcon
,
authorLink
=
authorLink
,
fields
=
fields
,
fallback
=
fallback
,
buttonAlignment
=
if
(
actions
!=
null
&&
actions
.
isNotEmpty
())
buttonAlignment
?:
"vertical"
else
null
,
actions
=
actions
title
=
title
,
type
=
type
,
description
=
description
,
authorName
=
authorName
,
text
=
text
,
thumbUrl
=
thumbUrl
,
color
=
color
?.
let
{
Color
.
Custom
(
color
)
},
titleLink
=
titleLink
,
titleLinkDownload
=
titleLinkDownload
,
imageUrl
=
imageUrl
,
imageType
=
imageType
,
imageSize
=
imageSize
,
videoUrl
=
videoUrl
,
videoType
=
videoType
,
videoSize
=
videoSize
,
audioUrl
=
audioUrl
,
audioType
=
audioType
,
audioSize
=
audioSize
,
messageLink
=
messageLink
,
attachments
=
null
,
// HOW TO MAP THIS
timestamp
=
timestamp
,
authorIcon
=
authorIcon
,
authorLink
=
authorLink
,
fields
=
fields
,
fallback
=
fallback
,
buttonAlignment
=
if
(
actions
!=
null
&&
actions
.
isNotEmpty
())
buttonAlignment
?:
"vertical"
else
null
,
actions
=
actions
)
list
.
add
(
attachment
)
}
...
...
@@ -190,9 +198,11 @@ class DatabaseMessageMapper(private val dbManager: DatabaseManager) {
private
fun
mapAction
(
action
:
AttachmentActionEntity
):
Action
?
{
return
when
(
action
.
type
)
{
"button"
->
ButtonAction
(
action
.
type
,
action
.
text
,
action
.
url
,
action
.
isWebView
,
action
.
webViewHeightRatio
,
action
.
imageUrl
,
action
.
message
,
action
.
isMessageInChatWindow
)
"button"
->
ButtonAction
(
action
.
type
,
action
.
text
,
action
.
url
,
action
.
isWebView
,
action
.
webViewHeightRatio
,
action
.
imageUrl
,
action
.
message
,
action
.
isMessageInChatWindow
)
else
->
null
}
}
...
...
app/src/main/java/chat/rocket/android/server/infraestructure/DatabaseMessagesRepository.kt
View file @
f4a743d4
...
...
@@ -6,41 +6,42 @@ 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
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
class
DatabaseMessagesRepository
(
private
val
dbManager
:
DatabaseManager
,
private
val
mapper
:
DatabaseMessageMapper
)
:
MessagesRepository
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
CommonPool
)
{
override
suspend
fun
getById
(
id
:
String
):
Message
?
=
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getMessageById($id)"
)
{
dbManager
.
messageDao
().
getMessageById
(
id
)
?.
let
{
message
->
mapper
.
map
(
message
)
}
}
}
override
suspend
fun
getByRoomId
(
roomId
:
String
):
List
<
Message
>
=
withContext
(
CommonPool
)
{
override
suspend
fun
getByRoomId
(
roomId
:
String
):
List
<
Message
>
=
withContext
(
Dispatchers
.
IO
)
{
// 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
)
}
.
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
)
override
suspend
fun
getRecentMessages
(
roomId
:
String
,
count
:
Long
):
List
<
Message
>
=
withContext
(
Dispatchers
.
IO
)
{
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
()
...
...
@@ -51,24 +52,24 @@ class DatabaseMessagesRepository(
}
override
suspend
fun
removeById
(
id
:
String
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"delete($id)"
)
{
dbManager
.
messageDao
().
delete
(
id
)
}
}
}
override
suspend
fun
removeByRoomId
(
roomId
:
String
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"deleteByRoomId($roomId)"
)
{
dbManager
.
messageDao
().
deleteByRoomId
(
roomId
)
}
}
}
override
suspend
fun
getAllUnsent
():
List
<
Message
>
=
withContext
(
CommonPool
)
{
override
suspend
fun
getAllUnsent
():
List
<
Message
>
=
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getUnsentMessages"
)
{
dbManager
.
messageDao
().
getUnsentMessages
()
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
mapper
.
map
(
it
)
}
.
distinctBy
{
it
.
message
.
message
.
id
}
.
let
{
mapper
.
map
(
it
)
}
}
}
...
...
@@ -76,7 +77,7 @@ class DatabaseMessagesRepository(
dbManager
.
sendOperation
(
Operation
.
SaveLastSync
(
MessagesSync
(
roomId
,
timeMillis
)))
}
override
suspend
fun
getLastSyncDate
(
roomId
:
String
):
Long
?
=
withContext
(
CommonPool
)
{
override
suspend
fun
getLastSyncDate
(
roomId
:
String
):
Long
?
=
withContext
(
Dispatchers
.
IO
)
{
retryDB
(
"getLastSync($roomId)"
)
{
dbManager
.
messageDao
().
getLastSync
(
roomId
)
?.
let
{
it
.
timestamp
}
}
...
...
app/src/main/java/chat/rocket/android/server/presentation/CheckServerPresenter.kt
View file @
f4a743d4
...
...
@@ -45,10 +45,10 @@ import chat.rocket.core.internal.rest.serverInfo
import
chat.rocket.core.internal.rest.settingsOauth
import
chat.rocket.core.internal.rest.unregisterPushToken
import
chat.rocket.core.model.Myself
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.
experimental.
channels.Channel
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.channels.Channel
import
kotlinx.coroutines.withContext
import
timber.log.Timber
private
const
val
SERVICE_NAME_FACEBOOK
=
"facebook"
...
...
@@ -220,7 +220,7 @@ abstract class CheckServerPresenter constructor(
}
removeAccountInteractor
?.
remove
(
currentServer
)
tokenRepository
?.
remove
(
currentServer
)
withContext
(
CommonPool
)
{
dbManager
.
logout
()
}
withContext
(
Dispatchers
.
IO
)
{
dbManager
.
logout
()
}
navigator
?.
switchOrAddNewServer
()
}
catch
(
ex
:
Exception
)
{
Timber
.
e
(
ex
,
"Error cleaning up the session..."
)
...
...
app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt
View file @
f4a743d4
...
...
@@ -26,7 +26,6 @@ fun Context.changeServerIntent(serverUrl: String? = null, chatRoomId: String? =
class
ChangeServerActivity
:
AppCompatActivity
(),
ChangeServerView
{
@Inject
lateinit
var
presenter
:
ChangeServerPresenter
var
progress
:
ProgressDialog
?
=
null
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
...
...
app/src/main/java/chat/rocket/android/settings/di/SettingsFragmentModule.kt
View file @
f4a743d4
...
...
@@ -7,7 +7,7 @@ import chat.rocket.android.settings.presentation.SettingsView
import
chat.rocket.android.settings.ui.SettingsFragment
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
SettingsFragmentModule
{
...
...
app/src/main/java/chat/rocket/android/settings/password/di/PasswordFragmentModule.kt
View file @
f4a743d4
...
...
@@ -7,7 +7,7 @@ import chat.rocket.android.settings.password.presentation.PasswordView
import
chat.rocket.android.settings.password.ui.PasswordFragment
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
PasswordFragmentModule
{
...
...
app/src/main/java/chat/rocket/android/userdetails/presentation/UserDetailsPresenter.kt
View file @
f4a743d4
...
...
@@ -16,8 +16,8 @@ import chat.rocket.android.util.retryIO
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.rest.createDirectMessage
import
kotlinx.coroutines.
experimental.DefaultDispatcher
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
timber.log.Timber
import
javax.inject.Inject
...
...
@@ -81,7 +81,7 @@ class UserDetailsPresenter @Inject constructor(
try
{
view
.
showLoading
()
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
directMessage
=
retryIO
(
"createDirectMessage($username"
)
{
client
.
createDirectMessage
(
username
)
}
...
...
app/src/main/java/chat/rocket/android/util/IO.kt
View file @
f4a743d4
...
...
@@ -2,26 +2,25 @@ 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
import
kotlinx.coroutines.experimental.isActive
import
kotlinx.coroutines.delay
import
kotlinx.coroutines.isActive
import
timber.log.Timber
import
kotlin.coroutines.
experimental.
coroutineContext
import
kotlin.coroutines.coroutineContext
const
val
DEFAULT_RETRY
=
3
private
const
val
DEFAULT_DB_RETRY
=
15
suspend
fun
<
T
>
retryIO
(
description
:
String
=
"<missing description>"
,
times
:
Int
=
DEFAULT_RETRY
,
initialDelay
:
Long
=
100
,
// 0.1 second
maxDelay
:
Long
=
1000
,
// 1 second
factor
:
Double
=
2.0
,
block
:
suspend
()
->
T
):
T
{
description
:
String
=
"<missing description>"
,
times
:
Int
=
DEFAULT_RETRY
,
initialDelay
:
Long
=
100
,
// 0.1 second
maxDelay
:
Long
=
1000
,
// 1 second
factor
:
Double
=
2.0
,
block
:
suspend
()
->
T
):
T
{
var
currentDelay
=
initialDelay
repeat
(
times
-
1
)
{
currentTry
->
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryIO
"
)
try
{
return
block
()
}
catch
(
e
:
RocketChatNetworkErrorException
)
{
...
...
@@ -29,26 +28,26 @@ suspend fun <T> retryIO(
e
.
printStackTrace
()
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryIO
"
)
delay
(
currentDelay
)
currentDelay
=
(
currentDelay
*
factor
).
toLong
().
coerceAtMost
(
maxDelay
)
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryIO
"
)
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
=
1500
,
// 1.5 second
factor
:
Double
=
1.2
,
block
:
suspend
()
->
T
):
T
{
description
:
String
=
"<missing description>"
,
times
:
Int
=
DEFAULT_DB_RETRY
,
initialDelay
:
Long
=
100
,
// 0.1 second
maxDelay
:
Long
=
1500
,
// 1.5 second
factor
:
Double
=
1.2
,
block
:
suspend
()
->
T
):
T
{
var
currentDelay
=
initialDelay
repeat
(
times
-
1
)
{
currentTry
->
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryDB
"
)
try
{
return
block
()
}
catch
(
e
:
SQLiteDatabaseLockedException
)
{
...
...
@@ -56,11 +55,11 @@ suspend fun <T> retryDB(
e
.
printStackTrace
()
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryDB
"
)
delay
(
currentDelay
)
currentDelay
=
(
currentDelay
*
factor
).
toLong
().
coerceAtMost
(
maxDelay
)
}
if
(!
coroutineContext
.
isActive
)
throw
TimeoutCancellationException
(
"job canceled
"
)
if
(!
coroutineContext
.
isActive
)
throw
Exception
(
"Job canceled when trying to execute retryDB
"
)
return
block
()
// last attempt
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/extensions/Fragment.kt
View file @
f4a743d4
...
...
@@ -3,9 +3,10 @@ package chat.rocket.android.util.extensions
import
android.os.Looper
import
androidx.fragment.app.Fragment
import
androidx.fragment.app.FragmentActivity
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.launch
inline
fun
Fragment
.
ui
(
crossinline
block
:
(
activity
:
FragmentActivity
)
->
Unit
):
Job
?
{
// Checking first for activity and view saves us from some synchronyzed and thread local checks
...
...
@@ -16,7 +17,7 @@ inline fun Fragment.ui(crossinline block: (activity: FragmentActivity) -> Unit):
null
}
else
{
// Launch a Job on the UI context and check again if the activity and view are still valid
launch
(
UI
)
{
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
if
(
activity
!=
null
&&
view
!=
null
&&
context
!=
null
)
{
block
(
activity
!!
)
}
...
...
app/src/main/java/chat/rocket/android/util/extensions/RocketChatClient.kt
View file @
f4a743d4
...
...
@@ -7,15 +7,15 @@ import chat.rocket.android.util.retryIO
import
chat.rocket.core.internal.rest.registerPushToken
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.asString
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.
withContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
timber.log.Timber
suspend
fun
RocketChatClientFactory
.
registerPushToken
(
token
:
String
,
accounts
:
List
<
Account
>
)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
accounts
.
forEach
{
account
->
try
{
retryIO
(
description
=
"register push token: ${account.serverUrl}"
)
{
...
...
app/src/main/java/chat/rocket/android/util/livedata/TransformedLiveData.kt
View file @
f4a743d4
...
...
@@ -2,28 +2,28 @@ package chat.rocket.android.util.livedata
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.Observer
import
kotlinx.coroutines.
experimental.CommonPool
import
kotlinx.coroutines.
experimental.Job
import
kotlinx.coroutines.
experimental.android.UI
import
kotlinx.coroutines.
experimental.
launch
import
kotlinx.coroutines.
experimental.
withContext
import
kotlin.coroutines.
experimental.
CoroutineContext
import
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.
GlobalScope
import
kotlinx.coroutines.
Job
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
kotlin.coroutines.CoroutineContext
class
TransformedLiveData
<
Source
,
Output
>(
private
val
runContext
:
CoroutineContext
=
CommonPool
,
private
val
runContext
:
CoroutineContext
=
Dispatchers
.
IO
,
private
val
source
:
LiveData
<
Source
>,
private
val
transformation
:
(
Source
?)
->
Output
?
)
:
LiveData
<
Output
>()
{
private
val
transformation
:
(
Source
?)
->
Output
?
)
:
LiveData
<
Output
>()
{
private
var
job
:
Job
?
=
null
private
val
observer
=
Observer
<
Source
>
{
source
->
job
?.
cancel
()
job
=
launch
(
runContext
)
{
job
=
GlobalScope
.
launch
(
runContext
)
{
transformation
(
source
)
?.
let
{
transformed
->
// Could have used postValue instead, but using the UI context I can guarantee that
// a canceled job will never emit values.
withContext
(
UI
)
{
withContext
(
Dispatchers
.
Main
)
{
value
=
transformed
}
}
...
...
@@ -41,5 +41,6 @@ class TransformedLiveData<Source, Output>(
}
fun
<
Source
,
Output
>
LiveData
<
Source
>.
transform
(
runContext
:
CoroutineContext
=
CommonPool
,
transformation
:
(
Source
?)
->
Output
?)
=
TransformedLiveData
(
runContext
,
this
,
transformation
)
\ No newline at end of file
runContext
:
CoroutineContext
=
Dispatchers
.
IO
,
transformation
:
(
Source
?)
->
Output
?
)
=
TransformedLiveData
(
runContext
,
this
,
transformation
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/livedata/WrappedLiveData.kt
View file @
f4a743d4
...
...
@@ -3,21 +3,22 @@ package chat.rocket.android.util.livedata
import
androidx.lifecycle.LiveData
import
androidx.lifecycle.MutableLiveData
import
androidx.lifecycle.Observer
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Job
import
kotlinx.coroutines.experimental.launch
import
kotlin.coroutines.experimental.CoroutineContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.launch
import
kotlin.coroutines.CoroutineContext
class
WrappedLiveData
<
Source
,
Output
>(
private
val
runContext
:
CoroutineContext
=
CommonPool
,
private
val
runContext
:
CoroutineContext
=
Dispatchers
.
IO
,
private
val
source
:
LiveData
<
Source
>,
private
val
transformation
:
suspend
(
Source
?,
MutableLiveData
<
Output
>)
->
Unit
)
:
MutableLiveData
<
Output
>()
{
private
val
transformation
:
suspend
(
Source
?,
MutableLiveData
<
Output
>)
->
Unit
)
:
MutableLiveData
<
Output
>()
{
private
var
job
:
Job
?
=
null
private
val
observer
=
Observer
<
Source
>
{
source
->
job
?.
cancel
()
job
=
launch
(
runContext
)
{
job
=
GlobalScope
.
launch
(
runContext
)
{
transformation
(
source
,
this
@WrappedLiveData
)
}
}
...
...
@@ -33,6 +34,7 @@ class WrappedLiveData<Source, Output>(
}
fun
<
Source
,
Output
>
LiveData
<
Source
>.
wrap
(
runContext
:
CoroutineContext
=
CommonPool
,
transformation
:
suspend
(
Source
?,
MutableLiveData
<
Output
>)
->
Unit
)
=
WrappedLiveData
(
runContext
,
this
,
transformation
)
\ No newline at end of file
runContext
:
CoroutineContext
=
Dispatchers
.
IO
,
transformation
:
suspend
(
Source
?,
MutableLiveData
<
Output
>)
->
Unit
)
=
WrappedLiveData
(
runContext
,
this
,
transformation
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/videoconferencing/di/VideoConferencingModule.kt
0 → 100644
View file @
f4a743d4
package
chat.rocket.android.videoconferencing.di
import
androidx.lifecycle.LifecycleOwner
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.dagger.scope.PerActivity
import
chat.rocket.android.videoconferencing.presenter.VideoConferencingView
import
chat.rocket.android.videoconferencing.ui.VideoConferencingActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.Job
@Module
class
VideoConferencingModule
{
@Provides
@PerActivity
fun
provideVideoConferencingView
(
activity
:
VideoConferencingActivity
):
VideoConferencingView
{
return
activity
}
@Provides
@PerActivity
fun
provideJob
()
=
Job
()
@Provides
@PerActivity
fun
provideLifecycleOwner
(
activity
:
VideoConferencingActivity
):
LifecycleOwner
=
activity
@Provides
@PerActivity
fun
provideCancelStrategy
(
owner
:
LifecycleOwner
,
jobs
:
Job
):
CancelStrategy
=
CancelStrategy
(
owner
,
jobs
)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/videoconferencing/presenter/VideoConferencingPresenter.kt
0 → 100644
View file @
f4a743d4
package
chat.rocket.android.videoconferencing.presenter
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.JitsiHelper
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.infraestructure.ConnectionManager
import
chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.util.extension.launchUI
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.realtime.updateJitsiTimeout
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
javax.inject.Inject
class
VideoConferencingPresenter
@Inject
constructor
(
private
val
view
:
VideoConferencingView
,
private
val
strategy
:
CancelStrategy
,
private
val
currentServerRepository
:
CurrentServerRepository
,
private
val
connectionManagerFactory
:
ConnectionManagerFactory
,
private
val
settings
:
GetSettingsInteractor
)
{
private
lateinit
var
currentServerUrl
:
String
private
lateinit
var
connectionManager
:
ConnectionManager
private
lateinit
var
client
:
RocketChatClient
private
lateinit
var
publicSettings
:
PublicSettings
private
lateinit
var
chatRoomId
:
String
fun
setup
(
chatRoomId
:
String
)
{
currentServerRepository
.
get
()
?.
let
{
currentServerUrl
=
it
connectionManager
=
connectionManagerFactory
.
create
(
it
)
client
=
connectionManager
.
client
publicSettings
=
settings
.
get
(
it
)
}
this
.
chatRoomId
=
chatRoomId
}
fun
setupVideoConferencing
()
{
launchUI
(
strategy
)
{
with
(
publicSettings
)
{
view
.
startVideoConferencing
(
JitsiHelper
.
getJitsiUrl
(
isJitsiSSL
(),
jitsiDomain
(),
jitsiPrefix
(),
uniqueIdentifier
(),
chatRoomId
)
)
}
client
.
updateJitsiTimeout
(
chatRoomId
)
}
}
private
fun
updateJitsiTimeout
()
{
GlobalScope
.
launch
(
Dispatchers
.
IO
+
strategy
.
jobs
)
{
client
.
updateJitsiTimeout
(
chatRoomId
)
}
}
}
app/src/main/java/chat/rocket/android/videoconferencing/presenter/VideoConferencingView.kt
0 → 100644
View file @
f4a743d4
package
chat.rocket.android.videoconferencing.presenter
import
java.net.URL
interface
VideoConferencingView
{
/**
* Starts the video conferencing.
*
* @param url The video conferencing URL to be loaded.
*/
fun
startVideoConferencing
(
url
:
URL
)
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/videoconferencing/ui/VideoConferencingActivity.kt
0 → 100644
View file @
f4a743d4
package
chat.rocket.android.videoconferencing.ui
import
android.content.Context
import
android.content.Intent
import
android.os.Bundle
import
chat.rocket.android.videoconferencing.presenter.VideoConferencingPresenter
import
chat.rocket.android.videoconferencing.presenter.VideoConferencingView
import
dagger.android.AndroidInjection
import
org.jitsi.meet.sdk.JitsiMeetActivity
import
java.net.URL
import
javax.inject.Inject
fun
Context
.
videoConferencingIntent
(
chatRoomId
:
String
):
Intent
=
Intent
(
this
,
VideoConferencingActivity
::
class
.
java
).
putExtra
(
INTENT_CHAT_ROOM_ID
,
chatRoomId
)
private
const
val
INTENT_CHAT_ROOM_ID
=
"chat_room_id"
class
VideoConferencingActivity
:
JitsiMeetActivity
(),
VideoConferencingView
{
@Inject
lateinit
var
presenter
:
VideoConferencingPresenter
private
lateinit
var
chatRoomId
:
String
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
AndroidInjection
.
inject
(
this
)
super
.
onCreate
(
savedInstanceState
)
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
requireNotNull
(
chatRoomId
)
{
"no chat_room_id provided in Intent extras"
}
presenter
.
setup
(
chatRoomId
)
presenter
.
setupVideoConferencing
()
}
override
fun
startVideoConferencing
(
url
:
URL
)
=
loadURL
(
url
)
}
app/src/main/java/chat/rocket/android/webview/ui/WebViewActivity.kt
View file @
f4a743d4
...
...
@@ -31,8 +31,8 @@ class WebViewActivity : AppCompatActivity() {
setContentView
(
R
.
layout
.
activity_web_view
)
webPageUrl
=
intent
.
getStringExtra
(
INTENT_WEB_PAGE_URL
)
toolbarTitle
=
intent
.
getStringExtra
(
TOOLBAR_TITLE
)
requireNotNull
(
webPageUrl
)
{
"no web_page_url provided in Intent extras"
}
toolbarTitle
=
intent
.
getStringExtra
(
TOOLBAR_TITLE
)
setupToolbar
()
}
...
...
app/src/main/res/layout/item_message.xml
View file @
f4a743d4
...
...
@@ -159,6 +159,16 @@
app:layout_constraintTop_toBottomOf=
"@+id/message_header"
tools:text=
"This is a multiline chat message from Bertie that will take more than just one line of text. I have sure that everything is amazing!"
/>
<Button
android:id=
"@+id/button_join_video_call"
android:layout_width=
"200dp"
android:layout_height=
"wrap_content"
android:background=
"@color/colorAccent"
android:text=
"@string/msg_join_video_call"
android:visibility=
"gone"
app:layout_constraintStart_toStartOf=
"@+id/text_content"
app:layout_constraintTop_toBottomOf=
"@+id/text_content"
/>
<include
layout=
"@layout/layout_reactions"
android:layout_width=
"0dp"
...
...
app/src/main/res/values-de/strings.xml
View file @
f4a743d4
...
...
@@ -100,6 +100,7 @@
<string
name=
"msg_today"
>
Heute
</string>
<string
name=
"msg_message"
>
Nachricht
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Dieser Raum ist nur lesen
</string>
<string
name=
"msg_invalid_2fa_code"
>
Falscher 2FA Code
</string>
<string
name=
"msg_invalid_file"
>
Falsche Datei
</string>
...
...
app/src/main/res/values-es/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Hoy
</string>
<string
name=
"msg_message"
>
Mensaje
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Esta sala es de solo lectura
</string>
<string
name=
"msg_invalid_2fa_code"
>
Código 2FA no válido
</string>
<string
name=
"msg_invalid_file"
>
Archivo inválido
</string>
...
...
app/src/main/res/values-fa/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
امروز
</string>
<string
name=
"msg_message"
>
پیام
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
این اتاق فقط خواندنی است
</string>
<string
name=
"msg_invalid_2fa_code"
>
Invalid 2FA Code
</string>
<!-- TODO Add translation -->
<string
name=
"msg_invalid_file"
>
پروندهی نامعتبر
</string>
...
...
app/src/main/res/values-fr/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Aujourd\'hui
</string>
<string
name=
"msg_message"
>
Message
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Ce salon est en lecture seule
</string>
<string
name=
"msg_invalid_2fa_code"
>
Code 2FA non valide
</string>
<string
name=
"msg_invalid_file"
>
Fichier non valide
</string>
...
...
app/src/main/res/values-hi-rIN/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
आज
</string>
<string
name=
"msg_message"
>
संदेश
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
यह रूम केवल पढ़ने के लिए है
</string>
<string
name=
"msg_invalid_2fa_code"
>
अमान्य 2FA कोड
</string>
<string
name=
"msg_invalid_file"
>
अवैध फाइल
</string>
...
...
app/src/main/res/values-it/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Oggi
</string>
<string
name=
"msg_message"
>
Messaggio
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Questa stanza è di sola lettura
</string>
<string
name=
"msg_invalid_2fa_code"
>
Invalido Codice 2FA non valido
</string>
<string
name=
"msg_invalid_file"
>
Documento non valido
</string>
...
...
app/src/main/res/values-ja/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_yesterday"
>
昨日
</string>
<string
name=
"msg_message"
>
メッセージ
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
この部屋は読み取り専用です
</string>
<string
name=
"msg_invalid_2fa_code"
>
無効な 2FA コード
</string>
<string
name=
"msg_invalid_file"
>
無効なファイル
</string>
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Hoje
</string>
<string
name=
"msg_message"
>
Mensagem
</string>
<string
name=
"msg_video_call"
>
Videochamada
</string>
<string
name=
"msg_join_video_call"
>
Entrar na videochamada
</string>
<string
name=
"msg_this_room_is_read_only"
>
Este chat é apenas de leitura
</string>
<string
name=
"msg_invalid_2fa_code"
>
Código 2FA inválido
</string>
<string
name=
"msg_invalid_file"
>
Arquivo inválido
</string>
...
...
app/src/main/res/values-ru-rRU/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Сегодня
</string>
<string
name=
"msg_message"
>
Сообщение
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Канал только для чтения
</string>
<string
name=
"msg_invalid_2fa_code"
>
Неверный код 2FA
</string>
<string
name=
"msg_invalid_file"
>
Неверный файл
</string>
...
...
app/src/main/res/values-tr/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Bugün
</string>
<string
name=
"msg_message"
>
Mesaj
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Bu oda sadece okunabilir modundadır
</string>
<string
name=
"msg_invalid_2fa_code"
>
Geçersiz 2FA Kodu
</string>
<string
name=
"msg_invalid_file"
>
Geçersiz dosya
</string>
...
...
app/src/main/res/values-uk/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
Сьогодні
</string>
<string
name=
"msg_message"
>
Повідомлення
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
Канал тільки для читання
</string>
<string
name=
"msg_invalid_2fa_code"
>
Неправильний код 2FA
</string>
<string
name=
"msg_invalid_file"
>
Неправильний файл
</string>
...
...
app/src/main/res/values-zh-rCN/strings.xml
View file @
f4a743d4
...
...
@@ -97,6 +97,7 @@
<string
name=
"msg_today"
>
今天
</string>
<string
name=
"msg_message"
>
消息
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<!-- TODO Add translation -->
<string
name=
"msg_this_room_is_read_only"
>
这个频道只读
</string>
<string
name=
"msg_invalid_2fa_code"
>
无效的2FA码
</string>
<string
name=
"msg_invalid_file"
>
无效文件
</string>
...
...
app/src/main/res/values/strings.xml
View file @
f4a743d4
...
...
@@ -110,6 +110,7 @@ https://github.com/RocketChat/java-code-styles/blob/master/CODING_STYLE.md#strin
<string
name=
"msg_today"
>
Today
</string>
<string
name=
"msg_message"
>
Message
</string>
<string
name=
"msg_video_call"
>
Video call
</string>
<string
name=
"msg_join_video_call"
>
Join video call
</string>
<string
name=
"msg_this_room_is_read_only"
>
This room is read only
</string>
<string
name=
"msg_invalid_2fa_code"
>
Invalid 2FA Code
</string>
<string
name=
"msg_invalid_file"
>
Invalid file
</string>
...
...
app/src/play/java/chat/rocket/android/extensions/GooglePlayServices.kt
View file @
f4a743d4
package
chat.rocket.android.extensions
import
com.google.android.gms.tasks.Task
import
kotlin.coroutines.experimental.suspendCoroutine
import
kotlin.coroutines.resume
import
kotlin.coroutines.resumeWithException
import
kotlin.coroutines.suspendCoroutine
@JvmName
(
"awaitVoid"
)
suspend
fun
Task
<
Void
>.
await
()
=
suspendCoroutine
<
Unit
>
{
continuation
->
...
...
app/src/play/java/chat/rocket/android/helper/SmartLockHelper.kt
View file @
f4a743d4
...
...
@@ -45,7 +45,7 @@ object SmartLockHelper {
.
addOnCompleteListener
{
when
{
it
.
isSuccessful
->
{
credential
=
it
.
result
.
credential
credential
=
it
.
result
?
.
credential
}
it
.
exception
is
ResolvableApiException
->
{
val
resolvableApiException
=
(
it
.
exception
as
ResolvableApiException
)
...
...
app/src/play/java/chat/rocket/android/infrastructure/CrashlyticsWrapper.kt
View file @
f4a743d4
...
...
@@ -7,16 +7,22 @@ import chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetSettingsInteractor
import
chat.rocket.android.server.domain.SITE_URL
import
com.crashlytics.android.Crashlytics
import
kotlinx.coroutines.
experimental.
runBlocking
import
kotlinx.coroutines.runBlocking
fun
installCrashlyticsWrapper
(
context
:
Application
,
currentServerInteractor
:
GetCurrentServerInteractor
,
settingsInteractor
:
GetSettingsInteractor
,
accountRepository
:
AccountsRepository
,
localRepository
:
LocalRepository
)
{
fun
installCrashlyticsWrapper
(
context
:
Application
,
currentServerInteractor
:
GetCurrentServerInteractor
,
settingsInteractor
:
GetSettingsInteractor
,
accountRepository
:
AccountsRepository
,
localRepository
:
LocalRepository
)
{
if
(
isCrashlyticsEnabled
())
{
Thread
.
setDefaultUncaughtExceptionHandler
(
RocketChatUncaughtExceptionHandler
(
currentServerInteractor
,
settingsInteractor
,
accountRepository
,
localRepository
))
Thread
.
setDefaultUncaughtExceptionHandler
(
RocketChatUncaughtExceptionHandler
(
currentServerInteractor
,
settingsInteractor
,
accountRepository
,
localRepository
)
)
}
}
...
...
@@ -25,13 +31,14 @@ private fun isCrashlyticsEnabled(): Boolean {
}
private
class
RocketChatUncaughtExceptionHandler
(
val
currentServerInteractor
:
GetCurrentServerInteractor
,
val
settingsInteractor
:
GetSettingsInteractor
,
val
accountRepository
:
AccountsRepository
,
val
localRepository
:
LocalRepository
)
:
Thread
.
UncaughtExceptionHandler
{
val
currentServerInteractor
:
GetCurrentServerInteractor
,
val
settingsInteractor
:
GetSettingsInteractor
,
val
accountRepository
:
AccountsRepository
,
val
localRepository
:
LocalRepository
)
:
Thread
.
UncaughtExceptionHandler
{
val
crashlyticsHandler
:
Thread
.
UncaughtExceptionHandler
?
=
Thread
.
getDefaultUncaughtExceptionHandler
()
val
crashlyticsHandler
:
Thread
.
UncaughtExceptionHandler
?
=
Thread
.
getDefaultUncaughtExceptionHandler
()
override
fun
uncaughtException
(
t
:
Thread
,
e
:
Throwable
)
{
val
currentServer
=
currentServerInteractor
.
get
()
?:
"<unknown>"
...
...
app/src/play/java/chat/rocket/android/push/worker/TokenRegistrationWorker.kt
View file @
f4a743d4
...
...
@@ -9,7 +9,7 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.registerPushToken
import
chat.rocket.common.util.ifNull
import
com.google.firebase.iid.FirebaseInstanceId
import
kotlinx.coroutines.
experimental.
runBlocking
import
kotlinx.coroutines.runBlocking
import
timber.log.Timber
import
javax.inject.Inject
...
...
build.gradle
View file @
f4a743d4
...
...
@@ -10,12 +10,11 @@ buildscript {
}
dependencies
{
classpath
'com.android.tools.build:gradle:3.
2.1
'
classpath
'com.android.tools.build:gradle:3.
3.2
'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath
"org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath
'com.google.gms:google-services:4.1.0'
classpath
'io.fabric.tools:gradle:1.25.4'
classpath
'com.google.gms:google-services:4.2.0'
classpath
'io.fabric.tools:gradle:1.26.1'
classpath
"com.github.ben-manes:gradle-versions-plugin:0.20.0"
}
}
...
...
@@ -26,6 +25,7 @@ allprojects {
jcenter
()
maven
{
url
"https://oss.sonatype.org/content/repositories/snapshots/"
}
maven
{
url
"https://jitpack.io"
}
maven
{
url
"https://github.com/jitsi/jitsi-maven-repository/raw/master/releases"
}
}
apply
from:
rootProject
.
file
(
'dependencies.gradle'
)
...
...
core/build.gradle
View file @
f4a743d4
...
...
@@ -29,7 +29,7 @@ dependencies {
implementation
fileTree
(
dir:
'libs'
,
include:
[
'*.jar'
])
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutines
Core
implementation
libraries
.
lifecycleExtensions
kapt
libraries
.
lifecycleCompiler
...
...
core/src/main/java/chat/rocket/android/core/lifecycle/CancelStrategy.kt
View file @
f4a743d4
...
...
@@ -4,7 +4,7 @@ import androidx.lifecycle.Lifecycle
import
androidx.lifecycle.LifecycleObserver
import
androidx.lifecycle.LifecycleOwner
import
androidx.lifecycle.OnLifecycleEvent
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
class
CancelStrategy
(
owner
:
LifecycleOwner
,
val
jobs
:
Job
)
:
LifecycleObserver
{
...
...
dependencies.gradle
View file @
f4a743d4
...
...
@@ -2,17 +2,17 @@ ext {
versions
=
[
// For project configuration
java
:
JavaVersion
.
VERSION_1_8
,
minSdk
:
21
,
compileSdk
:
28
,
targetSdk
:
28
,
minSdk
:
21
,
buildTools
:
'28.0.3'
,
dokka
:
'0.9.16'
,
// For app
kotlin
:
'1.
2.7
1'
,
coroutine
:
'
0.25.0
'
,
kotlin
:
'1.
3.2
1'
,
coroutine
:
'
1.1.1
'
,
appCompat
:
'1.0.
0
'
,
appCompat
:
'1.0.
2
'
,
recyclerview
:
'1.0.0'
,
constraintLayout
:
'2.0.0-alpha2'
,
cardview
:
'1.0.0'
,
...
...
@@ -21,9 +21,9 @@ ext {
workmanager
:
'1.0.0-alpha09'
,
dagger
:
'2.16'
,
firebaseCloudMessage
:
'17.3.
0
'
,
firebaseAnalytics
:
'16.0.
3
'
,
playServices
:
'16.0.0
'
,
firebaseCloudMessage
:
'17.3.
4
'
,
firebaseAnalytics
:
'16.0.
6
'
,
playServices
Auth
:
'16.0.1
'
,
exoPlayer
:
'2.8.2'
,
flexbox
:
'1.1.0'
,
material
:
'1.0.0'
,
...
...
@@ -36,8 +36,8 @@ ext {
rxKotlin
:
'2.3.0'
,
rxAndroid
:
'2.1.0'
,
moshi
:
'1.
6
.0'
,
okhttp
:
'3.1
1.0
'
,
moshi
:
'1.
8
.0'
,
okhttp
:
'3.1
2.1
'
,
timber
:
'4.7.1'
,
threeTenABP
:
'1.1.0'
,
...
...
@@ -45,18 +45,18 @@ ext {
fresco
:
'1.10.0'
,
kotshi
:
'1.0.
4
'
,
kotshi
:
'1.0.
6
'
,
frescoImageViewer
:
'0.5.1'
,
markwon
:
'2.0.0'
,
aVLoadingIndicatorView:
'2.1.3'
,
glide
:
'4.8.0'
,
glideTransformations
:
'4.0.0'
,
// For wearable
wear
:
'2.3.0'
,
playServicesWearable
:
'15.0.1'
,
supportWearable
:
'27.1.1'
,
jitsi
:
'+'
,
// TODO Avoid using + (https://github.com/jitsi/jitsi-meet/issues/3987)
// For testing
junit
:
'4.12'
,
...
...
@@ -66,7 +66,7 @@ ext {
]
libraries
=
[
kotlin
:
"org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}"
,
coroutines
:
"org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}"
,
coroutines
Core
:
"org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.coroutine}"
,
coroutinesAndroid
:
"org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.coroutine}"
,
appCompat
:
"androidx.appcompat:appcompat:${versions.appCompat}"
,
...
...
@@ -117,26 +117,20 @@ ext {
kotshiCompiler
:
"se.ansman.kotshi:compiler:${versions.kotshi}"
,
frescoImageViewer
:
"com.github.luciofm:FrescoImageViewer:${versions.frescoImageViewer}"
,
glide
:
"com.github.bumptech.glide:glide:${versions.glide}"
,
glideProcessor
:
"com.github.bumptech.glide:compiler:${versions.glide}"
,
glideTransformations
:
"jp.wasabeef:glide-transformations:${versions.glideTransformations}"
,
markwon
:
"ru.noties:markwon:${versions.markwon}"
,
aVLoadingIndicatorView:
"com.wang.avi:library:${versions.aVLoadingIndicatorView}"
,
glide
:
"com.github.bumptech.glide:glide:${versions.glide}"
,
glideTransformations
:
"jp.wasabeef:glide-transformations:${versions.glideTransformations}"
,
jitsi
:
"org.jitsi.react:jitsi-meet-sdk:${versions.jitsi}"
,
// Proprietary libraries
fcm
:
"com.google.firebase:firebase-messaging:${versions.firebaseCloudMessage}"
,
firebaseAnalytics
:
"com.google.firebase:firebase-core:${versions.firebaseAnalytics}"
,
playServicesAuth
:
"com.google.android.gms:play-services-auth:${versions.playServices}"
,
// For wearable
wearable
:
"com.google.android.support:wearable:${versions.wear}"
,
playServicesWearable
:
"com.google.android.gms:play-services-wearable:${versions.playServicesWearable}"
,
percentLayout
:
"com.android.support:percent:${versions.supportWearable}"
,
supportWearable
:
"com.android.support:support-v4:${versions.supportWearable}"
,
wearableRecyclerView
:
"com.android.support:recyclerview-v7:${versions.supportWearable}"
,
wearSupport
:
"com.android.support:wear:${versions.supportWearable}"
,
playServicesAuth
:
"com.google.android.gms:play-services-auth:${versions.playServicesAuth}"
,
// For testing
junit
:
"junit:junit:${versions.junit}"
,
...
...
draw/build.gradle
View file @
f4a743d4
...
...
@@ -51,7 +51,7 @@ dependencies {
implementation
project
(
':util'
)
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutines
Core
implementation
libraries
.
appCompat
implementation
libraries
.
constraintlayout
...
...
draw/src/main/java/chat/rocket/android/draw/main/di/DrawModule.kt
View file @
f4a743d4
...
...
@@ -6,7 +6,7 @@ import chat.rocket.android.draw.main.presenter.DrawView
import
chat.rocket.android.draw.main.ui.DrawingActivity
import
dagger.Module
import
dagger.Provides
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.Job
@Module
class
DrawModule
{
...
...
emoji/build.gradle
View file @
f4a743d4
...
...
@@ -5,14 +5,13 @@ apply plugin: 'kotlin-kapt'
android
{
compileSdkVersion
versions
.
compileSdk
buildToolsVersion
'28.0.3'
buildToolsVersion
versions
.
buildTools
defaultConfig
{
minSdkVersion
versions
.
minSdk
targetSdkVersion
versions
.
targetSdk
versionCode
1
versionName
"0.1"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions
{
...
...
@@ -28,28 +27,24 @@ android {
proguardFiles
getDefaultProguardFile
(
'proguard-android.txt'
),
'proguard-rules.pro'
}
}
}
dependencies
{
implementation
libraries
.
androidKtx
implementation
libraries
.
appCompat
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutines
Core
implementation
libraries
.
coroutinesAndroid
implementation
libraries
.
constraintlayout
implementation
libraries
.
appCompat
implementation
libraries
.
recyclerview
implementation
libraries
.
constraintlayout
implementation
libraries
.
androidKtx
implementation
libraries
.
material
implementation
libraries
.
glide
kapt
libraries
.
glideProcessor
implementation
libraries
.
room
kapt
libraries
.
roomProcessor
}
kotlin
{
experimental
{
coroutines
"enable"
}
implementation
libraries
.
glide
}
androidExtensions
{
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiKeyboardPopup.kt
View file @
f4a743d4
...
...
@@ -22,8 +22,9 @@ 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
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
class
EmojiKeyboardPopup
(
context
:
Context
,
view
:
View
)
:
OverKeyboardPopupWindow
(
context
,
view
)
{
private
lateinit
var
viewPager
:
ViewPager
...
...
@@ -49,7 +50,7 @@ class EmojiKeyboardPopup(context: Context, view: View) : OverKeyboardPopupWindow
}
override
fun
onViewCreated
(
view
:
View
)
{
launch
(
UI
)
{
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
setupViewPager
()
setupBottomBar
()
}
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiParser.kt
View file @
f4a743d4
...
...
@@ -8,12 +8,14 @@ import android.text.SpannableString
import
android.text.Spanned
import
android.text.style.ImageSpan
import
android.util.Log
import
c
hat.rocket.android.emoji.internal.GlideApp
import
c
om.bumptech.glide.Glide
import
com.bumptech.glide.load.engine.DiskCacheStrategy
import
com.bumptech.glide.load.resource.gif.GifDrawable
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.Deferred
import
kotlinx.coroutines.experimental.async
import
com.bumptech.glide.request.RequestOptions
import
kotlinx.coroutines.Deferred
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.async
class
EmojiParser
{
...
...
@@ -85,14 +87,14 @@ class EmojiParser {
emoji
.
url
?.
let
{
url
->
try
{
val
glideRequest
=
if
(
url
.
endsWith
(
"gif"
,
true
))
{
Glide
App
.
with
(
context
).
asGif
()
Glide
.
with
(
context
).
asGif
()
}
else
{
Glide
App
.
with
(
context
).
asBitmap
()
Glide
.
with
(
context
).
asBitmap
()
}
val
futureTarget
=
glideRequest
.
diskCacheStrategy
(
DiskCacheStrategy
.
ALL
)
.
load
(
url
)
.
apply
(
RequestOptions
().
diskCacheStrategy
(
DiskCacheStrategy
.
ALL
))
.
submit
(
px
,
px
)
val
range
=
match
.
range
...
...
@@ -120,7 +122,7 @@ class EmojiParser {
text
:
CharSequence
,
factory
:
Spannable
.
Factory
?
=
null
):
Deferred
<
CharSequence
>
{
return
async
(
CommonPool
)
{
parse
(
context
,
text
,
factory
)
}
return
GlobalScope
.
async
(
Dispatchers
.
IO
)
{
parse
(
context
,
text
,
factory
)
}
}
}
}
emoji/src/main/java/chat/rocket/android/emoji/EmojiPickerPopup.kt
View file @
f4a743d4
...
...
@@ -14,8 +14,9 @@ import chat.rocket.android.emoji.internal.EmojiCategory
import
chat.rocket.android.emoji.internal.EmojiPagerAdapter
import
chat.rocket.android.emoji.internal.PREF_EMOJI_SKIN_TONE
import
kotlinx.android.synthetic.main.emoji_picker.*
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
class
EmojiPickerPopup
(
context
:
Context
)
:
Dialog
(
context
)
{
...
...
@@ -29,7 +30,7 @@ class EmojiPickerPopup(context: Context) : Dialog(context) {
setContentView
(
R
.
layout
.
emoji_picker
)
tabs
.
setupWithViewPager
(
pager_categories
)
launch
(
UI
)
{
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
setupViewPager
()
setSize
()
}
...
...
emoji/src/main/java/chat/rocket/android/emoji/EmojiRepository.kt
View file @
f4a743d4
...
...
@@ -10,22 +10,21 @@ import chat.rocket.android.emoji.internal.db.EmojiDatabase
import
chat.rocket.android.emoji.internal.isCustom
import
com.bumptech.glide.Glide
import
com.bumptech.glide.load.engine.GlideException
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.GlobalScope
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
import
org.json.JSONArray
import
org.json.JSONObject
import
java.io.BufferedReader
import
java.io.InputStream
import
java.io.InputStreamReader
import
java.io.Reader
import
java.util.*
import
java.util.regex.Pattern
import
kotlin.collections.ArrayList
import
kotlin.coroutines.experimental.buildSequence
object
EmojiRepository
{
private
val
FITZPATRICK_REGEX
=
"(.*)_(tone[0-9]):"
.
toRegex
(
RegexOption
.
IGNORE_CASE
)
private
val
shortNameToUnicode
=
HashMap
<
String
,
String
>()
private
val
SHORTNAME_PATTERN
=
Pattern
.
compile
(
":([-+\\w]+):"
)
...
...
@@ -43,14 +42,19 @@ object EmojiRepository {
return
if
(
::
currentServerUrl
.
isInitialized
)
currentServerUrl
else
null
}
fun
load
(
context
:
Context
,
customEmojis
:
List
<
Emoji
>
=
emptyList
(),
path
:
String
=
"emoji.json"
)
{
launch
(
CommonPool
)
{
fun
load
(
context
:
Context
,
customEmojis
:
List
<
Emoji
>
=
emptyList
(),
path
:
String
=
"emoji.json"
)
{
GlobalScope
.
launch
(
Dispatchers
.
IO
)
{
this
@EmojiRepository
.
customEmojis
=
customEmojis
val
allEmojis
=
mutableListOf
<
Emoji
>()
db
=
EmojiDatabase
.
getInstance
(
context
)
if
(!
::
cachedTypeface
.
isInitialized
)
{
cachedTypeface
=
Typeface
.
createFromAsset
(
context
.
assets
,
"fonts/emojione-android.ttf"
)
cachedTypeface
=
Typeface
.
createFromAsset
(
context
.
assets
,
"fonts/emojione-android.ttf"
)
}
preferences
=
context
.
getSharedPreferences
(
"emoji"
,
Context
.
MODE_PRIVATE
)
...
...
@@ -117,9 +121,7 @@ object EmojiRepository {
customEmojis
.
forEach
{
try
{
val
future
=
Glide
.
with
(
context
)
.
load
(
it
.
url
)
.
submit
(
px
,
px
)
val
future
=
Glide
.
with
(
context
).
load
(
it
.
url
).
submit
(
px
,
px
)
future
.
get
()
}
catch
(
ex
:
Exception
)
{
Log
.
d
(
"EmojiRepository"
,
"Error fetching custom emoji ${it.shortname}"
,
ex
)
...
...
@@ -132,7 +134,7 @@ object EmojiRepository {
}
private
suspend
fun
saveEmojisToDatabase
(
emojis
:
List
<
Emoji
>)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
db
.
emojiDao
().
insertAllEmojis
(*
emojis
.
toTypedArray
())
}
}
...
...
@@ -146,31 +148,30 @@ object EmojiRepository {
*
* @return All emojis for all categories.
*/
suspend
fun
getAll
():
List
<
Emoji
>
=
withContext
(
CommonPool
)
{
suspend
fun
getAll
():
List
<
Emoji
>
=
withContext
(
Dispatchers
.
IO
)
{
return
@withContext
db
.
emojiDao
().
loadAllEmojis
()
}
internal
suspend
fun
getEmojiSequenceByCategory
(
category
:
EmojiCategory
):
Sequence
<
Emoji
>
{
val
list
=
withContext
(
CommonPool
)
{
val
list
=
withContext
(
Dispatchers
.
IO
)
{
db
.
emojiDao
().
loadEmojisByCategory
(
category
.
name
)
}
return
buildSequence
{
list
.
forEach
{
yield
(
it
)
}
return
sequence
{
list
.
forEach
{
yield
(
it
)
}
}
}
internal
suspend
fun
getEmojiSequenceByCategoryAndUrl
(
category
:
EmojiCategory
,
url
:
String
):
Sequence
<
Emoji
>
{
val
list
=
withContext
(
CommonPool
)
{
internal
suspend
fun
getEmojiSequenceByCategoryAndUrl
(
category
:
EmojiCategory
,
url
:
String
):
Sequence
<
Emoji
>
{
val
list
=
withContext
(
Dispatchers
.
IO
)
{
db
.
emojiDao
().
loadEmojisByCategoryAndUrl
(
category
.
name
,
"$url%"
)
}
return
buildSequence
{
list
.
forEach
{
yield
(
it
)
}
return
sequence
{
list
.
forEach
{
yield
(
it
)
}
}
}
...
...
@@ -181,9 +182,10 @@ object EmojiRepository {
*
* @return Emoji given by shortname or null
*/
private
suspend
fun
getEmojiByShortname
(
shortname
:
String
):
Emoji
?
=
withContext
(
CommonPool
)
{
return
@withContext
db
.
emojiDao
().
loadAllCustomEmojis
().
firstOrNull
()
}
private
suspend
fun
getEmojiByShortname
(
shortname
:
String
):
Emoji
?
=
withContext
(
Dispatchers
.
IO
)
{
return
@withContext
db
.
emojiDao
().
loadAllCustomEmojis
().
firstOrNull
()
}
/**
* Add an emoji to the Recents category.
...
...
@@ -203,9 +205,9 @@ object EmojiRepository {
}
internal
suspend
fun
getCustomEmojisAsync
():
List
<
Emoji
>
{
return
withContext
(
CommonPool
)
{
return
withContext
(
Dispatchers
.
IO
)
{
db
.
emojiDao
().
loadAllCustomEmojis
().
also
{
this
.
customEmojis
=
it
customEmojis
=
it
}
}
}
...
...
@@ -217,7 +219,7 @@ object EmojiRepository {
*
* @return All recent emojis ordered by usage.
*/
internal
suspend
fun
getRecents
():
List
<
Emoji
>
=
withContext
(
CommonPool
)
{
internal
suspend
fun
getRecents
():
List
<
Emoji
>
=
withContext
(
Dispatchers
.
IO
)
{
val
list
=
mutableListOf
<
Emoji
>()
val
recentsJson
=
JSONObject
(
preferences
.
getString
(
PREF_EMOJI_RECENTS
,
"{}"
))
...
...
@@ -281,11 +283,13 @@ object EmojiRepository {
if
(!
json
.
has
(
"shortname"
)
||
!
json
.
has
(
"unicode"
))
{
return
null
}
return
Emoji
(
shortname
=
json
.
getString
(
"shortname"
),
return
Emoji
(
shortname
=
json
.
getString
(
"shortname"
),
unicode
=
json
.
getString
(
"unicode"
),
shortnameAlternates
=
buildStringListFromJsonArray
(
json
.
getJSONArray
(
"shortnameAlternates"
)),
category
=
json
.
getString
(
"category"
),
keywords
=
buildStringListFromJsonArray
(
json
.
getJSONArray
(
"keywords"
)))
keywords
=
buildStringListFromJsonArray
(
json
.
getJSONArray
(
"keywords"
))
)
}
private
fun
buildStringListFromJsonArray
(
array
:
JSONArray
):
List
<
String
>
{
...
...
@@ -297,7 +301,7 @@ object EmojiRepository {
private
fun
inputStreamToString
(
stream
:
InputStream
):
String
{
val
sb
=
StringBuilder
()
val
isr
=
InputStreamReader
(
stream
,
Charsets
.
UTF_8
)
val
br
=
BufferedReader
(
isr
)
val
br
=
BufferedReader
(
isr
as
Reader
?
)
var
read
:
String
?
=
br
.
readLine
()
while
(
read
!=
null
)
{
sb
.
append
(
read
)
...
...
@@ -315,7 +319,7 @@ object EmojiRepository {
}
fun
init
(
context
:
Context
)
{
launch
{
GlobalScope
.
launch
{
db
=
EmojiDatabase
.
getInstance
(
context
)
preferences
=
context
.
getSharedPreferences
(
"emoji"
,
Context
.
MODE_PRIVATE
)
cachedTypeface
=
Typeface
.
createFromAsset
(
context
.
assets
,
"fonts/emojione-android.ttf"
)
...
...
emoji/src/main/java/chat/rocket/android/emoji/internal/EmojiGlideModule.kt
deleted
100644 → 0
View file @
fa0cc587
package
chat.rocket.android.emoji.internal
import
android.content.Context
import
com.bumptech.glide.GlideBuilder
import
com.bumptech.glide.annotation.GlideModule
import
com.bumptech.glide.load.engine.cache.ExternalPreferredCacheDiskCacheFactory
import
com.bumptech.glide.module.AppGlideModule
@GlideModule
class
EmojiGlideModule
:
AppGlideModule
()
{
override
fun
applyOptions
(
context
:
Context
,
builder
:
GlideBuilder
)
{
builder
.
setDiskCache
(
ExternalPreferredCacheDiskCacheFactory
(
context
))
}
}
emoji/src/main/java/chat/rocket/android/emoji/internal/EmojiPagerAdapter.kt
View file @
f4a743d4
...
...
@@ -14,15 +14,15 @@ import chat.rocket.android.emoji.EmojiParser
import
chat.rocket.android.emoji.EmojiRepository
import
chat.rocket.android.emoji.Fitzpatrick
import
chat.rocket.android.emoji.R
import
com.bumptech.glide.Glide
import
com.bumptech.glide.load.engine.DiskCacheStrategy
import
com.bumptech.glide.request.RequestOptions
import
kotlinx.android.synthetic.main.emoji_category_layout.view.*
import
kotlinx.android.synthetic.main.emoji_image_row_item.view.*
import
kotlinx.android.synthetic.main.emoji_row_item.view.*
import
kotlinx.coroutines.experimental.CommonPool
import
kotlinx.coroutines.experimental.android.UI
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.withContext
import
kotlinx.coroutines.Dispatchers
import
kotlinx.coroutines.launch
import
kotlinx.coroutines.withContext
internal
class
EmojiPagerAdapter
(
private
val
listener
:
EmojiKeyboardListener
)
:
PagerAdapter
()
{
...
...
@@ -44,7 +44,7 @@ internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) :
emoji_recycler_view
.
setRecycledViewPool
(
RecyclerView
.
RecycledViewPool
())
container
.
addView
(
view
)
launch
(
UI
)
{
kotlinx
.
coroutines
.
GlobalScope
.
launch
(
Dispatchers
.
Main
)
{
val
currentServerUrl
=
EmojiRepository
.
getCurrentServerUrl
()
val
emojis
=
if
(
category
!=
EmojiCategory
.
RECENTS
)
{
if
(
category
==
EmojiCategory
.
CUSTOM
)
{
...
...
@@ -111,9 +111,9 @@ internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) :
}
suspend
fun
addEmojisFromSequence
(
emojiSequence
:
Sequence
<
Emoji
>)
{
withContext
(
CommonPool
)
{
withContext
(
Dispatchers
.
IO
)
{
emojiSequence
.
forEachIndexed
{
index
,
emoji
->
withContext
(
UI
)
{
withContext
(
Dispatchers
.
Main
)
{
allEmojis
.
add
(
emoji
)
if
(
emoji
.
isDefault
)
{
emojis
.
add
(
emoji
)
...
...
@@ -180,9 +180,9 @@ internal class EmojiPagerAdapter(private val listener: EmojiKeyboardListener) :
}
}
else
{
// Handle custom emoji.
Glide
App
.
with
(
context
)
Glide
.
with
(
context
)
.
load
(
emoji
.
url
)
.
diskCacheStrategy
(
DiskCacheStrategy
.
ALL
)
.
apply
(
RequestOptions
().
diskCacheStrategy
(
DiskCacheStrategy
.
ALL
)
)
.
into
(
emoji_image_view
)
}
...
...
player/build.gradle
View file @
f4a743d4
...
...
@@ -13,6 +13,7 @@ android {
versionName
"1.0.0"
testInstrumentationRunner
"androidx.test.runner.AndroidJUnitRunner"
}
buildTypes
{
release
{
minifyEnabled
false
...
...
settings.gradle
View file @
f4a743d4
include
':app'
,
':player'
,
':emoji'
,
':draw'
,
':util'
,
':core'
,
':suggestions'
//, ':wear'
\ No newline at end of file
include
':app'
,
':player'
,
':emoji'
,
':draw'
,
':util'
,
':core'
,
':suggestions'
\ No newline at end of file
suggestions/src/main/java/chat/rocket/android/suggestions/strategy/trie/data/TrieNode.kt
View file @
f4a743d4
package
chat.rocket.android.suggestions.strategy.trie.data
import
chat.rocket.android.suggestions.model.SuggestionModel
import
kotlin.coroutines.experimental.buildSequence
internal
class
TrieNode
(
internal
var
data
:
Char
,
...
...
@@ -32,17 +31,13 @@ internal class TrieNode(
return
list
}
fun
getItems
():
Sequence
<
SuggestionModel
>
=
buildS
equence
{
fun
getItems
():
Sequence
<
SuggestionModel
>
=
s
equence
{
if
(
isLeaf
)
{
yield
(
item
!!
)
}
children
.
forEach
{
node
->
node
.
value
.
let
{
yieldAll
(
it
.
getItems
())
}
}
children
.
forEach
{
node
->
yieldAll
(
node
.
value
.
getItems
())
}
}
override
fun
toString
():
String
=
if
(
parent
==
null
)
""
else
"${parent.toString()}$data"
...
...
util/build.gradle
View file @
f4a743d4
...
...
@@ -31,7 +31,7 @@ dependencies {
implementation
project
(
':core'
)
implementation
libraries
.
kotlin
implementation
libraries
.
coroutines
implementation
libraries
.
coroutines
Core
implementation
libraries
.
coroutinesAndroid
implementation
libraries
.
appCompat
...
...
util/src/main/java/chat/rocket/android/util/extension/Coroutines.kt
View file @
f4a743d4
package
chat.rocket.android.util.extension
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
kotlinx.coroutines.
experimental.
CoroutineScope
import
kotlinx.coroutines.
experimental.
Job
import
kotlinx.coroutines.
experimental.android.UI
import
kotlinx.coroutines.
experimental.
launch
import
kotlinx.coroutines.CoroutineScope
import
kotlinx.coroutines.Job
import
kotlinx.coroutines.
MainScope
import
kotlinx.coroutines.launch
/**
* Launches a coroutine on the UI context.
*
* @param strategy a CancelStrategy for canceling the coroutine job
*/
fun
launchUI
(
strategy
:
CancelStrategy
,
block
:
suspend
CoroutineScope
.()
->
Unit
):
Job
{
return
launch
(
context
=
UI
,
parent
=
strategy
.
jobs
,
block
=
block
)
}
\ No newline at end of file
fun
launchUI
(
strategy
:
CancelStrategy
,
block
:
suspend
CoroutineScope
.()
->
Unit
):
Job
=
MainScope
().
launch
(
context
=
strategy
.
jobs
,
block
=
block
)
util/src/main/java/chat/rocket/android/util/extension/Image.kt
View file @
f4a743d4
...
...
@@ -6,8 +6,8 @@ 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
kotlinx.coroutines.
Dispatchers
import
kotlinx.coroutines.withContext
import
java.io.ByteArrayInputStream
import
java.io.ByteArrayOutputStream
import
java.io.InputStream
...
...
@@ -25,11 +25,10 @@ import java.util.*
suspend
fun
Bitmap
.
compressImageAndGetInputStream
(
mimeType
:
String
):
InputStream
?
{
var
inputStream
:
InputStream
?
=
null
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
byteArrayOutputStream
=
ByteArrayOutputStream
()
// TODO: Add an option the the app to the user be able to select the quality of the compressed image
val
isCompressed
=
this
.
compress
(
mimeType
.
getCompressFormat
(),
70
,
byteArrayOutputStream
)
val
isCompressed
=
compress
(
mimeType
.
getCompressFormat
(),
70
,
byteArrayOutputStream
)
if
(
isCompressed
)
{
inputStream
=
ByteArrayInputStream
(
byteArrayOutputStream
.
toByteArray
())
}
...
...
@@ -74,10 +73,9 @@ suspend fun Bitmap.getByteArray(
suspend
fun
Bitmap
.
compressImageAndGetByteArray
(
mimeType
:
String
,
quality
:
Int
=
100
):
ByteArray
?
{
var
byteArray
:
ByteArray
?
=
null
withContext
(
D
efaultDispatcher
)
{
withContext
(
D
ispatchers
.
Default
)
{
val
byteArrayOutputStream
=
ByteArrayOutputStream
()
val
isCompressed
=
this
.
compress
(
mimeType
.
getCompressFormat
(),
quality
,
byteArrayOutputStream
)
val
isCompressed
=
compress
(
mimeType
.
getCompressFormat
(),
quality
,
byteArrayOutputStream
)
if
(
isCompressed
)
{
byteArray
=
byteArrayOutputStream
.
toByteArray
()
}
...
...
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