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
1a137f9a
Unverified
Commit
1a137f9a
authored
Jun 05, 2018
by
Filipe de Lima Brito
Committed by
GitHub
Jun 05, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop' into aniket/feat/create-new-channel
parents
a1694c8e
26ead887
Changes
48
Hide whitespace changes
Inline
Side-by-side
Showing
48 changed files
with
966 additions
and
605 deletions
+966
-605
build.gradle
app/build.gradle
+12
-4
google-services.json
app/google-services.json
+247
-13
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+5
-23
DateTimeHelper.kt
app/src/main/java/chat/rocket/android/app/DateTimeHelper.kt
+3
-3
LoginPresenter.kt
...droid/authentication/login/presentation/LoginPresenter.kt
+1
-5
LoginView.kt
...et/android/authentication/login/presentation/LoginView.kt
+2
-2
LoginFragment.kt
...t/rocket/android/authentication/login/ui/LoginFragment.kt
+52
-170
SignupPresenter.kt
...oid/authentication/signup/presentation/SignupPresenter.kt
+1
-5
SignupView.kt
.../android/authentication/signup/presentation/SignupView.kt
+2
-2
SignupFragment.kt
...rocket/android/authentication/signup/ui/SignupFragment.kt
+15
-43
ImageAttachmentViewHolder.kt
...ket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
+1
-1
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+11
-11
MessageService.kt
...va/chat/rocket/android/chatroom/service/MessageService.kt
+1
-1
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+15
-10
MessageViewModel.kt
...hat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
+13
-13
ReactionViewModel.kt
...at/rocket/android/chatroom/viewmodel/ReactionViewModel.kt
+5
-5
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+102
-41
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+20
-2
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+30
-5
ServiceBuilder.kt
.../java/chat/rocket/android/dagger/module/ServiceBuilder.kt
+4
-4
FavoriteMessagesPresenter.kt
...avoritemessages/presentation/FavoriteMessagesPresenter.kt
+3
-3
FavoriteMessagesFragment.kt
...t/android/favoritemessages/ui/FavoriteMessagesFragment.kt
+1
-1
FilesFragment.kt
...c/main/java/chat/rocket/android/files/ui/FilesFragment.kt
+2
-6
FileViewModel.kt
...java/chat/rocket/android/files/viewmodel/FileViewModel.kt
+4
-3
ImageHelper.kt
app/src/main/java/chat/rocket/android/helper/ImageHelper.kt
+0
-3
SmartLockHelper.kt
...c/main/java/chat/rocket/android/helper/SmartLockHelper.kt
+146
-0
MainNavigator.kt
...va/chat/rocket/android/main/presentation/MainNavigator.kt
+3
-3
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+2
-4
MainView.kt
...in/java/chat/rocket/android/main/presentation/MainView.kt
+1
-4
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+63
-80
PinnedMessagesPresenter.kt
...id/pinnedmessages/presentation/PinnedMessagesPresenter.kt
+2
-3
PinnedMessagesFragment.kt
...ocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt
+1
-1
ProfilePresenter.kt
...t/rocket/android/profile/presentation/ProfilePresenter.kt
+2
-1
ProfileFragment.kt
...in/java/chat/rocket/android/profile/ui/ProfileFragment.kt
+2
-2
FirebaseMessagingService.kt
...java/chat/rocket/android/push/FirebaseMessagingService.kt
+24
-0
FirebaseTokenService.kt
...ain/java/chat/rocket/android/push/FirebaseTokenService.kt
+7
-12
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+115
-83
FirebaseMessagingServiceProvider.kt
...ocket/android/push/di/FirebaseMessagingServiceProvider.kt
+3
-3
ConnectionManager.kt
...ocket/android/server/infraestructure/ConnectionManager.kt
+6
-6
ChangeServerNavigator.kt
...cket/android/server/presentation/ChangeServerNavigator.kt
+5
-2
ChangeServerPresenter.kt
...cket/android/server/presentation/ChangeServerPresenter.kt
+2
-2
ChangeServerActivity.kt
...ava/chat/rocket/android/server/ui/ChangeServerActivity.kt
+5
-2
SettingsFragment.kt
.../java/chat/rocket/android/settings/ui/SettingsFragment.kt
+1
-3
fragment_authentication_log_in.xml
app/src/main/res/layout/fragment_authentication_log_in.xml
+12
-0
fragment_files.xml
app/src/main/res/layout/fragment_files.xml
+1
-0
api_keys.xml
app/src/main/res/values/api_keys.xml
+0
-5
debug.keystore
debug.keystore
+0
-0
dependencies.gradle
dependencies.gradle
+11
-10
No files found.
app/build.gradle
View file @
1a137f9a
...
@@ -13,7 +13,7 @@ android {
...
@@ -13,7 +13,7 @@ android {
applicationId
"chat.rocket.android"
applicationId
"chat.rocket.android"
minSdkVersion
21
minSdkVersion
21
targetSdkVersion
versions
.
targetSdk
targetSdkVersion
versions
.
targetSdk
versionCode
202
2
versionCode
202
4
versionName
"2.3.0"
versionName
"2.3.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
multiDexEnabled
true
...
@@ -26,12 +26,19 @@ android {
...
@@ -26,12 +26,19 @@ android {
keyAlias
System
.
getenv
(
"KEY_ALIAS"
)
keyAlias
System
.
getenv
(
"KEY_ALIAS"
)
keyPassword
System
.
getenv
(
"KEY_PASSWORD"
)
keyPassword
System
.
getenv
(
"KEY_PASSWORD"
)
}
}
debug
{
storeFile
project
.
rootProject
.
file
(
'debug.keystore'
).
getCanonicalFile
()
storePassword
"android"
keyAlias
"androiddebugkey"
keyPassword
"android"
}
}
}
buildTypes
{
buildTypes
{
release
{
release
{
buildConfigField
"String"
,
"REQUIRED_SERVER_VERSION"
,
'"0.62.0"'
buildConfigField
"String"
,
"REQUIRED_SERVER_VERSION"
,
'"0.62.0"'
buildConfigField
"String"
,
"RECOMMENDED_SERVER_VERSION"
,
'"0.6
3.0
"'
buildConfigField
"String"
,
"RECOMMENDED_SERVER_VERSION"
,
'"0.6
4.2
"'
signingConfig
signingConfigs
.
release
signingConfig
signingConfigs
.
release
minifyEnabled
false
minifyEnabled
false
proguardFiles
getDefaultProguardFile
(
'proguard-android.txt'
),
'proguard-rules.pro'
proguardFiles
getDefaultProguardFile
(
'proguard-android.txt'
),
'proguard-rules.pro'
...
@@ -39,7 +46,8 @@ android {
...
@@ -39,7 +46,8 @@ android {
debug
{
debug
{
buildConfigField
"String"
,
"REQUIRED_SERVER_VERSION"
,
'"0.62.0"'
buildConfigField
"String"
,
"REQUIRED_SERVER_VERSION"
,
'"0.62.0"'
buildConfigField
"String"
,
"RECOMMENDED_SERVER_VERSION"
,
'"0.63.0"'
buildConfigField
"String"
,
"RECOMMENDED_SERVER_VERSION"
,
'"0.64.2"'
signingConfig
signingConfigs
.
debug
applicationIdSuffix
".dev"
applicationIdSuffix
".dev"
}
}
}
}
...
@@ -74,7 +82,7 @@ dependencies {
...
@@ -74,7 +82,7 @@ dependencies {
kapt
libraries
.
daggerProcessor
kapt
libraries
.
daggerProcessor
kapt
libraries
.
daggerAndroidApt
kapt
libraries
.
daggerAndroidApt
implementation
libraries
.
playServicesG
cm
implementation
libraries
.
f
cm
implementation
libraries
.
playServicesAuth
implementation
libraries
.
playServicesAuth
implementation
libraries
.
room
implementation
libraries
.
room
...
...
app/google-services.json
View file @
1a137f9a
{
{
"project_info"
:
{
"project_info"
:
{
"project_number"
:
"
1020987621558
"
,
"project_number"
:
"
673693445664
"
,
"firebase_url"
:
"https://rocketchat
nativ
e.firebaseio.com"
,
"firebase_url"
:
"https://rocketchat
-9e9b
e.firebaseio.com"
,
"project_id"
:
"rocketchat
nativ
e"
,
"project_id"
:
"rocketchat
-9e9b
e"
,
"storage_bucket"
:
"rocketchat
nativ
e.appspot.com"
"storage_bucket"
:
"rocketchat
-9e9b
e.appspot.com"
},
},
"client"
:
[
"client"
:
[
{
{
"client_info"
:
{
"client_info"
:
{
"mobilesdk_app_id"
:
"1:
1020987621558:android:16da2e50aff9f0c9
"
,
"mobilesdk_app_id"
:
"1:
673693445664:android:6ef4638e500ec958
"
,
"android_client_info"
:
{
"android_client_info"
:
{
"package_name"
:
"
chat.rocket.android
"
"package_name"
:
"
RocketChat
"
}
}
},
},
"oauth_client"
:
[
"oauth_client"
:
[
{
{
"client_id"
:
"1020987621558-trk61fjrahho0ujtjap095p1jmi48pfq.apps.googleusercontent.com"
,
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
"client_type"
:
3
}
}
],
],
"api_key"
:
[
"api_key"
:
[
{
{
"current_key"
:
"AIzaSyD
c7VYUdU6kRkoRTToiCn1rh-W0wJvhLWk
"
"current_key"
:
"AIzaSyD
IkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg
"
}
}
],
],
"services"
:
{
"services"
:
{
...
@@ -39,20 +63,218 @@
...
@@ -39,20 +63,218 @@
},
},
{
{
"client_info"
:
{
"client_info"
:
{
"mobilesdk_app_id"
:
"1:1020987621558:android:1551054db195f705"
,
"mobilesdk_app_id"
:
"1:673693445664:android:16da2e50aff9f0c9"
,
"android_client_info"
:
{
"package_name"
:
"chat.rocket.android"
}
},
"oauth_client"
:
[
{
"client_id"
:
"673693445664-k0mvosdjoe5dbvqce3b377ckabb5dgu8.apps.googleusercontent.com"
,
"client_type"
:
1
,
"android_info"
:
{
"package_name"
:
"chat.rocket.android"
,
"certificate_hash"
:
"33fa8582794176014a59054192e261bfad0e5273"
}
},
{
"client_id"
:
"673693445664-hrjftksij02vqtd467ln2cubvu48ft5j.apps.googleusercontent.com"
,
"client_type"
:
1
,
"android_info"
:
{
"package_name"
:
"chat.rocket.android"
,
"certificate_hash"
:
"41cf750df786a6d9da712a98a629d0c8391876d6"
}
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
}
],
"api_key"
:
[
{
"current_key"
:
"AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
}
],
"services"
:
{
"analytics_service"
:
{
"status"
:
1
},
"appinvite_service"
:
{
"status"
:
2
,
"other_platform_oauth_client"
:
[
{
"client_id"
:
"673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com"
,
"client_type"
:
2
,
"ios_info"
:
{
"bundle_id"
:
"com.konecty.rocket.chat"
}
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
}
]
},
"ads_service"
:
{
"status"
:
2
}
}
},
{
"client_info"
:
{
"mobilesdk_app_id"
:
"1:673693445664:android:1551054db195f705"
,
"android_client_info"
:
{
"android_client_info"
:
{
"package_name"
:
"chat.rocket.android.dev"
"package_name"
:
"chat.rocket.android.dev"
}
}
},
},
"oauth_client"
:
[
"oauth_client"
:
[
{
{
"client_id"
:
"1020987621558-trk61fjrahho0ujtjap095p1jmi48pfq.apps.googleusercontent.com"
,
"client_id"
:
"673693445664-t5aeku0oie010npd40a0tgn27c418vk7.apps.googleusercontent.com"
,
"client_type"
:
1
,
"android_info"
:
{
"package_name"
:
"chat.rocket.android.dev"
,
"certificate_hash"
:
"41cf750df786a6d9da712a98a629d0c8391876d6"
}
},
{
"client_id"
:
"673693445664-iml14ln4vccuu7liclrpt2k671fkjs38.apps.googleusercontent.com"
,
"client_type"
:
1
,
"android_info"
:
{
"package_name"
:
"chat.rocket.android.dev"
,
"certificate_hash"
:
"33fa8582794176014a59054192e261bfad0e5273"
}
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
}
],
"api_key"
:
[
{
"current_key"
:
"AIzaSyDIkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg"
}
],
"services"
:
{
"analytics_service"
:
{
"status"
:
1
},
"appinvite_service"
:
{
"status"
:
2
,
"other_platform_oauth_client"
:
[
{
"client_id"
:
"673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com"
,
"client_type"
:
2
,
"ios_info"
:
{
"bundle_id"
:
"com.konecty.rocket.chat"
}
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
}
]
},
"ads_service"
:
{
"status"
:
2
}
}
},
{
"client_info"
:
{
"mobilesdk_app_id"
:
"1:673693445664:android:64932c99863e2838"
,
"android_client_info"
:
{
"package_name"
:
"com.konecty.rocket.chat"
}
},
"oauth_client"
:
[
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-vtcvuvso7k88gpodlpshod55g1ehs03s.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-sf7lqf11kk6vplg9ljh7pi491gvb08f3.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-fb71j3aqmafmm20jkj8gvpusv04fdnq8.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-ejd5lkbsdjoo5onc052dotsjacdh1kcc.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-3ajben08beuco6eout3kpod2gbbm8fij.apps.googleusercontent.com"
,
"client_type"
:
1
,
"android_info"
:
{
"package_name"
:
"com.konecty.rocket.chat"
,
"certificate_hash"
:
"cd5806ba3f0141d0f2e47acfe64a485f575108ab"
}
},
{
"client_id"
:
"673693445664-ssfpeb0are3svvg0etbttog789s0n3ua.apps.googleusercontent.com"
,
"client_type"
:
3
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
"client_type"
:
3
}
}
],
],
"api_key"
:
[
"api_key"
:
[
{
{
"current_key"
:
"AIzaSyD
c7VYUdU6kRkoRTToiCn1rh-W0wJvhLWk
"
"current_key"
:
"AIzaSyD
IkZj1TRz8TmhnMswDwVY5OnWuzFK3rxg
"
}
}
],
],
"services"
:
{
"services"
:
{
...
@@ -60,8 +282,20 @@
...
@@ -60,8 +282,20 @@
"status"
:
1
"status"
:
1
},
},
"appinvite_service"
:
{
"appinvite_service"
:
{
"status"
:
1
,
"status"
:
2
,
"other_platform_oauth_client"
:
[]
"other_platform_oauth_client"
:
[
{
"client_id"
:
"673693445664-pa3k48sg81r89rn65e9rlnu4gpmm5vem.apps.googleusercontent.com"
,
"client_type"
:
2
,
"ios_info"
:
{
"bundle_id"
:
"com.konecty.rocket.chat"
}
},
{
"client_id"
:
"673693445664-97s9t777ful7mn2510vuhb48958qd9tb.apps.googleusercontent.com"
,
"client_type"
:
3
}
]
},
},
"ads_service"
:
{
"ads_service"
:
{
"status"
:
2
"status"
:
2
...
...
app/src/main/AndroidManifest.xml
View file @
1a137f9a
...
@@ -3,17 +3,9 @@
...
@@ -3,17 +3,9 @@
package=
"chat.rocket.android"
>
package=
"chat.rocket.android"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.WAKE_LOCK"
/>
<uses-permission
android:name=
"android.permission.VIBRATE"
/>
<uses-permission
android:name=
"android.permission.VIBRATE"
/>
<uses-permission
android:name=
"com.google.android.c2dm.permission.RECEIVE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<permission
android:name=
"${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel=
"signature"
/>
<uses-permission
android:name=
"${applicationId}.permission.C2D_MESSAGE"
/>
<application
<application
android:name=
".app.RocketChatApplication"
android:name=
".app.RocketChatApplication"
android:allowBackup=
"true"
android:allowBackup=
"true"
...
@@ -39,6 +31,7 @@
...
@@ -39,6 +31,7 @@
<intent-filter>
<intent-filter>
<action
android:name=
"android.intent.action.VIEW"
/>
<action
android:name=
"android.intent.action.VIEW"
/>
<category
android:name=
"android.intent.category.BROWSABLE"
/>
<category
android:name=
"android.intent.category.BROWSABLE"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
...
@@ -99,18 +92,6 @@
...
@@ -99,18 +92,6 @@
android:name=
".settings.about.ui.AboutActivity"
android:name=
".settings.about.ui.AboutActivity"
android:theme=
"@style/AppTheme"
/>
android:theme=
"@style/AppTheme"
/>
<receiver
android:name=
"com.google.android.gms.gcm.GcmReceiver"
android:exported=
"true"
android:permission=
"com.google.android.c2dm.permission.SEND"
>
<intent-filter>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
<action
android:name=
"com.google.android.c2dm.intent.REGISTRATION"
/>
<category
android:name=
"${applicationId}"
/>
</intent-filter>
</receiver>
<receiver
<receiver
android:name=
".push.DirectReplyReceiver"
android:name=
".push.DirectReplyReceiver"
android:enabled=
"true"
android:enabled=
"true"
...
@@ -129,10 +110,11 @@
...
@@ -129,10 +110,11 @@
</service>
</service>
<service
<service
android:name=
".push.GcmListenerService"
android:name=
".push.FirebaseMessagingService"
android:exported=
"false"
>
android:enabled=
"true"
android:exported=
"true"
>
<intent-filter>
<intent-filter>
<action
android:name=
"com.google.
android.c2dm.intent.RECEIVE
"
/>
<action
android:name=
"com.google.
firebase.MESSAGING_EVENT
"
/>
</intent-filter>
</intent-filter>
</service>
</service>
...
...
app/src/main/java/chat/rocket/android/app/DateTimeHelper.kt
View file @
1a137f9a
...
@@ -14,11 +14,11 @@ object DateTimeHelper {
...
@@ -14,11 +14,11 @@ object DateTimeHelper {
/**
/**
* Returns a [LocalDateTime] from a [Long].
* Returns a [LocalDateTime] from a [Long].
*
*
* @param long The [Long]
* @param long The [Long]
to gets a [LocalDateTime].
* @return The [LocalDateTime] from a [Long].
* @return The [LocalDateTime] from a [Long].
*/
*/
fun
getLocalDateTime
(
long
:
Long
?
):
LocalDateTime
{
fun
getLocalDateTime
(
long
:
Long
):
LocalDateTime
{
return
LocalDateTime
.
ofInstant
(
long
?.
let
{
Instant
.
ofEpochMilli
(
it
)
}
,
ZoneId
.
systemDefault
())
return
LocalDateTime
.
ofInstant
(
Instant
.
ofEpochMilli
(
long
)
,
ZoneId
.
systemDefault
())
}
}
/**
/**
...
...
app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt
View file @
1a137f9a
...
@@ -56,7 +56,6 @@ class LoginPresenter @Inject constructor(
...
@@ -56,7 +56,6 @@ class LoginPresenter @Inject constructor(
private
lateinit
var
credentialSecret
:
String
private
lateinit
var
credentialSecret
:
String
private
lateinit
var
deepLinkUserId
:
String
private
lateinit
var
deepLinkUserId
:
String
private
lateinit
var
deepLinkToken
:
String
private
lateinit
var
deepLinkToken
:
String
private
var
loginCredentials
:
Credential
?
=
null
fun
setupView
()
{
fun
setupView
()
{
setupConnectionInfo
(
currentServer
)
setupConnectionInfo
(
currentServer
)
...
@@ -357,10 +356,7 @@ class LoginPresenter @Inject constructor(
...
@@ -357,10 +356,7 @@ class LoginPresenter @Inject constructor(
saveToken
(
token
)
saveToken
(
token
)
registerPushToken
()
registerPushToken
()
if
(
loginType
==
TYPE_LOGIN_USER_EMAIL
)
{
if
(
loginType
==
TYPE_LOGIN_USER_EMAIL
)
{
loginCredentials
=
Credential
.
Builder
(
usernameOrEmail
)
view
.
saveSmartLockCredentials
(
usernameOrEmail
,
password
)
.
setPassword
(
password
)
.
build
()
view
.
saveSmartLockCredentials
(
loginCredentials
)
}
}
navigator
.
toChatList
()
navigator
.
toChatList
()
}
else
if
(
loginType
==
TYPE_LOGIN_OAUTH
)
{
}
else
if
(
loginType
==
TYPE_LOGIN_OAUTH
)
{
...
...
app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginView.kt
View file @
1a137f9a
...
@@ -243,7 +243,7 @@ interface LoginView : LoadingView, MessageView {
...
@@ -243,7 +243,7 @@ interface LoginView : LoadingView, MessageView {
fun
alertWrongPassword
()
fun
alertWrongPassword
()
/**
/**
* Save
credentials via google smart lock
* Save
s Google Smart Lock credentials.
*/
*/
fun
saveSmartLockCredentials
(
loginCredential
:
Credential
?
)
fun
saveSmartLockCredentials
(
id
:
String
,
password
:
String
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/authentication/login/ui/LoginFragment.kt
View file @
1a137f9a
...
@@ -2,28 +2,27 @@ package chat.rocket.android.authentication.login.ui
...
@@ -2,28 +2,27 @@ package chat.rocket.android.authentication.login.ui
import
DrawableHelper
import
DrawableHelper
import
android.app.Activity
import
android.app.Activity
import
android.app.PendingIntent
import
android.content.Intent
import
android.content.Intent
import
android.content.IntentSender
import
android.graphics.PorterDuff
import
android.graphics.PorterDuff
import
android.os.Build
import
android.os.Build
import
android.os.Bundle
import
android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v4.app.FragmentActivity
import
android.text.style.ClickableSpan
import
android.text.style.ClickableSpan
import
android.view.LayoutInflater
import
android.view.LayoutInflater
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
import
android.view.ViewTreeObserver
import
android.view.ViewTreeObserver
import
android.widget.*
import
android.widget.Button
import
android.widget.ImageButton
import
android.widget.LinearLayout
import
android.widget.ScrollView
import
androidx.core.view.isVisible
import
androidx.core.view.isVisible
import
androidx.core.view.postDelayed
import
androidx.core.view.postDelayed
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import
chat.rocket.android.authentication.domain.model.LoginDeepLinkInfo
import
chat.rocket.android.authentication.login.presentation.LoginPresenter
import
chat.rocket.android.authentication.login.presentation.LoginPresenter
import
chat.rocket.android.authentication.login.presentation.LoginView
import
chat.rocket.android.authentication.login.presentation.LoginView
import
chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.*
import
chat.rocket.android.helper.TextHelper
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import
chat.rocket.android.webview.sso.ui.INTENT_SSO_TOKEN
import
chat.rocket.android.webview.sso.ui.ssoWebViewIntent
import
chat.rocket.android.webview.sso.ui.ssoWebViewIntent
...
@@ -31,37 +30,25 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
...
@@ -31,37 +30,25 @@ import chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_SECRET
import
chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import
chat.rocket.android.webview.oauth.ui.INTENT_OAUTH_CREDENTIAL_TOKEN
import
chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import
chat.rocket.android.webview.oauth.ui.oauthWebViewIntent
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
com.google.android.gms.auth.api.Auth
import
com.google.android.gms.auth.api.credentials.*
import
com.google.android.gms.auth.api.credentials.*
import
com.google.android.gms.common.api.CommonStatusCodes
import
com.google.android.gms.common.api.GoogleApiClient
import
com.google.android.gms.common.api.ResolvingResultCallbacks
import
com.google.android.gms.common.api.Status
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import
kotlinx.android.synthetic.main.fragment_authentication_log_in.*
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
internal
const
val
REQUEST_CODE_FOR_CAS
=
1
internal
const
val
REQUEST_CODE_FOR_CAS
=
4
internal
const
val
REQUEST_CODE_FOR_SAML
=
2
internal
const
val
REQUEST_CODE_FOR_SAML
=
5
internal
const
val
REQUEST_CODE_FOR_OAUTH
=
3
internal
const
val
REQUEST_CODE_FOR_OAUTH
=
6
internal
const
val
MULTIPLE_CREDENTIALS_READ
=
4
internal
const
val
NO_CREDENTIALS_EXIST
=
5
internal
const
val
SAVE_CREDENTIALS
=
6
lateinit
var
googleApiClient
:
GoogleApiClient
class
LoginFragment
:
Fragment
(),
LoginView
{
class
LoginFragment
:
Fragment
(),
LoginView
,
GoogleApiClient
.
ConnectionCallbacks
{
@Inject
@Inject
lateinit
var
presenter
:
LoginPresenter
lateinit
var
presenter
:
LoginPresenter
private
var
isOauthViewEnable
=
false
private
var
isOauthViewEnable
=
false
private
val
layoutListener
=
ViewTreeObserver
.
OnGlobalLayoutListener
{
private
val
layoutListener
=
ViewTreeObserver
.
OnGlobalLayoutListener
{
areLoginOptionsNeeded
()
areLoginOptionsNeeded
()
}
}
private
var
isOauthSuccessful
=
false
private
var
isGlobalLayoutListenerSetUp
=
false
private
var
isGlobalLayoutListenerSetUp
=
false
private
var
deepLinkInfo
:
LoginDeepLinkInfo
?
=
null
private
var
deepLinkInfo
:
LoginDeepLinkInfo
?
=
null
private
va
r
credentialsToBeSaved
:
Credential
?
=
null
private
va
l
credentialsClient
by
lazy
{
Credentials
.
getClient
(
requireActivity
())
}
companion
object
{
companion
object
{
private
const
val
DEEP_LINK_INFO
=
"DeepLinkInfo"
private
const
val
DEEP_LINK_INFO
=
"DeepLinkInfo"
...
@@ -73,17 +60,9 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
...
@@ -73,17 +60,9 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
}
}
}
}
override
fun
onConnected
(
bundle
:
Bundle
?)
{
saveSmartLockCredentials
(
credentialsToBeSaved
)
}
override
fun
onConnectionSuspended
(
errorCode
:
Int
)
{
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
AndroidSupportInjection
.
inject
(
this
)
buildGoogleApiClient
()
deepLinkInfo
=
arguments
?.
getParcelable
(
DEEP_LINK_INFO
)
deepLinkInfo
=
arguments
?.
getParcelable
(
DEEP_LINK_INFO
)
}
}
...
@@ -120,156 +99,40 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
...
@@ -120,156 +99,40 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
if
(
resultCode
==
Activity
.
RESULT_OK
)
{
if
(
resultCode
==
Activity
.
RESULT_OK
)
{
if
(
data
!=
null
)
{
if
(
data
!=
null
)
{
when
(
requestCode
)
{
when
(
requestCode
)
{
REQUEST_CODE_FOR_
CAS
->
data
.
apply
{
REQUEST_CODE_FOR_
MULTIPLE_ACCOUNTS_RESOLUTION
->
{
presenter
.
authenticateWithCas
(
getStringExtra
(
INTENT_SSO_TOKEN
))
onCredentialRetrieved
(
data
.
getParcelableExtra
(
Credential
.
EXTRA_KEY
))
}
}
REQUEST_CODE_FOR_SAML
->
data
.
apply
{
REQUEST_CODE_FOR_SIGN_IN_REQUIRED
->
{
presenter
.
authenticateWithSaml
(
getStringExtra
(
INTENT_SSO_TOKEN
))
//use the hints to autofill sign in forms to reduce the info to be filled.
val
credential
:
Credential
=
data
.
getParcelableExtra
(
Credential
.
EXTRA_KEY
)
text_username_or_email
.
setText
(
credential
.
id
)
text_password
.
setText
(
credential
.
password
)
}
}
REQUEST_CODE_FOR_OAUTH
->
{
REQUEST_CODE_FOR_SAVE_RESOLUTION
->
{
isOauthSuccessful
=
true
showMessage
(
getString
(
R
.
string
.
message_credentials_saved_successfully
))
data
.
apply
{
presenter
.
authenticateWithOauth
(
getStringExtra
(
INTENT_OAUTH_CREDENTIAL_TOKEN
),
getStringExtra
(
INTENT_OAUTH_CREDENTIAL_SECRET
)
)
}
}
}
MULTIPLE_CREDENTIALS_READ
->
{
REQUEST_CODE_FOR_CAS
->
{
val
loginCredentials
:
Credential
=
presenter
.
authenticateWithCas
(
data
.
getStringExtra
(
INTENT_SSO_TOKEN
))
data
.
getParcelableExtra
(
Credential
.
EXTRA_KEY
)
handleCredential
(
loginCredentials
)
}
}
NO_CREDENTIALS_EXIST
->
{
REQUEST_CODE_FOR_SAML
->
data
.
apply
{
//use the hints to autofill sign in forms to reduce the info to be filled
presenter
.
authenticateWithSaml
(
getStringExtra
(
INTENT_SSO_TOKEN
))
val
loginCredentials
:
Credential
=
data
.
getParcelableExtra
(
Credential
.
EXTRA_KEY
)
val
email
=
loginCredentials
.
id
val
password
=
loginCredentials
.
password
text_username_or_email
.
setText
(
email
)
text_password
.
setText
(
password
)
}
}
SAVE_CREDENTIALS
->
Toast
.
makeText
(
REQUEST_CODE_FOR_OAUTH
->
{
context
,
presenter
.
authenticateWithOauth
(
getString
(
R
.
string
.
message_credentials_saved_successfully
),
data
.
getStringExtra
(
INTENT_OAUTH_CREDENTIAL_TOKEN
),
Toast
.
LENGTH_SHORT
data
.
getStringExtra
(
INTENT_OAUTH_CREDENTIAL_SECRET
)
).
show
()
)
}
}
}
//cancel button pressed by the user in case of reading from smart lock
else
if
(
resultCode
==
Activity
.
RESULT_CANCELED
&&
requestCode
==
REQUEST_CODE_FOR_OAUTH
)
{
Timber
.
d
(
"Returned from oauth"
)
}
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
googleApiClient
.
let
{
activity
?.
let
{
it1
->
it
.
stopAutoManage
(
it1
)
}
it
.
disconnect
()
}
}
private
fun
buildGoogleApiClient
()
{
googleApiClient
=
GoogleApiClient
.
Builder
(
context
!!
)
.
enableAutoManage
(
activity
as
FragmentActivity
,
{
Timber
.
e
(
"ERROR: Connection to client failed"
)
})
.
addConnectionCallbacks
(
this
)
.
addApi
(
Auth
.
CREDENTIALS_API
)
.
build
()
}
override
fun
onStart
()
{
super
.
onStart
()
if
(!
isOauthSuccessful
)
{
requestCredentials
()
}
}
private
fun
requestCredentials
()
{
val
request
:
CredentialRequest
=
CredentialRequest
.
Builder
()
.
setPasswordLoginSupported
(
true
)
.
build
()
Auth
.
CredentialsApi
.
request
(
googleApiClient
,
request
)
.
setResultCallback
{
credentialRequestResult
->
val
status
=
credentialRequestResult
.
status
when
{
status
.
isSuccess
->
handleCredential
(
credentialRequestResult
.
credential
)
(
status
.
statusCode
==
CommonStatusCodes
.
RESOLUTION_REQUIRED
)
->
resolveResult
(
status
,
MULTIPLE_CREDENTIALS_READ
)
(
status
.
statusCode
==
CommonStatusCodes
.
SIGN_IN_REQUIRED
)
->
{
val
hintRequest
:
HintRequest
=
HintRequest
.
Builder
()
.
setHintPickerConfig
(
CredentialPickerConfig
.
Builder
()
.
setShowCancelButton
(
true
)
.
build
()
)
.
setEmailAddressIdentifierSupported
(
true
)
.
setAccountTypes
(
IdentityProviders
.
GOOGLE
)
.
build
()
val
intent
:
PendingIntent
=
Auth
.
CredentialsApi
.
getHintPickerIntent
(
googleApiClient
,
hintRequest
)
try
{
startIntentSenderForResult
(
intent
.
intentSender
,
NO_CREDENTIALS_EXIST
,
null
,
0
,
0
,
0
,
null
)
}
catch
(
e
:
IntentSender
.
SendIntentException
)
{
Timber
.
e
(
"ERROR: Could not start hint picker Intent"
)
}
}
}
else
->
Timber
.
d
(
"ERROR: nothing happening"
)
}
}
}
}
}
private
fun
handleCredential
(
loginCredentials
:
Credential
)
{
if
(
loginCredentials
.
accountType
==
null
)
{
presenter
.
authenticateWithUserAndPassword
(
loginCredentials
.
id
,
loginCredentials
.
password
.
toString
()
)
}
}
private
fun
resolveResult
(
status
:
Status
,
requestCode
:
Int
)
{
try
{
status
.
startResolutionForResult
(
activity
,
requestCode
)
}
catch
(
e
:
IntentSender
.
SendIntentException
)
{
Timber
.
e
(
"Failed to send Credentials intent"
)
}
}
}
}
override
fun
saveSmartLockCredentials
(
loginCredential
:
Credential
?)
{
override
fun
onResume
()
{
credentialsToBeSaved
=
loginCredential
super
.
onResume
()
if
(
credentialsToBeSaved
==
null
)
{
image_key
.
setOnClickListener
{
return
requestStoredCredentials
()
}
image_key
.
isVisible
=
false
activity
?.
let
{
Auth
.
CredentialsApi
.
save
(
googleApiClient
,
credentialsToBeSaved
).
setResultCallback
(
object
:
ResolvingResultCallbacks
<
Status
>(
it
,
SAVE_CREDENTIALS
)
{
override
fun
onSuccess
(
status
:
Status
)
{
Timber
.
d
(
"credentials save:SUCCESS:$status"
)
credentialsToBeSaved
=
null
}
override
fun
onUnresolvableFailure
(
status
:
Status
)
{
Timber
.
e
(
"credentials save:FAILURE:$status"
)
credentialsToBeSaved
=
null
}
})
}
}
}
}
...
@@ -289,6 +152,24 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
...
@@ -289,6 +152,24 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
}
}
}
}
private
fun
requestStoredCredentials
()
{
activity
?.
let
{
SmartLockHelper
.
requestStoredCredentials
(
credentialsClient
,
it
)
?.
let
{
onCredentialRetrieved
(
it
)
}
}
}
private
fun
onCredentialRetrieved
(
credential
:
Credential
)
{
presenter
.
authenticateWithUserAndPassword
(
credential
.
id
,
credential
.
password
.
toString
())
}
override
fun
saveSmartLockCredentials
(
id
:
String
,
password
:
String
)
{
activity
?.
let
{
SmartLockHelper
.
save
(
credentialsClient
,
it
,
id
,
password
)
}
}
override
fun
showLoading
()
{
override
fun
showLoading
()
{
ui
{
ui
{
view_loading
.
isVisible
=
true
view_loading
.
isVisible
=
true
...
@@ -321,6 +202,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
...
@@ -321,6 +202,7 @@ class LoginFragment : Fragment(), LoginView, GoogleApiClient.ConnectionCallbacks
ui
{
ui
{
text_username_or_email
.
isVisible
=
true
text_username_or_email
.
isVisible
=
true
text_password
.
isVisible
=
true
text_password
.
isVisible
=
true
image_key
.
isVisible
=
true
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt
View file @
1a137f9a
...
@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
...
@@ -15,7 +15,6 @@ import chat.rocket.core.internal.rest.login
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.signup
import
chat.rocket.core.internal.rest.signup
import
chat.rocket.core.model.Myself
import
chat.rocket.core.model.Myself
import
com.google.android.gms.auth.api.credentials.Credential
import
javax.inject.Inject
import
javax.inject.Inject
class
SignupPresenter
@Inject
constructor
(
class
SignupPresenter
@Inject
constructor
(
...
@@ -64,10 +63,7 @@ class SignupPresenter @Inject constructor(
...
@@ -64,10 +63,7 @@ class SignupPresenter @Inject constructor(
localRepository
.
save
(
LocalRepository
.
CURRENT_USERNAME_KEY
,
me
.
username
)
localRepository
.
save
(
LocalRepository
.
CURRENT_USERNAME_KEY
,
me
.
username
)
saveAccount
(
me
)
saveAccount
(
me
)
registerPushToken
()
registerPushToken
()
val
loginCredentials
=
Credential
.
Builder
(
email
)
view
.
saveSmartLockCredentials
(
username
,
password
)
.
setPassword
(
password
)
.
build
()
view
.
saveSmartLockCredentials
(
loginCredentials
)
navigator
.
toChatList
()
navigator
.
toChatList
()
}
catch
(
exception
:
RocketChatException
)
{
}
catch
(
exception
:
RocketChatException
)
{
exception
.
message
?.
let
{
exception
.
message
?.
let
{
...
...
app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupView.kt
View file @
1a137f9a
...
@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView {
...
@@ -27,7 +27,7 @@ interface SignupView : LoadingView, MessageView {
fun
alertBlankEmail
()
fun
alertBlankEmail
()
/**
/**
* Save
credentials via google smart lock
* Save
s Google Smart Lock credentials.
*/
*/
fun
saveSmartLockCredentials
(
loginCredential
:
Credential
)
fun
saveSmartLockCredentials
(
id
:
String
,
password
:
String
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/authentication/signup/ui/SignupFragment.kt
View file @
1a137f9a
package
chat.rocket.android.authentication.signup.ui
package
chat.rocket.android.authentication.signup.ui
import
DrawableHelper
import
DrawableHelper
import
android.app.Activity
.RESULT_OK
import
android.app.Activity
import
android.content.Intent
import
android.content.Intent
import
android.os.Build
import
android.os.Build
import
android.os.Bundle
import
android.os.Bundle
...
@@ -11,30 +11,24 @@ import android.view.LayoutInflater
...
@@ -11,30 +11,24 @@ import android.view.LayoutInflater
import
android.view.View
import
android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
import
android.view.ViewTreeObserver
import
android.view.ViewTreeObserver
import
android.widget.Toast
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.
authentication.login.ui.googleApiClient
import
chat.rocket.android.
R.string.message_credentials_saved_successfully
import
chat.rocket.android.authentication.signup.presentation.SignupPresenter
import
chat.rocket.android.authentication.signup.presentation.SignupPresenter
import
chat.rocket.android.authentication.signup.presentation.SignupView
import
chat.rocket.android.authentication.signup.presentation.SignupView
import
chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.SmartLockHelper
import
chat.rocket.android.helper.TextHelper
import
chat.rocket.android.helper.TextHelper
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.util.extensions.*
import
com.google.android.gms.auth.api.Auth
import
com.google.android.gms.auth.api.credentials.Credentials
import
com.google.android.gms.auth.api.credentials.Credential
import
com.google.android.gms.common.api.ResolvingResultCallbacks
import
com.google.android.gms.common.api.Status
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import
kotlinx.android.synthetic.main.fragment_authentication_sign_up.*
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
internal
const
val
SAVE_CREDENTIALS
=
1
internal
const
val
SAVE_CREDENTIALS
=
1
class
SignupFragment
:
Fragment
(),
SignupView
{
class
SignupFragment
:
Fragment
(),
SignupView
{
@Inject
@Inject
lateinit
var
presenter
:
SignupPresenter
lateinit
var
presenter
:
SignupPresenter
private
lateinit
var
credentialsToBeSaved
:
Credential
private
val
layoutListener
=
ViewTreeObserver
.
OnGlobalLayoutListener
{
private
val
layoutListener
=
ViewTreeObserver
.
OnGlobalLayoutListener
{
if
(
KeyboardHelper
.
isSoftKeyboardShown
(
relative_layout
.
rootView
))
{
if
(
KeyboardHelper
.
isSoftKeyboardShown
(
relative_layout
.
rootView
))
{
bottom_container
.
setVisible
(
false
)
bottom_container
.
setVisible
(
false
)
...
@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView {
...
@@ -120,44 +114,16 @@ class SignupFragment : Fragment(), SignupView {
}
}
}
}
override
fun
saveSmartLockCredentials
(
loginCredential
:
Credential
)
{
credentialsToBeSaved
=
loginCredential
googleApiClient
.
let
{
if
(
it
.
isConnected
)
{
saveCredentials
()
}
}
}
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
override
fun
onActivityResult
(
requestCode
:
Int
,
resultCode
:
Int
,
data
:
Intent
?)
{
if
(
requestCode
==
SAVE_CREDENTIALS
)
{
if
(
resultCode
==
Activity
.
RESULT_OK
)
{
if
(
resultCode
==
RESULT_OK
)
{
if
(
data
!=
null
)
{
Toast
.
makeText
(
if
(
requestCode
==
SAVE_CREDENTIALS
)
{
context
,
showMessage
(
getString
(
message_credentials_saved_successfully
))
getString
(
R
.
string
.
message_credentials_saved_successfully
),
}
Toast
.
LENGTH_SHORT
).
show
()
}
else
{
Timber
.
e
(
"ERROR: Cancelled by user"
)
}
}
}
}
}
}
private
fun
saveCredentials
()
{
activity
?.
let
{
Auth
.
CredentialsApi
.
save
(
googleApiClient
,
credentialsToBeSaved
).
setResultCallback
(
object
:
ResolvingResultCallbacks
<
Status
>(
it
,
SAVE_CREDENTIALS
)
{
override
fun
onSuccess
(
status
:
Status
)
{
Timber
.
d
(
"save:SUCCESS:$status"
)
}
override
fun
onUnresolvableFailure
(
status
:
Status
)
{
Timber
.
e
(
"save:FAILURE:$status"
)
}
})
}
}
override
fun
showLoading
()
{
override
fun
showLoading
()
{
ui
{
ui
{
enableUserInput
(
false
)
enableUserInput
(
false
)
...
@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView {
...
@@ -188,6 +154,12 @@ class SignupFragment : Fragment(), SignupView {
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
}
}
override
fun
saveSmartLockCredentials
(
id
:
String
,
password
:
String
)
{
activity
?.
let
{
SmartLockHelper
.
save
(
Credentials
.
getClient
(
it
),
it
,
id
,
password
)
}
}
private
fun
tintEditTextDrawableStart
()
{
private
fun
tintEditTextDrawableStart
()
{
ui
{
ui
{
val
personDrawable
=
val
personDrawable
=
...
...
app/src/main/java/chat/rocket/android/chatroom/adapter/ImageAttachmentViewHolder.kt
View file @
1a137f9a
...
@@ -30,7 +30,7 @@ class ImageAttachmentViewHolder(
...
@@ -30,7 +30,7 @@ class ImageAttachmentViewHolder(
file_name
.
text
=
data
.
attachmentTitle
file_name
.
text
=
data
.
attachmentTitle
image_attachment
.
setOnClickListener
{
image_attachment
.
setOnClickListener
{
ImageHelper
.
openImage
(
ImageHelper
.
openImage
(
it
.
context
,
context
,
data
.
attachmentUrl
,
data
.
attachmentUrl
,
data
.
attachmentTitle
.
toString
()
data
.
attachmentTitle
.
toString
()
)
)
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
1a137f9a
...
@@ -181,9 +181,7 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -181,9 +181,7 @@ class ChatRoomPresenter @Inject constructor(
}
}
subscribeTypingStatus
()
subscribeTypingStatus
()
if
(
offset
==
0L
)
{
subscribeState
()
subscribeState
()
}
}
}
}
}
...
@@ -229,10 +227,9 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -229,10 +227,9 @@ class ChatRoomPresenter @Inject constructor(
)
)
try
{
try
{
messagesRepository
.
save
(
newMessage
)
messagesRepository
.
save
(
newMessage
)
val
message
=
client
.
sendMessage
(
id
,
chatRoomId
,
text
)
view
.
showNewMessage
(
mapper
.
map
(
newMessage
,
RoomViewModel
(
view
.
showNewMessage
(
mapper
.
map
(
newMessage
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
)))
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
)))
message
client
.
sendMessage
(
id
,
chatRoomId
,
text
)
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
// Ok, not very beautiful, but the backend sends us a not valid response
// Ok, not very beautiful, but the backend sends us a not valid response
// When someone sends a message on a read-only channel, so we just ignore it
// When someone sends a message on a read-only channel, so we just ignore it
...
@@ -324,7 +321,7 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -324,7 +321,7 @@ class ChatRoomPresenter @Inject constructor(
}
}
}
}
private
fun
subscribeState
()
{
private
suspend
fun
subscribeState
()
{
Timber
.
d
(
"Subscribing to Status changes"
)
Timber
.
d
(
"Subscribing to Status changes"
)
lastState
=
manager
.
state
lastState
=
manager
.
state
manager
.
addStatusChannel
(
stateChannel
)
manager
.
addStatusChannel
(
stateChannel
)
...
@@ -790,12 +787,14 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -790,12 +787,14 @@ class ChatRoomPresenter @Inject constructor(
}
}
private
suspend
fun
subscribeTypingStatus
()
{
private
suspend
fun
subscribeTypingStatus
()
{
client
.
subscribeTypingStatus
(
chatRoomId
.
toString
())
{
_
,
id
->
launch
(
CommonPool
+
strategy
.
jobs
)
{
typingStatusSubscriptionId
=
id
client
.
subscribeTypingStatus
(
chatRoomId
.
toString
())
{
_
,
id
->
}
typingStatusSubscriptionId
=
id
}
for
(
typingStatus
in
client
.
typingStatusChannel
)
{
for
(
typingStatus
in
client
.
typingStatusChannel
)
{
processTypingStatus
(
typingStatus
)
processTypingStatus
(
typingStatus
)
}
}
}
}
}
...
@@ -837,6 +836,7 @@ class ChatRoomPresenter @Inject constructor(
...
@@ -837,6 +836,7 @@ class ChatRoomPresenter @Inject constructor(
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
,
RoomViewModel
(
val
viewModelStreamedMessage
=
mapper
.
map
(
streamedMessage
,
RoomViewModel
(
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
))
roles
=
chatRoles
,
isBroadcast
=
chatIsBroadcast
))
val
roomMessages
=
messagesRepository
.
getByRoomId
(
streamedMessage
.
roomId
)
val
roomMessages
=
messagesRepository
.
getByRoomId
(
streamedMessage
.
roomId
)
val
index
=
roomMessages
.
indexOfFirst
{
msg
->
msg
.
id
==
streamedMessage
.
id
}
val
index
=
roomMessages
.
indexOfFirst
{
msg
->
msg
.
id
==
streamedMessage
.
id
}
if
(
index
>
-
1
)
{
if
(
index
>
-
1
)
{
...
...
app/src/main/java/chat/rocket/android/chatroom/service/MessageService.kt
View file @
1a137f9a
...
@@ -64,7 +64,7 @@ class MessageService : JobService() {
...
@@ -64,7 +64,7 @@ class MessageService : JobService() {
Timber
.
e
(
ex
)
Timber
.
e
(
ex
)
// TODO - remove the generic message when we implement :userId:/message subscription
// TODO - remove the generic message when we implement :userId:/message subscription
if
(
ex
is
IllegalStateException
)
{
if
(
ex
is
IllegalStateException
)
{
Timber
.
d
(
ex
,
"Probably a read-only problem..."
)
Timber
.
e
(
ex
,
"Probably a read-only problem..."
)
// TODO: For now we are only going to reschedule when api is fixed.
// TODO: For now we are only going to reschedule when api is fixed.
messageRepository
.
removeById
(
message
.
id
)
messageRepository
.
removeById
(
message
.
id
)
jobFinished
(
params
,
false
)
jobFinished
(
params
,
false
)
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
1a137f9a
...
@@ -31,6 +31,8 @@ import chat.rocket.android.helper.KeyboardHelper
...
@@ -31,6 +31,8 @@ import chat.rocket.android.helper.KeyboardHelper
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.widget.emoji.*
import
chat.rocket.android.widget.emoji.*
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.model.ChatRoom
import
chat.rocket.core.model.ChatRoom
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
...
@@ -243,7 +245,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
...
@@ -243,7 +245,9 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if
(
recycler_view
.
adapter
==
null
)
{
if
(
recycler_view
.
adapter
==
null
)
{
adapter
=
ChatRoomAdapter
(
adapter
=
ChatRoomAdapter
(
chatRoomType
,
chatRoomName
,
presenter
,
chatRoomType
,
chatRoomName
,
presenter
,
reactionListener
=
this
@ChatRoomFragment
reactionListener
=
this
@ChatRoomFragment
)
)
recycler_view
.
adapter
=
adapter
recycler_view
.
adapter
=
adapter
...
@@ -272,9 +276,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
...
@@ -272,9 +276,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
)
{
)
{
// TODO: We should rely solely on the user being able to post, but we cannot guarantee
// TODO: We should rely solely on the user being able to post, but we cannot guarantee
// that the "(channels|groups).roles" endpoint is supported by the server in use.
// that the "(channels|groups).roles" endpoint is supported by the server in use.
setupMessageComposer
(
userCanPost
)
ui
{
isBroadcastChannel
=
channelIsBroadcast
setupMessageComposer
(
userCanPost
)
if
(
isBroadcastChannel
&&
!
userCanMod
)
activity
?.
invalidateOptionsMenu
()
isBroadcastChannel
=
channelIsBroadcast
if
(
isBroadcastChannel
&&
!
userCanMod
)
activity
?.
invalidateOptionsMenu
()
}
}
}
override
fun
openDirectMessage
(
chatRoom
:
ChatRoom
,
permalink
:
String
)
{
override
fun
openDirectMessage
(
chatRoom
:
ChatRoom
,
permalink
:
String
)
{
...
@@ -647,12 +653,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
...
@@ -647,12 +653,11 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
if
(
isChatRoomReadOnly
&&
!
canPost
)
{
if
(
isChatRoomReadOnly
&&
!
canPost
)
{
text_room_is_read_only
.
setVisible
(
true
)
text_room_is_read_only
.
setVisible
(
true
)
input_container
.
setVisible
(
false
)
input_container
.
setVisible
(
false
)
}
else
if
(!
isSubscribed
)
{
}
else
if
(!
isSubscribed
&&
roomTypeOf
(
chatRoomType
)
!=
RoomType
.
DIRECT_MESSAGE
)
{
input_container
.
setVisible
(
false
)
input_container
.
setVisible
(
false
)
button_join_chat
.
setVisible
(
true
)
button_join_chat
.
setVisible
(
true
)
button_join_chat
.
setOnClickListener
{
presenter
.
joinChat
(
chatRoomId
)
}
button_join_chat
.
setOnClickListener
{
presenter
.
joinChat
(
chatRoomId
)
}
}
else
{
}
else
{
button_send
.
alpha
=
0f
button_send
.
setVisible
(
false
)
button_send
.
setVisible
(
false
)
button_show_attachment_options
.
alpha
=
1f
button_show_attachment_options
.
alpha
=
1f
button_show_attachment_options
.
setVisible
(
true
)
button_show_attachment_options
.
setVisible
(
true
)
...
@@ -788,14 +793,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
...
@@ -788,14 +793,14 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
fun
setupComposeButtons
(
charSequence
:
CharSequence
)
{
private
fun
setupComposeButtons
(
charSequence
:
CharSequence
)
{
if
(
charSequence
.
isNotEmpty
()
&&
playComposeMessageButtonsAnimation
)
{
if
(
charSequence
.
isNotEmpty
()
&&
playComposeMessageButtonsAnimation
)
{
button_show_attachment_options
.
fadeOut
(
1F
,
0F
,
120
)
button_show_attachment_options
.
setVisible
(
false
)
button_send
.
fadeIn
(
0F
,
1F
,
120
)
button_send
.
setVisible
(
true
)
playComposeMessageButtonsAnimation
=
false
playComposeMessageButtonsAnimation
=
false
}
}
if
(
charSequence
.
isEmpty
())
{
if
(
charSequence
.
isEmpty
())
{
button_send
.
fadeOut
(
1F
,
0F
,
120
)
button_send
.
setVisible
(
false
)
button_show_attachment_options
.
fadeIn
(
0F
,
1F
,
120
)
button_show_attachment_options
.
setVisible
(
true
)
playComposeMessageButtonsAnimation
=
true
playComposeMessageButtonsAnimation
=
true
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/MessageViewModel.kt
View file @
1a137f9a
...
@@ -4,19 +4,19 @@ import chat.rocket.android.R
...
@@ -4,19 +4,19 @@ import chat.rocket.android.R
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
data class
MessageViewModel
(
data class
MessageViewModel
(
override
val
message
:
Message
,
override
val
message
:
Message
,
override
val
rawData
:
Message
,
override
val
rawData
:
Message
,
override
val
messageId
:
String
,
override
val
messageId
:
String
,
override
val
avatar
:
String
,
override
val
avatar
:
String
,
override
val
time
:
CharSequence
,
override
val
time
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
senderName
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
content
:
CharSequence
,
override
val
isPinned
:
Boolean
,
override
val
isPinned
:
Boolean
,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
reactions
:
List
<
ReactionViewModel
>,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
nextDownStreamMessage
:
BaseViewModel
<*>?
=
null
,
override
var
preview
:
Message
?
=
null
,
override
var
preview
:
Message
?
=
null
,
var
isFirstUnread
:
Boolean
,
var
isFirstUnread
:
Boolean
,
override
var
isTemporary
:
Boolean
=
false
override
var
isTemporary
:
Boolean
=
false
)
:
BaseMessageViewModel
<
Message
>
{
)
:
BaseMessageViewModel
<
Message
>
{
override
val
viewType
:
Int
override
val
viewType
:
Int
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
get
()
=
BaseViewModel
.
ViewType
.
MESSAGE
.
viewType
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ReactionViewModel.kt
View file @
1a137f9a
package
chat.rocket.android.chatroom.viewmodel
package
chat.rocket.android.chatroom.viewmodel
data class
ReactionViewModel
(
data class
ReactionViewModel
(
val
messageId
:
String
,
val
messageId
:
String
,
val
shortname
:
String
,
val
shortname
:
String
,
val
unicode
:
CharSequence
,
val
unicode
:
CharSequence
,
val
count
:
Int
,
val
count
:
Int
,
val
usernames
:
List
<
String
>
=
emptyList
()
val
usernames
:
List
<
String
>
=
emptyList
()
)
)
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
1a137f9a
...
@@ -65,70 +65,131 @@ class ViewModelMapper @Inject constructor(
...
@@ -65,70 +65,131 @@ class ViewModelMapper @Inject constructor(
private
val
currentUsername
:
String
?
=
localRepository
.
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
private
val
currentUsername
:
String
?
=
localRepository
.
get
(
LocalRepository
.
CURRENT_USERNAME_KEY
)
private
val
secondaryTextColor
=
ContextCompat
.
getColor
(
context
,
R
.
color
.
colorSecondaryText
)
private
val
secondaryTextColor
=
ContextCompat
.
getColor
(
context
,
R
.
color
.
colorSecondaryText
)
suspend
fun
map
(
message
:
Message
,
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
suspend
fun
map
(
roles
=
emptyList
(),
isBroadcast
=
true
)):
List
<
BaseViewModel
<*
>>
{
message
:
Message
,
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
roles
=
emptyList
(),
isBroadcast
=
true
)
):
List
<
BaseViewModel
<*
>>
{
return
translate
(
message
,
roomViewModel
)
return
translate
(
message
,
roomViewModel
)
}
}
suspend
fun
map
(
messages
:
List
<
Message
>,
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
suspend
fun
map
(
roles
=
emptyList
(),
isBroadcast
=
true
)):
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
messages
:
List
<
Message
>,
val
list
=
ArrayList
<
BaseViewModel
<*>>(
messages
.
size
)
roomViewModel
:
RoomViewModel
=
RoomViewModel
(
roles
=
emptyList
(),
isBroadcast
=
true
),
asNotReversed
:
Boolean
=
false
messages
.
forEach
{
):
List
<
BaseViewModel
<*
>>
=
list
.
addAll
(
translate
(
it
,
roomViewModel
))
withContext
(
CommonPool
)
{
val
list
=
ArrayList
<
BaseViewModel
<*>>(
messages
.
size
)
messages
.
forEach
{
list
.
addAll
(
if
(
asNotReversed
)
translateAsNotReversed
(
it
,
roomViewModel
)
else
translate
(
it
,
roomViewModel
)
)
}
return
@withContext
list
}
}
return
@withContext
list
private
suspend
fun
translate
(
}
message
:
Message
,
roomViewModel
:
RoomViewModel
):
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
val
list
=
ArrayList
<
BaseViewModel
<*>>()
private
suspend
fun
translate
(
message
:
Message
,
roomViewModel
:
RoomViewModel
)
message
.
urls
?.
forEach
{
:
List
<
BaseViewModel
<*
>>
=
withContext
(
CommonPool
)
{
val
url
=
mapUrl
(
message
,
it
)
val
list
=
ArrayList
<
BaseViewModel
<*>>()
url
?.
let
{
list
.
add
(
url
)
}
}
message
.
url
s
?.
forEach
{
message
.
attachment
s
?.
forEach
{
val
url
=
mapUrl
(
message
,
it
)
val
attachment
=
mapAttachment
(
message
,
it
)
url
?.
let
{
list
.
add
(
url
)
}
attachment
?.
let
{
list
.
add
(
attachment
)
}
}
}
message
.
attachments
?.
forEach
{
mapMessage
(
message
).
let
{
val
attachment
=
mapAttachment
(
message
,
it
)
if
(
list
.
isNotEmpty
())
{
attachment
?.
let
{
list
.
add
(
attachment
)
}
it
.
preview
=
list
.
first
().
preview
}
}
list
.
add
(
it
)
}
mapMessage
(
message
).
let
{
for
(
i
in
list
.
size
-
1
downTo
0
)
{
if
(
list
.
isNotEmpty
())
{
val
next
=
if
(
i
-
1
<
0
)
null
else
list
[
i
-
1
]
it
.
preview
=
list
.
first
().
preview
list
[
i
].
nextDownStreamMessage
=
next
}
if
(
isBroadcastReplyAvailable
(
roomViewModel
,
message
))
{
roomsInteractor
.
getById
(
currentServer
,
message
.
roomId
)
?.
let
{
chatRoom
->
val
replyViewModel
=
mapMessageReply
(
message
,
chatRoom
)
list
.
first
().
nextDownStreamMessage
=
replyViewModel
list
.
add
(
0
,
replyViewModel
)
}
}
}
list
.
add
(
it
)
}
for
(
i
in
list
.
size
-
1
downTo
0
)
{
return
@withContext
list
val
next
=
if
(
i
-
1
<
0
)
null
else
list
[
i
-
1
]
list
[
i
].
nextDownStreamMessage
=
next
}
}
if
(
isBroadcastReplyAvailable
(
roomViewModel
,
message
))
{
private
suspend
fun
translateAsNotReversed
(
roomsInteractor
.
getById
(
currentServer
,
message
.
roomId
)
?.
let
{
chatRoom
->
message
:
Message
,
val
replyViewModel
=
mapMessageReply
(
message
,
chatRoom
)
roomViewModel
:
RoomViewModel
list
.
first
().
nextDownStreamMessage
=
replyViewModel
):
List
<
BaseViewModel
<*
>>
=
list
.
add
(
0
,
replyViewModel
)
withContext
(
CommonPool
)
{
val
list
=
ArrayList
<
BaseViewModel
<*>>()
mapMessage
(
message
).
let
{
if
(
list
.
isNotEmpty
())
{
it
.
preview
=
list
.
first
().
preview
}
list
.
add
(
it
)
}
message
.
attachments
?.
forEach
{
val
attachment
=
mapAttachment
(
message
,
it
)
attachment
?.
let
{
list
.
add
(
attachment
)
}
}
}
}
return
@withContext
list
message
.
urls
?.
forEach
{
}
val
url
=
mapUrl
(
message
,
it
)
url
?.
let
{
list
.
add
(
url
)
}
}
for
(
i
in
list
.
size
-
1
downTo
0
)
{
val
next
=
if
(
i
-
1
<
0
)
null
else
list
[
i
-
1
]
list
[
i
].
nextDownStreamMessage
=
next
}
if
(
isBroadcastReplyAvailable
(
roomViewModel
,
message
))
{
roomsInteractor
.
getById
(
currentServer
,
message
.
roomId
)
?.
let
{
chatRoom
->
val
replyViewModel
=
mapMessageReply
(
message
,
chatRoom
)
list
.
first
().
nextDownStreamMessage
=
replyViewModel
list
.
add
(
0
,
replyViewModel
)
}
}
list
.
dropLast
(
1
).
forEach
{
it
.
reactions
=
emptyList
()
}
list
.
last
().
reactions
=
getReactions
(
message
)
list
.
last
().
nextDownStreamMessage
=
null
return
@withContext
list
}
private
fun
isBroadcastReplyAvailable
(
roomViewModel
:
RoomViewModel
,
message
:
Message
):
Boolean
{
private
fun
isBroadcastReplyAvailable
(
roomViewModel
:
RoomViewModel
,
message
:
Message
):
Boolean
{
val
senderUsername
=
message
.
sender
?.
username
val
senderUsername
=
message
.
sender
?.
username
return
roomViewModel
.
isRoom
&&
roomViewModel
.
isBroadcast
&&
return
roomViewModel
.
isRoom
&&
roomViewModel
.
isBroadcast
&&
!
message
.
isSystemMessage
()
&&
!
message
.
isSystemMessage
()
&&
senderUsername
!=
currentUsername
senderUsername
!=
currentUsername
}
}
private
fun
mapMessageReply
(
message
:
Message
,
chatRoom
:
ChatRoom
):
MessageReplyViewModel
{
private
fun
mapMessageReply
(
message
:
Message
,
chatRoom
:
ChatRoom
):
MessageReplyViewModel
{
val
name
=
message
.
sender
?.
name
val
name
=
message
.
sender
?.
name
val
roomName
=
if
(
settings
.
useRealName
()
&&
name
!=
null
)
name
else
message
.
sender
?.
username
val
roomName
=
?:
""
if
(
settings
.
useRealName
()
&&
name
!=
null
)
name
else
message
.
sender
?.
username
?:
""
val
permalink
=
messageHelper
.
createPermalink
(
message
,
chatRoom
)
val
permalink
=
messageHelper
.
createPermalink
(
message
,
chatRoom
)
return
MessageReplyViewModel
(
return
MessageReplyViewModel
(
messageId
=
message
.
id
,
messageId
=
message
.
id
,
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
1a137f9a
...
@@ -34,6 +34,7 @@ import chat.rocket.common.model.SimpleUser
...
@@ -34,6 +34,7 @@ import chat.rocket.common.model.SimpleUser
import
chat.rocket.common.model.User
import
chat.rocket.common.model.User
import
chat.rocket.common.util.ifNull
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.model.Subscription
import
chat.rocket.core.internal.realtime.createDirectMessage
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.StreamMessage
import
chat.rocket.core.internal.realtime.socket.model.StreamMessage
import
chat.rocket.core.internal.realtime.socket.model.Type
import
chat.rocket.core.internal.realtime.socket.model.Type
...
@@ -124,8 +125,19 @@ class ChatRoomsPresenter @Inject constructor(
...
@@ -124,8 +125,19 @@ class ChatRoomsPresenter @Inject constructor(
if
(
myself
?.
username
==
null
)
{
if
(
myself
?.
username
==
null
)
{
view
.
showMessage
(
R
.
string
.
msg_generic_error
)
view
.
showMessage
(
R
.
string
.
msg_generic_error
)
}
else
{
}
else
{
val
id
=
if
(
isDirectMessage
&&
!
chatRoom
.
open
)
{
retryIO
(
"createDirectMessage(${chatRoom.name})"
)
{
client
.
createDirectMessage
(
chatRoom
.
name
)
}
val
fromTo
=
mutableListOf
(
myself
.
id
,
chatRoom
.
id
).
apply
{
sort
()
}
fromTo
.
joinToString
(
""
)
}
else
{
chatRoom
.
id
}
val
isChatRoomOwner
=
chatRoom
.
user
?.
username
==
myself
.
username
||
isDirectMessage
val
isChatRoomOwner
=
chatRoom
.
user
?.
username
==
myself
.
username
||
isDirectMessage
navigator
.
toChatRoom
(
chatRoom
.
id
,
roomName
,
navigator
.
toChatRoom
(
id
,
roomName
,
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
,
chatRoom
.
type
.
toString
(),
chatRoom
.
readonly
?:
false
,
chatRoom
.
lastSeen
?:
-
1
,
chatRoom
.
lastSeen
?:
-
1
,
chatRoom
.
open
,
isChatRoomOwner
)
chatRoom
.
open
,
isChatRoomOwner
)
...
@@ -210,7 +222,7 @@ class ChatRoomsPresenter @Inject constructor(
...
@@ -210,7 +222,7 @@ class ChatRoomsPresenter @Inject constructor(
}
else
{
}
else
{
null
null
},
},
name
=
it
.
name
?:
""
,
name
=
it
.
username
?:
it
.
name
?:
""
,
fullName
=
it
.
name
,
fullName
=
it
.
name
,
readonly
=
false
,
readonly
=
false
,
updatedAt
=
null
,
updatedAt
=
null
,
...
@@ -640,4 +652,10 @@ class ChatRoomsPresenter @Inject constructor(
...
@@ -640,4 +652,10 @@ class ChatRoomsPresenter @Inject constructor(
manager
.
removeRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
manager
.
removeRoomsAndSubscriptionsChannel
(
subscriptionsChannel
)
manager
.
removeActiveUserChannel
(
activeUserChannel
)
manager
.
removeActiveUserChannel
(
activeUserChannel
)
}
}
fun
goToChatRoomWithId
(
chatRoomId
:
String
)
{
launchUI
(
strategy
)
{
chatRoomsInteractor
.
getById
(
currentServer
,
chatRoomId
)
?.
let
{
loadChatRoom
(
it
)
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
1a137f9a
...
@@ -2,8 +2,6 @@ package chat.rocket.android.chatrooms.ui
...
@@ -2,8 +2,6 @@ package chat.rocket.android.chatrooms.ui
import
android.content.Intent
import
android.content.Intent
import
android.app.AlertDialog
import
android.app.AlertDialog
import
android.content.Context
import
android.content.SharedPreferences
import
android.os.Bundle
import
android.os.Bundle
import
android.os.Handler
import
android.os.Handler
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
...
@@ -12,7 +10,12 @@ import android.support.v7.util.DiffUtil
...
@@ -12,7 +10,12 @@ import android.support.v7.util.DiffUtil
import
android.support.v7.widget.DefaultItemAnimator
import
android.support.v7.widget.DefaultItemAnimator
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.LinearLayoutManager
import
android.support.v7.widget.SearchView
import
android.support.v7.widget.SearchView
import
android.view.*
import
android.view.LayoutInflater
import
android.view.Menu
import
android.view.MenuInflater
import
android.view.MenuItem
import
android.view.View
import
android.view.ViewGroup
import
android.widget.CheckBox
import
android.widget.CheckBox
import
android.widget.RadioGroup
import
android.widget.RadioGroup
import
androidx.core.view.isVisible
import
androidx.core.view.isVisible
...
@@ -26,7 +29,12 @@ import chat.rocket.android.helper.SharedPreferenceHelper
...
@@ -26,7 +29,12 @@ import chat.rocket.android.helper.SharedPreferenceHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.server.domain.SettingsRepository
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.ui
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.android.widget.DividerItemDecoration
import
chat.rocket.common.model.RoomType
import
chat.rocket.common.model.RoomType
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.State
...
@@ -38,6 +46,8 @@ import kotlinx.coroutines.experimental.NonCancellable.isActive
...
@@ -38,6 +46,8 @@ import kotlinx.coroutines.experimental.NonCancellable.isActive
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
private
const
val
BUNDLE_CHAT_ROOM_ID
=
"BUNDLE_CHAT_ROOM_ID"
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
class
ChatRoomsFragment
:
Fragment
(),
ChatRoomsView
{
@Inject
@Inject
lateinit
var
presenter
:
ChatRoomsPresenter
lateinit
var
presenter
:
ChatRoomsPresenter
...
@@ -52,15 +62,30 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
...
@@ -52,15 +62,30 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
private
var
listJob
:
Job
?
=
null
private
var
listJob
:
Job
?
=
null
private
var
sectionedAdapter
:
SimpleSectionedRecyclerViewAdapter
?
=
null
private
var
sectionedAdapter
:
SimpleSectionedRecyclerViewAdapter
?
=
null
private
var
chatRoomId
:
String
?
=
null
companion
object
{
companion
object
{
fun
newInstance
()
=
ChatRoomsFragment
()
fun
newInstance
(
chatRoomId
:
String
?
=
null
):
ChatRoomsFragment
{
return
ChatRoomsFragment
().
apply
{
arguments
=
Bundle
(
1
).
apply
{
putString
(
BUNDLE_CHAT_ROOM_ID
,
chatRoomId
)
}
}
}
}
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
AndroidSupportInjection
.
inject
(
this
)
AndroidSupportInjection
.
inject
(
this
)
setHasOptionsMenu
(
true
)
setHasOptionsMenu
(
true
)
val
bundle
=
arguments
if
(
bundle
!=
null
)
{
chatRoomId
=
bundle
.
getString
(
BUNDLE_CHAT_ROOM_ID
)
chatRoomId
?.
let
{
presenter
.
goToChatRoomWithId
(
it
)
chatRoomId
=
null
}
}
}
}
override
fun
onDestroy
()
{
override
fun
onDestroy
()
{
...
...
app/src/main/java/chat/rocket/android/dagger/module/ServiceBuilder.kt
View file @
1a137f9a
...
@@ -2,10 +2,10 @@ package chat.rocket.android.dagger.module
...
@@ -2,10 +2,10 @@ package chat.rocket.android.dagger.module
import
chat.rocket.android.chatroom.di.MessageServiceProvider
import
chat.rocket.android.chatroom.di.MessageServiceProvider
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.chatroom.service.MessageService
import
chat.rocket.android.push.FirebaseMessagingService
import
chat.rocket.android.push.FirebaseTokenService
import
chat.rocket.android.push.FirebaseTokenService
import
chat.rocket.android.push.
GcmListenerService
import
chat.rocket.android.push.
di.FirebaseMessagingServiceProvider
import
chat.rocket.android.push.di.FirebaseTokenServiceProvider
import
chat.rocket.android.push.di.FirebaseTokenServiceProvider
import
chat.rocket.android.push.di.GcmListenerServiceProvider
import
dagger.Module
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
import
dagger.android.ContributesAndroidInjector
...
@@ -14,8 +14,8 @@ import dagger.android.ContributesAndroidInjector
...
@@ -14,8 +14,8 @@ import dagger.android.ContributesAndroidInjector
@ContributesAndroidInjector
(
modules
=
[
FirebaseTokenServiceProvider
::
class
])
@ContributesAndroidInjector
(
modules
=
[
FirebaseTokenServiceProvider
::
class
])
abstract
fun
bindFirebaseTokenService
():
FirebaseTokenService
abstract
fun
bindFirebaseTokenService
():
FirebaseTokenService
@ContributesAndroidInjector
(
modules
=
[
GcmListener
ServiceProvider
::
class
])
@ContributesAndroidInjector
(
modules
=
[
FirebaseMessaging
ServiceProvider
::
class
])
abstract
fun
bindGcmListenerService
():
GcmListener
Service
abstract
fun
bindGcmListenerService
():
FirebaseMessaging
Service
@ContributesAndroidInjector
(
modules
=
[
MessageServiceProvider
::
class
])
@ContributesAndroidInjector
(
modules
=
[
MessageServiceProvider
::
class
])
abstract
fun
bindMessageService
():
MessageService
abstract
fun
bindMessageService
():
MessageService
...
...
app/src/main/java/chat/rocket/android/favoritemessages/presentation/FavoriteMessagesPresenter.kt
View file @
1a137f9a
...
@@ -25,9 +25,9 @@ class FavoriteMessagesPresenter @Inject constructor(
...
@@ -25,9 +25,9 @@ class FavoriteMessagesPresenter @Inject constructor(
private
var
offset
:
Int
=
0
private
var
offset
:
Int
=
0
/**
/**
* Loads all favorite messages for
room.
the given room id.
* Loads all favorite messages for the given room id.
*
*
* @param roomId The id of the room to get
its favorite messages
.
* @param roomId The id of the room to get
favorite messages from
.
*/
*/
fun
loadFavoriteMessages
(
roomId
:
String
)
{
fun
loadFavoriteMessages
(
roomId
:
String
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
...
@@ -35,7 +35,7 @@ class FavoriteMessagesPresenter @Inject constructor(
...
@@ -35,7 +35,7 @@ class FavoriteMessagesPresenter @Inject constructor(
view
.
showLoading
()
view
.
showLoading
()
roomsInteractor
.
getById
(
serverUrl
,
roomId
)
?.
let
{
roomsInteractor
.
getById
(
serverUrl
,
roomId
)
?.
let
{
val
favoriteMessages
=
client
.
getFavoriteMessages
(
roomId
,
it
.
type
,
offset
)
val
favoriteMessages
=
client
.
getFavoriteMessages
(
roomId
,
it
.
type
,
offset
)
val
messageList
=
mapper
.
map
(
favoriteMessages
.
result
)
val
messageList
=
mapper
.
map
(
favoriteMessages
.
result
,
asNotReversed
=
true
)
view
.
showFavoriteMessages
(
messageList
)
view
.
showFavoriteMessages
(
messageList
)
offset
+=
1
*
30
offset
+=
1
*
30
}.
ifNull
{
}.
ifNull
{
...
...
app/src/main/java/chat/rocket/android/favoritemessages/ui/FavoriteMessagesFragment.kt
View file @
1a137f9a
...
@@ -72,7 +72,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
...
@@ -72,7 +72,7 @@ class FavoriteMessagesFragment : Fragment(), FavoriteMessagesView {
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
recycler_view
.
layoutManager
=
linearLayoutManager
recycler_view
.
layoutManager
=
linearLayoutManager
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
if
(
favoriteMessages
.
size
>
1
0
)
{
if
(
favoriteMessages
.
size
>
=
3
0
)
{
recycler_view
.
addOnScrollListener
(
object
:
recycler_view
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
override
fun
onLoadMore
(
...
...
app/src/main/java/chat/rocket/android/files/ui/FilesFragment.kt
View file @
1a137f9a
...
@@ -94,17 +94,13 @@ class FilesFragment : Fragment(), FilesView {
...
@@ -94,17 +94,13 @@ class FilesFragment : Fragment(), FilesView {
override
fun
playMedia
(
url
:
String
)
{
override
fun
playMedia
(
url
:
String
)
{
ui
{
ui
{
activity
?.
let
{
PlayerActivity
.
play
(
it
,
url
)
PlayerActivity
.
play
(
it
,
url
)
}
}
}
}
}
override
fun
openImage
(
url
:
String
,
name
:
String
)
{
override
fun
openImage
(
url
:
String
,
name
:
String
)
{
ui
{
ui
{
activity
?.
let
{
ImageHelper
.
openImage
(
root_layout
.
context
,
url
,
name
)
ImageHelper
.
openImage
(
it
,
url
,
name
)
}
}
}
}
}
...
...
app/src/main/java/chat/rocket/android/files/viewmodel/FileViewModel.kt
View file @
1a137f9a
...
@@ -41,9 +41,10 @@ class FileViewModel(
...
@@ -41,9 +41,10 @@ class FileViewModel(
}
}
private
fun
getFileUploadDate
():
String
{
private
fun
getFileUploadDate
():
String
{
return
DateTimeHelper
.
getDateTime
(
genericAttachment
.
uploadedAt
?.
let
{
DateTimeHelper
.
getLocalDateTime
(
genericAttachment
.
uploadedAt
)
return
DateTimeHelper
.
getDateTime
(
DateTimeHelper
.
getLocalDateTime
(
it
))
)
}
return
""
}
}
private
fun
getFileUrl
():
String
?
{
private
fun
getFileUrl
():
String
?
{
...
...
app/src/main/java/chat/rocket/android/helper/ImageHelper.kt
View file @
1a137f9a
...
@@ -53,7 +53,6 @@ object ImageHelper {
...
@@ -53,7 +53,6 @@ object ImageHelper {
)
)
val
toolbar
=
Toolbar
(
context
).
also
{
val
toolbar
=
Toolbar
(
context
).
also
{
it
.
inflateMenu
(
R
.
menu
.
image_actions
)
it
.
inflateMenu
(
R
.
menu
.
image_actions
)
it
.
overflowIcon
?.
setTint
(
Color
.
WHITE
)
it
.
setOnMenuItemClickListener
{
it
.
setOnMenuItemClickListener
{
return
@setOnMenuItemClickListener
when
(
it
.
itemId
)
{
return
@setOnMenuItemClickListener
when
(
it
.
itemId
)
{
R
.
id
.
action_save_image
->
saveImage
(
context
)
R
.
id
.
action_save_image
->
saveImage
(
context
)
...
@@ -109,7 +108,6 @@ object ImageHelper {
...
@@ -109,7 +108,6 @@ object ImageHelper {
.
hideStatusBar
(
false
)
.
hideStatusBar
(
false
)
.
setCustomDraweeControllerBuilder
(
builder
)
.
setCustomDraweeControllerBuilder
(
builder
)
.
show
()
.
show
()
}
}
private
fun
saveImage
(
context
:
Context
):
Boolean
{
private
fun
saveImage
(
context
:
Context
):
Boolean
{
...
@@ -166,5 +164,4 @@ object ImageHelper {
...
@@ -166,5 +164,4 @@ object ImageHelper {
)
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/helper/SmartLockHelper.kt
0 → 100644
View file @
1a137f9a
package
chat.rocket.android.helper
import
android.app.Activity
import
android.content.IntentSender
import
android.support.v4.app.FragmentActivity
import
com.google.android.gms.auth.api.credentials.*
import
com.google.android.gms.common.api.CommonStatusCodes
import
com.google.android.gms.common.api.ResolvableApiException
import
timber.log.Timber
const
val
REQUEST_CODE_FOR_SIGN_IN_REQUIRED
=
1
const
val
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION
=
2
const
val
REQUEST_CODE_FOR_SAVE_RESOLUTION
=
3
/**
* This class handles some cases of Google Smart Lock for passwords like the request to retrieve
* credentials, to retrieve sign-in hints and to store the credentials.
*
* See https://developers.google.com/identity/smartlock-passwords/android/overview for futher
* information.
*/
object
SmartLockHelper
{
/**
* Requests for stored Google Smart Lock credentials.
* Note that in case of exception it will try to start a sign in
* ([REQUEST_CODE_FOR_SIGN_IN_REQUIRED]) or "multiple account"
* ([REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION]) resolution.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @return null or the [Credential] result.
*/
fun
requestStoredCredentials
(
credentialsClient
:
CredentialsClient
,
activity
:
Activity
):
Credential
?
{
var
credential
:
Credential
?
=
null
val
credentialRequest
=
CredentialRequest
.
Builder
()
.
setPasswordLoginSupported
(
true
)
.
build
()
credentialsClient
.
request
(
credentialRequest
)
.
addOnCompleteListener
{
when
{
it
.
isSuccessful
->
{
credential
=
it
.
result
.
credential
}
it
.
exception
is
ResolvableApiException
->
{
val
resolvableApiException
=
(
it
.
exception
as
ResolvableApiException
)
if
(
resolvableApiException
.
statusCode
==
CommonStatusCodes
.
SIGN_IN_REQUIRED
)
{
provideSignInHint
(
credentialsClient
,
activity
)
}
else
{
// This is most likely the case where the user has multiple saved
// credentials and needs to pick one. This requires showing UI to
// resolve the read request.
resolveResult
(
resolvableApiException
,
REQUEST_CODE_FOR_MULTIPLE_ACCOUNTS_RESOLUTION
,
activity
)
}
}
}
}
return
credential
}
/**
* Saves a user credential to Google Smart Lock.
* Note that in case of exception it will try to start a save resolution,
* so the activity/fragment should expected for a request code
* ([REQUEST_CODE_FOR_SAVE_RESOLUTION]) on onActivityResult call.
*
* @param credentialsClient The credential client.
* @param activity The activity.
* @param id The user id credential.
* @param password The user password credential.
*/
fun
save
(
credentialsClient
:
CredentialsClient
,
activity
:
FragmentActivity
,
id
:
String
,
password
:
String
)
{
val
credential
=
Credential
.
Builder
(
id
)
.
setPassword
(
password
)
.
build
()
credentialsClient
.
save
(
credential
)
.
addOnCompleteListener
{
val
exception
=
it
.
exception
if
(
exception
is
ResolvableApiException
)
{
// Try to resolve the save request. This will prompt the user if
// the credential is new.
try
{
exception
.
startResolutionForResult
(
activity
,
REQUEST_CODE_FOR_SAVE_RESOLUTION
)
}
catch
(
e
:
IntentSender
.
SendIntentException
)
{
Timber
.
e
(
"Failed to send resolution. Exception is: $e"
)
}
}
}
}
private
fun
provideSignInHint
(
credentialsClient
:
CredentialsClient
,
activity
:
Activity
)
{
val
hintRequest
=
HintRequest
.
Builder
()
.
setHintPickerConfig
(
CredentialPickerConfig
.
Builder
()
.
setShowCancelButton
(
true
)
.
build
()
)
.
setEmailAddressIdentifierSupported
(
true
)
.
build
()
try
{
val
intent
=
credentialsClient
.
getHintPickerIntent
(
hintRequest
)
activity
.
startIntentSenderForResult
(
intent
.
intentSender
,
REQUEST_CODE_FOR_SIGN_IN_REQUIRED
,
null
,
0
,
0
,
0
,
null
)
}
catch
(
e
:
IntentSender
.
SendIntentException
)
{
Timber
.
e
(
"Could not start hint picker Intent. Exception is: $e"
)
}
}
private
fun
resolveResult
(
exception
:
ResolvableApiException
,
requestCode
:
Int
,
activity
:
Activity
)
{
try
{
exception
.
startResolutionForResult
(
activity
,
requestCode
)
}
catch
(
e
:
IntentSender
.
SendIntentException
)
{
Timber
.
e
(
"Failed to send resolution. Exception is: $e"
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/presentation/MainNavigator.kt
View file @
1a137f9a
...
@@ -12,9 +12,9 @@ import chat.rocket.android.util.extensions.addFragment
...
@@ -12,9 +12,9 @@ import chat.rocket.android.util.extensions.addFragment
class
MainNavigator
(
internal
val
activity
:
MainActivity
)
{
class
MainNavigator
(
internal
val
activity
:
MainActivity
)
{
fun
toChatList
()
{
fun
toChatList
(
chatRoomId
:
String
?
=
null
)
{
activity
.
addFragment
(
"ChatRoomsFragment"
,
R
.
id
.
fragment_container
)
{
activity
.
addFragment
(
"ChatRoomsFragment"
,
R
.
id
.
fragment_container
)
{
ChatRoomsFragment
.
newInstance
()
ChatRoomsFragment
.
newInstance
(
chatRoomId
)
}
}
}
}
...
@@ -43,7 +43,7 @@ class MainNavigator(internal val activity: MainActivity) {
...
@@ -43,7 +43,7 @@ class MainNavigator(internal val activity: MainActivity) {
}
}
fun
toNewServer
(
serverUrl
:
String
?
=
null
)
{
fun
toNewServer
(
serverUrl
:
String
?
=
null
)
{
activity
.
startActivity
(
activity
.
changeServerIntent
(
serverUrl
))
activity
.
startActivity
(
activity
.
changeServerIntent
(
serverUrl
=
serverUrl
))
activity
.
finish
()
activity
.
finish
()
}
}
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
1a137f9a
...
@@ -49,7 +49,7 @@ class MainPresenter @Inject constructor(
...
@@ -49,7 +49,7 @@ class MainPresenter @Inject constructor(
private
val
userDataChannel
=
Channel
<
Myself
>()
private
val
userDataChannel
=
Channel
<
Myself
>()
fun
toChatList
(
)
=
navigator
.
toChatList
(
)
fun
toChatList
(
chatRoomId
:
String
?
=
null
)
=
navigator
.
toChatList
(
chatRoomId
)
fun
toUserProfile
()
=
navigator
.
toUserProfile
()
fun
toUserProfile
()
=
navigator
.
toUserProfile
()
...
@@ -105,13 +105,10 @@ class MainPresenter @Inject constructor(
...
@@ -105,13 +105,10 @@ class MainPresenter @Inject constructor(
disconnect
()
disconnect
()
removeAccountInteractor
.
remove
(
currentServer
)
removeAccountInteractor
.
remove
(
currentServer
)
tokenRepository
.
remove
(
currentServer
)
tokenRepository
.
remove
(
currentServer
)
view
.
disableAutoSignIn
()
navigator
.
toNewServer
()
navigator
.
toNewServer
()
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error cleaning up the session..."
)
Timber
.
d
(
ex
,
"Error cleaning up the session..."
)
}
}
view
.
disableAutoSignIn
()
navigator
.
toNewServer
()
}
}
}
}
...
@@ -178,6 +175,7 @@ class MainPresenter @Inject constructor(
...
@@ -178,6 +175,7 @@ class MainPresenter @Inject constructor(
if
(
pushToken
!=
null
)
{
if
(
pushToken
!=
null
)
{
try
{
try
{
retryIO
(
"unregisterPushToken"
)
{
client
.
unregisterPushToken
(
pushToken
)
}
retryIO
(
"unregisterPushToken"
)
{
client
.
unregisterPushToken
(
pushToken
)
}
view
.
invalidateToken
(
pushToken
)
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error unregistering push token"
)
Timber
.
d
(
ex
,
"Error unregistering push token"
)
}
}
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainView.kt
View file @
1a137f9a
...
@@ -25,8 +25,5 @@ interface MainView : MessageView, VersionCheckView {
...
@@ -25,8 +25,5 @@ interface MainView : MessageView, VersionCheckView {
fun
closeServerSelection
()
fun
closeServerSelection
()
/**
fun
invalidateToken
(
token
:
String
)
* callback to disable auto sign in for google smart lock when the user logs out
*/
fun
disableAutoSignIn
()
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
View file @
1a137f9a
...
@@ -18,15 +18,15 @@ import chat.rocket.android.main.presentation.MainPresenter
...
@@ -18,15 +18,15 @@ import chat.rocket.android.main.presentation.MainPresenter
import
chat.rocket.android.main.presentation.MainView
import
chat.rocket.android.main.presentation.MainView
import
chat.rocket.android.main.viewmodel.NavHeaderViewModel
import
chat.rocket.android.main.viewmodel.NavHeaderViewModel
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
import
chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.rotateBy
import
chat.rocket.android.util.extensions.rotateBy
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.common.model.UserStatus
import
chat.rocket.common.model.UserStatus
import
com.google.android.gms.auth.api.Auth
import
com.google.firebase.iid.FirebaseInstanceId
import
com.google.firebase.messaging.FirebaseMessaging
import
com.google.android.gms.common.api.GoogleApiClient
import
com.google.android.gms.common.api.GoogleApiClient
import
com.google.android.gms.gcm.GoogleCloudMessaging
import
com.google.android.gms.iid.InstanceID
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjector
import
dagger.android.AndroidInjector
import
dagger.android.DispatchingAndroidInjector
import
dagger.android.DispatchingAndroidInjector
...
@@ -40,8 +40,10 @@ import kotlinx.coroutines.experimental.launch
...
@@ -40,8 +40,10 @@ import kotlinx.coroutines.experimental.launch
import
timber.log.Timber
import
timber.log.Timber
import
javax.inject.Inject
import
javax.inject.Inject
class
MainActivity
:
AppCompatActivity
(),
MainView
,
HasActivityInjector
,
HasSupportFragmentInjector
,
private
const
val
CURRENT_STATE
=
"current_state"
GoogleApiClient
.
ConnectionCallbacks
{
class
MainActivity
:
AppCompatActivity
(),
MainView
,
HasActivityInjector
,
HasSupportFragmentInjector
{
@Inject
@Inject
lateinit
var
activityDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Activity
>
lateinit
var
activityDispatchingAndroidInjector
:
DispatchingAndroidInjector
<
Activity
>
@Inject
@Inject
...
@@ -50,70 +52,46 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
...
@@ -50,70 +52,46 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
lateinit
var
presenter
:
MainPresenter
lateinit
var
presenter
:
MainPresenter
private
var
isFragmentAdded
:
Boolean
=
false
private
var
isFragmentAdded
:
Boolean
=
false
private
var
expanded
=
false
private
var
expanded
=
false
private
lateinit
var
googleApiClient
:
GoogleApiClient
private
val
headerLayout
by
lazy
{
view_navigation
.
getHeaderView
(
0
)
}
private
val
headerLayout
by
lazy
{
view_navigation
.
getHeaderView
(
0
)
}
private
var
chatRoomId
:
String
?
=
null
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
AndroidInjection
.
inject
(
this
)
AndroidInjection
.
inject
(
this
)
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
setContentView
(
R
.
layout
.
activity_main
)
setContentView
(
R
.
layout
.
activity_main
)
buildGoogleApiClient
()
launch
(
CommonPool
)
{
launch
(
CommonPool
)
{
try
{
try
{
val
token
=
InstanceID
.
getInstance
(
this
@MainActivity
).
getToken
(
val
token
=
FirebaseInstanceId
.
getInstance
().
token
getString
(
R
.
string
.
gcm_sender_id
),
Timber
.
d
(
"FCM token: $token"
)
GoogleCloudMessaging
.
INSTANCE_ID_SCOPE
,
null
)
Timber
.
d
(
"GCM token: $token"
)
presenter
.
refreshToken
(
token
)
presenter
.
refreshToken
(
token
)
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Missing play services..."
)
Timber
.
d
(
ex
,
"Missing play services..."
)
}
}
}
}
chatRoomId
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
presenter
.
connect
()
presenter
.
connect
()
presenter
.
loadCurrentInfo
()
presenter
.
loadCurrentInfo
()
setupToolbar
()
setupToolbar
()
setupNavigationView
()
setupNavigationView
()
}
}
override
fun
onConnected
(
bundle
:
Bundle
?)
{
override
fun
onSaveInstanceState
(
outState
:
Bundle
?)
{
}
super
.
onSaveInstanceState
(
outState
)
outState
?.
putBoolean
(
CURRENT_STATE
,
isFragmentAdded
)
override
fun
onConnectionSuspended
(
errorCode
:
Int
)
{
}
private
fun
buildGoogleApiClient
()
{
googleApiClient
=
GoogleApiClient
.
Builder
(
this
)
.
enableAutoManage
(
this
,
{
Timber
.
d
(
"ERROR: connection to client failed"
)
})
.
addConnectionCallbacks
(
this
)
.
addApi
(
Auth
.
CREDENTIALS_API
)
.
build
()
}
}
override
fun
onStart
()
{
override
fun
onRestoreInstanceState
(
savedInstanceState
:
Bundle
?)
{
super
.
onStart
()
super
.
onRestoreInstanceState
(
savedInstanceState
)
googleApiClient
.
let
{
isFragmentAdded
=
savedInstanceState
?.
getBoolean
(
CURRENT_STATE
)
?:
false
if
(
it
.
isConnected
)
{
Timber
.
d
(
"Google api client connected successfully"
)
}
}
}
override
fun
disableAutoSignIn
()
{
googleApiClient
.
let
{
if
(
it
.
isConnected
)
{
Auth
.
CredentialsApi
.
disableAutoSignIn
(
googleApiClient
)
}
}
}
}
override
fun
onResume
()
{
override
fun
onResume
()
{
super
.
onResume
()
super
.
onResume
()
if
(!
isFragmentAdded
)
{
if
(!
isFragmentAdded
)
{
presenter
.
toChatList
()
presenter
.
toChatList
(
chatRoomId
)
isFragmentAdded
=
true
isFragmentAdded
=
true
}
}
}
}
...
@@ -125,6 +103,12 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
...
@@ -125,6 +103,12 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
}
}
}
}
override
fun
activityInjector
():
AndroidInjector
<
Activity
>
=
activityDispatchingAndroidInjector
override
fun
supportFragmentInjector
():
AndroidInjector
<
Fragment
>
=
fragmentDispatchingAndroidInjector
override
fun
showUserStatus
(
userStatus
:
UserStatus
)
{
override
fun
showUserStatus
(
userStatus
:
UserStatus
)
{
headerLayout
.
apply
{
headerLayout
.
apply
{
image_user_status
.
setImageDrawable
(
image_user_status
.
setImageDrawable
(
...
@@ -188,38 +172,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
...
@@ -188,38 +172,8 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
.
show
()
.
show
()
}
}
private
fun
setupAccountsList
(
header
:
View
,
accounts
:
List
<
Account
>)
{
override
fun
invalidateToken
(
token
:
String
)
{
accounts_list
.
layoutManager
=
LinearLayoutManager
(
this
)
FirebaseInstanceId
.
getInstance
().
deleteToken
(
token
,
FirebaseMessaging
.
INSTANCE_ID_SCOPE
)
accounts_list
.
adapter
=
AccountsAdapter
(
accounts
,
object
:
Selector
{
override
fun
onStatusSelected
(
userStatus
:
UserStatus
)
{
presenter
.
changeDefaultStatus
(
userStatus
)
}
override
fun
onAccountSelected
(
serverUrl
:
String
)
{
presenter
.
changeServer
(
serverUrl
)
}
override
fun
onAddedAccountSelected
()
{
presenter
.
addNewServer
()
}
})
header
.
account_container
.
setOnClickListener
{
header
.
image_account_expand
.
rotateBy
(
180f
)
if
(
expanded
)
{
accounts_list
.
fadeOut
()
}
else
{
accounts_list
.
fadeIn
()
}
expanded
=
!
expanded
}
header
.
image_avatar
.
setOnClickListener
{
view_navigation
.
menu
.
findItem
(
R
.
id
.
action_profile
).
isChecked
=
true
presenter
.
toUserProfile
()
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
}
}
}
override
fun
showMessage
(
resId
:
Int
)
=
showToast
(
resId
)
override
fun
showMessage
(
resId
:
Int
)
=
showToast
(
resId
)
...
@@ -228,11 +182,6 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
...
@@ -228,11 +182,6 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
showGenericErrorMessage
()
=
showMessage
(
getString
(
R
.
string
.
msg_generic_error
))
override
fun
activityInjector
():
AndroidInjector
<
Activity
>
=
activityDispatchingAndroidInjector
override
fun
supportFragmentInjector
():
AndroidInjector
<
Fragment
>
=
fragmentDispatchingAndroidInjector
private
fun
setupToolbar
()
{
private
fun
setupToolbar
()
{
setSupportActionBar
(
toolbar
)
setSupportActionBar
(
toolbar
)
toolbar
.
setNavigationIcon
(
R
.
drawable
.
ic_menu_white_24dp
)
toolbar
.
setNavigationIcon
(
R
.
drawable
.
ic_menu_white_24dp
)
...
@@ -266,4 +215,38 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
...
@@ -266,4 +215,38 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
}
}
}
}
}
}
private
fun
setupAccountsList
(
header
:
View
,
accounts
:
List
<
Account
>)
{
accounts_list
.
layoutManager
=
LinearLayoutManager
(
this
)
accounts_list
.
adapter
=
AccountsAdapter
(
accounts
,
object
:
Selector
{
override
fun
onStatusSelected
(
userStatus
:
UserStatus
)
{
presenter
.
changeDefaultStatus
(
userStatus
)
}
override
fun
onAccountSelected
(
serverUrl
:
String
)
{
presenter
.
changeServer
(
serverUrl
)
}
override
fun
onAddedAccountSelected
()
{
presenter
.
addNewServer
()
}
})
header
.
account_container
.
setOnClickListener
{
header
.
image_account_expand
.
rotateBy
(
180f
)
if
(
expanded
)
{
accounts_list
.
fadeOut
()
}
else
{
accounts_list
.
fadeIn
()
}
expanded
=
!
expanded
}
header
.
image_avatar
.
setOnClickListener
{
view_navigation
.
menu
.
findItem
(
R
.
id
.
action_profile
).
isChecked
=
true
presenter
.
toUserProfile
()
drawer_layout
.
closeDrawer
(
Gravity
.
START
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/pinnedmessages/presentation/PinnedMessagesPresenter.kt
View file @
1a137f9a
...
@@ -26,7 +26,7 @@ class PinnedMessagesPresenter @Inject constructor(
...
@@ -26,7 +26,7 @@ class PinnedMessagesPresenter @Inject constructor(
private
var
offset
:
Int
=
0
private
var
offset
:
Int
=
0
/**
/**
* Load all pinned messages for the given room id.
* Load
s
all pinned messages for the given room id.
*
*
* @param roomId The id of the room to get pinned messages from.
* @param roomId The id of the room to get pinned messages from.
*/
*/
...
@@ -36,8 +36,7 @@ class PinnedMessagesPresenter @Inject constructor(
...
@@ -36,8 +36,7 @@ class PinnedMessagesPresenter @Inject constructor(
view
.
showLoading
()
view
.
showLoading
()
roomsInteractor
.
getById
(
serverUrl
,
roomId
)
?.
let
{
roomsInteractor
.
getById
(
serverUrl
,
roomId
)
?.
let
{
val
pinnedMessages
=
client
.
getPinnedMessages
(
roomId
,
it
.
type
,
offset
)
val
pinnedMessages
=
client
.
getPinnedMessages
(
roomId
,
it
.
type
,
offset
)
val
messageList
=
val
messageList
=
mapper
.
map
(
pinnedMessages
.
result
,
asNotReversed
=
true
)
mapper
.
map
(
pinnedMessages
.
result
.
filterNot
{
it
.
isSystemMessage
()
})
view
.
showPinnedMessages
(
messageList
)
view
.
showPinnedMessages
(
messageList
)
offset
+=
1
*
30
offset
+=
1
*
30
}.
ifNull
{
}.
ifNull
{
...
...
app/src/main/java/chat/rocket/android/pinnedmessages/ui/PinnedMessagesFragment.kt
View file @
1a137f9a
...
@@ -74,7 +74,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
...
@@ -74,7 +74,7 @@ class PinnedMessagesFragment : Fragment(), PinnedMessagesView {
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
LinearLayoutManager
(
context
,
LinearLayoutManager
.
VERTICAL
,
false
)
recycler_view_pinned
.
layoutManager
=
linearLayoutManager
recycler_view_pinned
.
layoutManager
=
linearLayoutManager
recycler_view_pinned
.
itemAnimator
=
DefaultItemAnimator
()
recycler_view_pinned
.
itemAnimator
=
DefaultItemAnimator
()
if
(
pinnedMessages
.
size
>
1
0
)
{
if
(
pinnedMessages
.
size
>
=
3
0
)
{
recycler_view_pinned
.
addOnScrollListener
(
object
:
recycler_view_pinned
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
override
fun
onLoadMore
(
override
fun
onLoadMore
(
...
...
app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt
View file @
1a137f9a
...
@@ -58,7 +58,8 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
...
@@ -58,7 +58,8 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
if
(
avatarUrl
!=
""
)
{
if
(
avatarUrl
!=
""
)
{
retryIO
{
client
.
setAvatar
(
avatarUrl
)
}
retryIO
{
client
.
setAvatar
(
avatarUrl
)
}
}
}
val
user
=
retryIO
{
client
.
updateProfile
(
myselfId
,
email
,
name
,
username
)
}
val
user
=
retryIO
{
client
.
updateProfile
(
userId
=
myselfId
,
email
=
email
,
name
=
name
,
username
=
username
)
}
view
.
showProfileUpdateSuccessfullyMessage
()
view
.
showProfileUpdateSuccessfullyMessage
()
loadUserProfile
()
loadUserProfile
()
}
catch
(
exception
:
RocketChatException
)
{
}
catch
(
exception
:
RocketChatException
)
{
...
...
app/src/main/java/chat/rocket/android/profile/ui/ProfileFragment.kt
View file @
1a137f9a
...
@@ -4,6 +4,7 @@ import DrawableHelper
...
@@ -4,6 +4,7 @@ import DrawableHelper
import
android.os.Build
import
android.os.Build
import
android.os.Bundle
import
android.os.Bundle
import
android.support.v4.app.Fragment
import
android.support.v4.app.Fragment
import
android.support.v7.app.AppCompatActivity
import
android.support.v7.view.ActionMode
import
android.support.v7.view.ActionMode
import
android.view.*
import
android.view.*
import
chat.rocket.android.R
import
chat.rocket.android.R
...
@@ -14,7 +15,6 @@ import chat.rocket.android.util.extensions.*
...
@@ -14,7 +15,6 @@ import chat.rocket.android.util.extensions.*
import
dagger.android.support.AndroidSupportInjection
import
dagger.android.support.AndroidSupportInjection
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.disposables.CompositeDisposable
import
io.reactivex.rxkotlin.Observables
import
io.reactivex.rxkotlin.Observables
import
kotlinx.android.synthetic.main.app_bar.*
import
kotlinx.android.synthetic.main.avatar_profile.*
import
kotlinx.android.synthetic.main.avatar_profile.*
import
kotlinx.android.synthetic.main.fragment_profile.*
import
kotlinx.android.synthetic.main.fragment_profile.*
import
javax.inject.Inject
import
javax.inject.Inject
...
@@ -135,7 +135,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
...
@@ -135,7 +135,7 @@ class ProfileFragment : Fragment(), ProfileView, ActionMode.Callback {
}
}
private
fun
setupToolbar
()
{
private
fun
setupToolbar
()
{
(
activity
as
MainActivity
).
toolbar
.
title
=
getString
(
R
.
string
.
title_profile
)
(
activity
as
AppCompatActivity
?)
?.
supportActionBar
?
.
title
=
getString
(
R
.
string
.
title_profile
)
}
}
private
fun
tintEditTextDrawableStart
()
{
private
fun
tintEditTextDrawableStart
()
{
...
...
app/src/main/java/chat/rocket/android/push/
GcmListener
Service.kt
→
app/src/main/java/chat/rocket/android/push/
FirebaseMessaging
Service.kt
View file @
1a137f9a
package
chat.rocket.android.push
package
chat.rocket.android.push
import
android.os.Bundle
import
androidx.core.os.bundleOf
import
com.google.android.gms.gcm.GcmListenerService
import
com.google.firebase.messaging.FirebaseMessagingService
import
com.google.firebase.messaging.RemoteMessage
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjection
import
javax.inject.Inject
import
javax.inject.Inject
class
GcmListenerService
:
GcmListener
Service
()
{
class
FirebaseMessagingService
:
FirebaseMessaging
Service
()
{
@Inject
@Inject
lateinit
var
pushManager
:
PushManager
lateinit
var
pushManager
:
PushManager
...
@@ -15,9 +16,9 @@ class GcmListenerService : GcmListenerService() {
...
@@ -15,9 +16,9 @@ class GcmListenerService : GcmListenerService() {
AndroidInjection
.
inject
(
this
)
AndroidInjection
.
inject
(
this
)
}
}
override
fun
onMessageReceived
(
from
:
String
?,
data
:
Bundle
?
)
{
override
fun
onMessageReceived
(
message
:
RemoteMessage
)
{
data
?.
let
{
message
.
data
?.
let
{
pushManager
.
handle
(
data
)
pushManager
.
handle
(
bundleOf
(*(
it
.
map
{
Pair
(
it
.
key
,
it
.
value
)
}).
toTypedArray
())
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt
View file @
1a137f9a
package
chat.rocket.android.push
package
chat.rocket.android.push
import
chat.rocket.android.R
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.retryIO
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.internal.rest.registerPushToken
import
chat.rocket.core.internal.rest.registerPushToken
import
com.google.android.gms.gcm.GoogleCloudMessaging
import
com.google.firebase.iid.FirebaseInstanceId
import
com.google.android.gms.iid.InstanceID
import
com.google.firebase.iid.FirebaseInstanceIdService
import
com.google.firebase.iid.FirebaseInstanceIdService
import
dagger.android.AndroidInjection
import
dagger.android.AndroidInjection
import
kotlinx.coroutines.experimental.launch
import
kotlinx.coroutines.experimental.launch
...
@@ -31,21 +29,18 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
...
@@ -31,21 +29,18 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
}
}
override
fun
onTokenRefresh
()
{
override
fun
onTokenRefresh
()
{
//TODO: We need to use the Cordova Project gcm_sender_id since it's the one configured on RC
// default push gateway. We should register this project's own project sender id into it.
try
{
try
{
val
gcmToken
=
InstanceID
.
getInstance
(
this
)
val
fcmToken
=
FirebaseInstanceId
.
getInstance
().
token
.
getToken
(
getString
(
R
.
string
.
gcm_sender_id
),
GoogleCloudMessaging
.
INSTANCE_ID_SCOPE
,
null
)
val
currentServer
=
getCurrentServerInteractor
.
get
()
val
currentServer
=
getCurrentServerInteractor
.
get
()
val
client
=
currentServer
?.
let
{
factory
.
create
(
currentServer
)
}
val
client
=
currentServer
?.
let
{
factory
.
create
(
currentServer
)
}
g
cmToken
?.
let
{
f
cmToken
?.
let
{
localRepository
.
save
(
LocalRepository
.
KEY_PUSH_TOKEN
,
g
cmToken
)
localRepository
.
save
(
LocalRepository
.
KEY_PUSH_TOKEN
,
f
cmToken
)
client
?.
let
{
client
?.
let
{
launch
{
launch
{
try
{
try
{
Timber
.
d
(
"Registering push token: $
g
cmToken for ${client.url}"
)
Timber
.
d
(
"Registering push token: $
f
cmToken for ${client.url}"
)
retryIO
(
"register push token"
)
{
client
.
registerPushToken
(
gcmToken
)
}
retryIO
(
"register push token"
)
{
client
.
registerPushToken
(
fcmToken
)
}
}
catch
(
ex
:
RocketChatException
)
{
}
catch
(
ex
:
RocketChatException
)
{
Timber
.
e
(
ex
,
"Error registering push token"
)
Timber
.
e
(
ex
,
"Error registering push token"
)
}
}
...
@@ -53,7 +48,7 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
...
@@ -53,7 +48,7 @@ class FirebaseTokenService : FirebaseInstanceIdService() {
}
}
}
}
}
catch
(
ex
:
Exception
)
{
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error refreshing Firebase TOKEN"
)
Timber
.
e
(
ex
,
"Error refreshing Firebase TOKEN"
)
}
}
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
1a137f9a
...
@@ -41,14 +41,15 @@ import javax.inject.Inject
...
@@ -41,14 +41,15 @@ import javax.inject.Inject
* for old source code.
* for old source code.
*/
*/
class
PushManager
@Inject
constructor
(
class
PushManager
@Inject
constructor
(
private
val
groupedPushes
:
GroupedPush
,
private
val
groupedPushes
:
GroupedPush
,
private
val
manager
:
NotificationManager
,
private
val
manager
:
NotificationManager
,
private
val
moshi
:
Moshi
,
private
val
moshi
:
Moshi
,
private
val
getAccountInteractor
:
GetAccountInteractor
,
private
val
getAccountInteractor
:
GetAccountInteractor
,
private
val
getSettingsInteractor
:
GetSettingsInteractor
,
private
val
getSettingsInteractor
:
GetSettingsInteractor
,
private
val
context
:
Context
private
val
context
:
Context
)
{
)
{
private
val
randomizer
=
Random
()
private
val
random
=
Random
()
/**
/**
* Handles a receiving push by creating and displaying an appropriate notification based
* Handles a receiving push by creating and displaying an appropriate notification based
...
@@ -59,7 +60,7 @@ class PushManager @Inject constructor(
...
@@ -59,7 +60,7 @@ class PushManager @Inject constructor(
val
message
=
data
[
"message"
]
as
String
?
val
message
=
data
[
"message"
]
as
String
?
val
ejson
=
data
[
"ejson"
]
as
String
?
val
ejson
=
data
[
"ejson"
]
as
String
?
val
title
=
data
[
"title"
]
as
String
?
val
title
=
data
[
"title"
]
as
String
?
val
notId
=
data
[
"notId"
]
as
String
?
?:
random
izer
.
nextInt
().
toString
()
val
notId
=
data
[
"notId"
]
as
String
?
?:
random
.
nextInt
().
toString
()
val
image
=
data
[
"image"
]
as
String
?
val
image
=
data
[
"image"
]
as
String
?
val
style
=
data
[
"style"
]
as
String
?
val
style
=
data
[
"style"
]
as
String
?
val
summaryText
=
data
[
"summaryText"
]
as
String
?
val
summaryText
=
data
[
"summaryText"
]
as
String
?
...
@@ -67,9 +68,13 @@ class PushManager @Inject constructor(
...
@@ -67,9 +68,13 @@ class PushManager @Inject constructor(
try
{
try
{
val
adapter
=
moshi
.
adapter
<
PushInfo
>(
PushInfo
::
class
.
java
)
val
adapter
=
moshi
.
adapter
<
PushInfo
>(
PushInfo
::
class
.
java
)
val
info
=
adapter
.
fromJson
(
ejson
)
val
pushMessage
=
PushMessage
(
title
!!
,
message
!!
,
info
!!
,
image
,
count
,
notId
,
summaryText
,
style
)
val
pushMessage
=
if
(
ejson
!=
null
)
{
val
info
=
adapter
.
fromJson
(
ejson
)
PushMessage
(
title
!!
,
message
!!
,
info
!!
,
image
,
count
,
notId
,
summaryText
,
style
)
}
else
{
PushMessage
(
title
!!
,
message
!!
,
PushInfo
.
EMPTY
,
image
,
count
,
notId
,
summaryText
,
style
)
}
Timber
.
d
(
"Received push message: $pushMessage"
)
Timber
.
d
(
"Received push message: $pushMessage"
)
...
@@ -82,13 +87,17 @@ class PushManager @Inject constructor(
...
@@ -82,13 +87,17 @@ class PushManager @Inject constructor(
@SuppressLint
(
"NewApi"
)
@SuppressLint
(
"NewApi"
)
suspend
fun
showNotification
(
pushMessage
:
PushMessage
)
{
suspend
fun
showNotification
(
pushMessage
:
PushMessage
)
{
if
(!
hasAccount
(
pushMessage
.
info
.
host
))
{
val
notId
=
pushMessage
.
notificationId
.
toInt
()
Timber
.
d
(
"ignoring push message: $pushMessage"
)
val
host
=
pushMessage
.
info
.
host
if
(!
hasAccount
(
host
))
{
createSingleNotification
(
pushMessage
)
?.
let
{
NotificationManagerCompat
.
from
(
context
).
notify
(
notId
,
it
)
}
Timber
.
d
(
"ignoring push message: $pushMessage (maybe a test notification?)"
)
return
return
}
}
val
notId
=
pushMessage
.
notificationId
.
toInt
()
val
host
=
pushMessage
.
info
.
host
val
groupTuple
=
getGroupForHost
(
host
)
val
groupTuple
=
getGroupForHost
(
host
)
groupTuple
.
second
.
incrementAndGet
()
groupTuple
.
second
.
incrementAndGet
()
...
@@ -103,7 +112,7 @@ class PushManager @Inject constructor(
...
@@ -103,7 +112,7 @@ class PushManager @Inject constructor(
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
[
host
]
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
[
host
]
notification
?.
let
{
notification
?.
let
{
manager
.
notify
(
notId
,
notification
)
manager
.
notify
(
notId
,
it
)
}
}
pushMessageList
?.
let
{
pushMessageList
?.
let
{
...
@@ -137,7 +146,7 @@ class PushManager @Inject constructor(
...
@@ -137,7 +146,7 @@ class PushManager @Inject constructor(
val
host
=
info
.
host
val
host
=
info
.
host
val
builder
=
createBaseNotificationBuilder
(
pushMessage
,
grouped
=
true
)
val
builder
=
createBaseNotificationBuilder
(
pushMessage
,
grouped
=
true
)
.
setGroupSummary
(
true
)
.
setGroupSummary
(
true
)
if
(
style
==
null
||
style
==
"inbox"
)
{
if
(
style
==
null
||
style
==
"inbox"
)
{
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
[
host
]
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
[
host
]
...
@@ -150,7 +159,7 @@ class PushManager @Inject constructor(
...
@@ -150,7 +159,7 @@ class PushManager @Inject constructor(
builder
.
setContentTitle
(
getTitle
(
count
,
title
))
builder
.
setContentTitle
(
getTitle
(
count
,
title
))
val
inbox
=
NotificationCompat
.
InboxStyle
()
val
inbox
=
NotificationCompat
.
InboxStyle
()
.
setBigContentTitle
(
getTitle
(
count
,
title
))
.
setBigContentTitle
(
getTitle
(
count
,
title
))
for
(
push
in
pushMessageList
)
{
for
(
push
in
pushMessageList
)
{
inbox
.
addLine
(
push
.
message
)
inbox
.
addLine
(
push
.
message
)
...
@@ -160,8 +169,8 @@ class PushManager @Inject constructor(
...
@@ -160,8 +169,8 @@ class PushManager @Inject constructor(
}
}
}
else
{
}
else
{
val
bigText
=
NotificationCompat
.
BigTextStyle
()
val
bigText
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
message
.
fromHtml
())
.
bigText
(
message
.
fromHtml
())
.
setBigContentTitle
(
title
.
fromHtml
())
.
setBigContentTitle
(
title
.
fromHtml
())
builder
.
setStyle
(
bigText
)
builder
.
setStyle
(
bigText
)
}
}
...
@@ -177,12 +186,12 @@ class PushManager @Inject constructor(
...
@@ -177,12 +186,12 @@ class PushManager @Inject constructor(
val
host
=
info
.
host
val
host
=
info
.
host
val
builder
=
createBaseNotificationBuilder
(
pushMessage
)
val
builder
=
createBaseNotificationBuilder
(
pushMessage
)
.
setGroupSummary
(
false
)
.
setGroupSummary
(
false
)
if
(
style
==
null
||
"inbox"
==
style
)
{
if
(
style
==
null
||
"inbox"
==
style
)
{
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
.
get
(
host
)
val
pushMessageList
=
groupedPushes
.
hostToPushMessageList
.
get
(
host
)
pushMessageList
?.
let
{
if
(
pushMessageList
!=
null
)
{
val
userMessages
=
pushMessageList
.
filter
{
val
userMessages
=
pushMessageList
.
filter
{
it
.
notificationId
==
pushMessage
.
notificationId
it
.
notificationId
==
pushMessage
.
notificationId
}
}
...
@@ -203,18 +212,23 @@ class PushManager @Inject constructor(
...
@@ -203,18 +212,23 @@ class PushManager @Inject constructor(
builder
.
setStyle
(
inbox
)
builder
.
setStyle
(
inbox
)
}
else
{
}
else
{
val
bigTextStyle
=
NotificationCompat
.
BigTextStyle
()
val
bigTextStyle
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
message
.
fromHtml
())
.
bigText
(
message
.
fromHtml
())
builder
.
setStyle
(
bigTextStyle
)
builder
.
setStyle
(
bigTextStyle
)
}
}
}
else
{
// We don't know which kind of push is this - maybe a test push, so just show it
val
bigTextStyle
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
message
.
fromHtml
())
builder
.
setStyle
(
bigTextStyle
)
return
builder
.
build
()
}
}
}
else
{
}
else
{
val
bigTextStyle
=
NotificationCompat
.
BigTextStyle
()
val
bigTextStyle
=
NotificationCompat
.
BigTextStyle
()
.
bigText
(
message
.
fromHtml
())
.
bigText
(
message
.
fromHtml
())
builder
.
setStyle
(
bigTextStyle
)
builder
.
setStyle
(
bigTextStyle
)
}
}
return
builder
.
addReplyAction
(
pushMessage
)
return
builder
.
addReplyAction
(
pushMessage
).
build
()
.
build
()
}
}
}
}
...
@@ -227,21 +241,35 @@ class PushManager @Inject constructor(
...
@@ -227,21 +241,35 @@ class PushManager @Inject constructor(
val
deleteIntent
=
getDismissIntent
(
context
,
pushMessage
)
val
deleteIntent
=
getDismissIntent
(
context
,
pushMessage
)
val
builder
=
NotificationCompat
.
Builder
(
context
,
host
)
val
builder
=
NotificationCompat
.
Builder
(
context
,
host
)
.
setWhen
(
info
.
createdAt
)
.
setWhen
(
info
.
createdAt
)
.
setContentTitle
(
title
.
fromHtml
())
.
setContentTitle
(
title
.
fromHtml
())
.
setContentText
(
message
.
fromHtml
())
.
setContentText
(
message
.
fromHtml
())
.
setGroup
(
host
)
.
setGroup
(
host
)
.
setDeleteIntent
(
deleteIntent
)
.
setDeleteIntent
(
deleteIntent
)
.
setContentIntent
(
contentIntent
)
.
setContentIntent
(
contentIntent
)
.
setMessageNotification
()
.
setMessageNotification
()
if
(
host
.
isEmpty
())
{
builder
.
setContentIntent
(
deleteIntent
)
}
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
O
)
{
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
O
)
{
val
channel
=
NotificationChannel
(
host
,
host
,
NotificationManager
.
IMPORTANCE_HIGH
)
val
channelId
:
String
val
channelName
:
String
if
(
host
.
isEmpty
())
{
channelName
=
"Test Notification"
channelId
=
"test-channel"
}
else
{
channelName
=
host
channelId
=
host
}
val
channel
=
NotificationChannel
(
channelId
,
channelName
,
NotificationManager
.
IMPORTANCE_HIGH
)
channel
.
lockscreenVisibility
=
Notification
.
VISIBILITY_PUBLIC
channel
.
lockscreenVisibility
=
Notification
.
VISIBILITY_PUBLIC
channel
.
enableLights
(
false
)
channel
.
enableLights
(
false
)
channel
.
enableVibration
(
true
)
channel
.
enableVibration
(
true
)
channel
.
setShowBadge
(
true
)
channel
.
setShowBadge
(
true
)
manager
.
createNotificationChannel
(
channel
)
manager
.
createNotificationChannel
(
channel
)
builder
.
setChannelId
(
channelId
)
}
}
//TODO: Get Site_Name PublicSetting from cache
//TODO: Get Site_Name PublicSetting from cache
...
@@ -265,18 +293,18 @@ class PushManager @Inject constructor(
...
@@ -265,18 +293,18 @@ class PushManager @Inject constructor(
private
fun
getDismissIntent
(
context
:
Context
,
pushMessage
:
PushMessage
):
PendingIntent
{
private
fun
getDismissIntent
(
context
:
Context
,
pushMessage
:
PushMessage
):
PendingIntent
{
val
deleteIntent
=
Intent
(
context
,
DeleteReceiver
::
class
.
java
)
val
deleteIntent
=
Intent
(
context
,
DeleteReceiver
::
class
.
java
)
.
putExtra
(
EXTRA_NOT_ID
,
pushMessage
.
notificationId
.
toInt
())
.
putExtra
(
EXTRA_NOT_ID
,
pushMessage
.
notificationId
.
toInt
())
.
putExtra
(
EXTRA_HOSTNAME
,
pushMessage
.
info
.
host
)
.
putExtra
(
EXTRA_HOSTNAME
,
pushMessage
.
info
.
host
)
return
PendingIntent
.
getBroadcast
(
context
,
pushMessage
.
notificationId
.
toInt
(),
deleteIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
)
return
PendingIntent
.
getBroadcast
(
context
,
pushMessage
.
notificationId
.
toInt
(),
deleteIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
)
}
}
private
fun
getContentIntent
(
context
:
Context
,
notificationId
:
Int
,
pushMessage
:
PushMessage
,
grouped
:
Boolean
=
false
):
PendingIntent
{
private
fun
getContentIntent
(
context
:
Context
,
notificationId
:
Int
,
pushMessage
:
PushMessage
,
grouped
:
Boolean
=
false
):
PendingIntent
{
val
notificationIntent
=
context
.
changeServerIntent
(
pushMessage
.
info
.
host
)
val
notificationIntent
=
context
.
changeServerIntent
(
pushMessage
.
info
.
host
,
chatRoomId
=
pushMessage
.
info
.
roomId
)
// TODO - add support to go directly to the chatroom
// TODO - add support to go directly to the chatroom
/*if (!grouped) {
/*if (!grouped) {
notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId)
notificationIntent.putExtra(EXTRA_ROOM_ID, pushMessage.info.roomId)
}*/
}*/
return
PendingIntent
.
getActivity
(
context
,
random
izer
.
nextInt
(),
notificationIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
)
return
PendingIntent
.
getActivity
(
context
,
random
.
nextInt
(),
notificationIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
)
}
}
// CharSequence extensions
// CharSequence extensions
...
@@ -288,13 +316,13 @@ class PushManager @Inject constructor(
...
@@ -288,13 +316,13 @@ class PushManager @Inject constructor(
private
fun
NotificationCompat
.
Builder
.
addReplyAction
(
pushMessage
:
PushMessage
):
NotificationCompat
.
Builder
{
private
fun
NotificationCompat
.
Builder
.
addReplyAction
(
pushMessage
:
PushMessage
):
NotificationCompat
.
Builder
{
val
replyTextHint
=
context
.
getText
(
R
.
string
.
notif_action_reply_hint
)
val
replyTextHint
=
context
.
getText
(
R
.
string
.
notif_action_reply_hint
)
val
replyRemoteInput
=
RemoteInput
.
Builder
(
REMOTE_INPUT_REPLY
)
val
replyRemoteInput
=
RemoteInput
.
Builder
(
REMOTE_INPUT_REPLY
)
.
setLabel
(
replyTextHint
)
.
setLabel
(
replyTextHint
)
.
build
()
.
build
()
val
pendingIntent
=
getReplyPendingIntent
(
pushMessage
)
val
pendingIntent
=
getReplyPendingIntent
(
pushMessage
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_action_message_reply_24dp
,
replyTextHint
,
pendingIntent
)
val
replyAction
=
NotificationCompat
.
Action
.
Builder
(
R
.
drawable
.
ic_action_message_reply_24dp
,
replyTextHint
,
pendingIntent
)
.
addRemoteInput
(
replyRemoteInput
)
.
addRemoteInput
(
replyRemoteInput
)
.
setAllowGeneratedReplies
(
true
)
.
setAllowGeneratedReplies
(
true
)
.
build
()
.
build
()
this
.
addAction
(
replyAction
)
this
.
addAction
(
replyAction
)
return
this
return
this
...
@@ -317,17 +345,17 @@ class PushManager @Inject constructor(
...
@@ -317,17 +345,17 @@ class PushManager @Inject constructor(
val
replyIntent
=
getReplyIntent
(
pushMessage
)
val
replyIntent
=
getReplyIntent
(
pushMessage
)
return
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
)
{
return
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
N
)
{
PendingIntent
.
getBroadcast
(
PendingIntent
.
getBroadcast
(
context
,
context
,
randomizer
.
nextInt
(),
random
.
nextInt
(),
replyIntent
,
replyIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
PendingIntent
.
FLAG_UPDATE_CURRENT
)
)
}
else
{
}
else
{
PendingIntent
.
getActivity
(
PendingIntent
.
getActivity
(
context
,
context
,
randomizer
.
nextInt
(),
random
.
nextInt
(),
replyIntent
,
replyIntent
,
PendingIntent
.
FLAG_UPDATE_CURRENT
PendingIntent
.
FLAG_UPDATE_CURRENT
)
)
}
}
}
}
...
@@ -336,7 +364,7 @@ class PushManager @Inject constructor(
...
@@ -336,7 +364,7 @@ class PushManager @Inject constructor(
val
alarmSound
=
RingtoneManager
.
getDefaultUri
(
RingtoneManager
.
TYPE_NOTIFICATION
)
val
alarmSound
=
RingtoneManager
.
getDefaultUri
(
RingtoneManager
.
TYPE_NOTIFICATION
)
val
res
=
context
.
resources
val
res
=
context
.
resources
val
smallIcon
=
res
.
getIdentifier
(
val
smallIcon
=
res
.
getIdentifier
(
"rocket_chat_notification"
,
"drawable"
,
context
.
packageName
)
"rocket_chat_notification"
,
"drawable"
,
context
.
packageName
)
with
(
this
,
{
with
(
this
,
{
setAutoCancel
(
true
)
setAutoCancel
(
true
)
setShowWhen
(
true
)
setShowWhen
(
true
)
...
@@ -350,24 +378,25 @@ class PushManager @Inject constructor(
...
@@ -350,24 +378,25 @@ class PushManager @Inject constructor(
}
}
data class
PushMessage
(
data class
PushMessage
(
val
title
:
String
,
val
title
:
String
,
val
message
:
String
,
val
message
:
String
,
val
info
:
PushInfo
,
val
info
:
PushInfo
,
val
image
:
String
?
=
null
,
val
image
:
String
?
=
null
,
val
count
:
String
?
=
null
,
val
count
:
String
?
=
null
,
val
notificationId
:
String
,
val
notificationId
:
String
,
val
summaryText
:
String
?
=
null
,
val
summaryText
:
String
?
=
null
,
val
style
:
String
?
=
null
val
style
:
String
?
=
null
)
:
Parcelable
{
)
:
Parcelable
{
constructor
(
parcel
:
Parcel
)
:
this
(
constructor
(
parcel
:
Parcel
)
:
this
(
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readParcelable
(
PushMessage
::
class
.
java
.
classLoader
),
parcel
.
readParcelable
(
PushMessage
::
class
.
java
.
classLoader
),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
())
parcel
.
readString
())
override
fun
writeToParcel
(
parcel
:
Parcel
,
flags
:
Int
)
{
override
fun
writeToParcel
(
parcel
:
Parcel
,
flags
:
Int
)
{
parcel
.
writeString
(
title
)
parcel
.
writeString
(
title
)
...
@@ -397,11 +426,11 @@ data class PushMessage(
...
@@ -397,11 +426,11 @@ data class PushMessage(
@JsonSerializable
@JsonSerializable
data class
PushInfo
@KotshiConstructor
constructor
(
data class
PushInfo
@KotshiConstructor
constructor
(
@Json
(
name
=
"host"
)
val
hostname
:
String
,
@Json
(
name
=
"host"
)
val
hostname
:
String
,
@Json
(
name
=
"rid"
)
val
roomId
:
String
,
@Json
(
name
=
"rid"
)
val
roomId
:
String
,
val
type
:
RoomType
,
val
type
:
RoomType
,
val
name
:
String
?,
val
name
:
String
?,
val
sender
:
PushSender
?
val
sender
:
PushSender
?
)
:
Parcelable
{
)
:
Parcelable
{
val
createdAt
:
Long
val
createdAt
:
Long
get
()
=
System
.
currentTimeMillis
()
get
()
=
System
.
currentTimeMillis
()
...
@@ -410,11 +439,11 @@ data class PushInfo @KotshiConstructor constructor(
...
@@ -410,11 +439,11 @@ data class PushInfo @KotshiConstructor constructor(
}
}
constructor
(
parcel
:
Parcel
)
:
this
(
constructor
(
parcel
:
Parcel
)
:
this
(
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
roomTypeOf
(
parcel
.
readString
()),
roomTypeOf
(
parcel
.
readString
()),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readParcelable
(
PushInfo
::
class
.
java
.
classLoader
))
parcel
.
readParcelable
(
PushInfo
::
class
.
java
.
classLoader
))
private
fun
sanitizeUrl
(
baseUrl
:
String
):
String
{
private
fun
sanitizeUrl
(
baseUrl
:
String
):
String
{
var
url
=
baseUrl
.
trim
()
var
url
=
baseUrl
.
trim
()
...
@@ -438,6 +467,9 @@ data class PushInfo @KotshiConstructor constructor(
...
@@ -438,6 +467,9 @@ data class PushInfo @KotshiConstructor constructor(
}
}
companion
object
CREATOR
:
Parcelable
.
Creator
<
PushInfo
>
{
companion
object
CREATOR
:
Parcelable
.
Creator
<
PushInfo
>
{
val
EMPTY
=
PushInfo
(
hostname
=
""
,
roomId
=
""
,
type
=
RoomType
.
CHANNEL
,
name
=
""
,
sender
=
null
)
override
fun
createFromParcel
(
parcel
:
Parcel
):
PushInfo
{
override
fun
createFromParcel
(
parcel
:
Parcel
):
PushInfo
{
return
PushInfo
(
parcel
)
return
PushInfo
(
parcel
)
}
}
...
@@ -450,14 +482,14 @@ data class PushInfo @KotshiConstructor constructor(
...
@@ -450,14 +482,14 @@ data class PushInfo @KotshiConstructor constructor(
@JsonSerializable
@JsonSerializable
data class
PushSender
@KotshiConstructor
constructor
(
data class
PushSender
@KotshiConstructor
constructor
(
@Json
(
name
=
"_id"
)
val
id
:
String
,
@Json
(
name
=
"_id"
)
val
id
:
String
,
val
username
:
String
?,
val
username
:
String
?,
val
name
:
String
?
val
name
:
String
?
)
:
Parcelable
{
)
:
Parcelable
{
constructor
(
parcel
:
Parcel
)
:
this
(
constructor
(
parcel
:
Parcel
)
:
this
(
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
(),
parcel
.
readString
())
parcel
.
readString
())
override
fun
writeToParcel
(
parcel
:
Parcel
,
flags
:
Int
)
{
override
fun
writeToParcel
(
parcel
:
Parcel
,
flags
:
Int
)
{
parcel
.
writeString
(
id
)
parcel
.
writeString
(
id
)
...
...
app/src/main/java/chat/rocket/android/push/di/
GcmListener
ServiceProvider.kt
→
app/src/main/java/chat/rocket/android/push/di/
FirebaseMessaging
ServiceProvider.kt
View file @
1a137f9a
package
chat.rocket.android.push.di
package
chat.rocket.android.push.di
import
chat.rocket.android.dagger.module.AppModule
import
chat.rocket.android.dagger.module.AppModule
import
chat.rocket.android.push.
GcmListener
Service
import
chat.rocket.android.push.
FirebaseMessaging
Service
import
dagger.Module
import
dagger.Module
import
dagger.android.ContributesAndroidInjector
import
dagger.android.ContributesAndroidInjector
@Module
abstract
class
GcmListener
ServiceProvider
{
@Module
abstract
class
FirebaseMessaging
ServiceProvider
{
@ContributesAndroidInjector
(
modules
=
[
AppModule
::
class
])
@ContributesAndroidInjector
(
modules
=
[
AppModule
::
class
])
abstract
fun
provide
GcmListenerService
():
GcmListener
Service
abstract
fun
provide
FirebaseMessagingService
():
FirebaseMessaging
Service
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/server/infraestructure/ConnectionManager.kt
View file @
1a137f9a
...
@@ -3,16 +3,16 @@ package chat.rocket.android.server.infraestructure
...
@@ -3,16 +3,16 @@ package chat.rocket.android.server.infraestructure
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.User
import
chat.rocket.common.model.User
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.realtime.subscribeSubscriptions
import
chat.rocket.core.internal.realtime.subscribeRooms
import
chat.rocket.core.internal.realtime.subscribeUserData
import
chat.rocket.core.internal.realtime.subscribeActiveUsers
import
chat.rocket.core.internal.realtime.subscribeRoomMessages
import
chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.realtime.socket.connect
import
chat.rocket.core.internal.realtime.socket.connect
import
chat.rocket.core.internal.realtime.socket.disconnect
import
chat.rocket.core.internal.realtime.socket.disconnect
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.State
import
chat.rocket.core.internal.realtime.socket.model.StreamMessage
import
chat.rocket.core.internal.realtime.socket.model.StreamMessage
import
chat.rocket.core.internal.realtime.subscribeActiveUsers
import
chat.rocket.core.internal.realtime.subscribeRoomMessages
import
chat.rocket.core.internal.realtime.subscribeRooms
import
chat.rocket.core.internal.realtime.subscribeSubscriptions
import
chat.rocket.core.internal.realtime.subscribeUserData
import
chat.rocket.core.internal.realtime.unsubscribe
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.internal.rest.chatRooms
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.Myself
import
chat.rocket.core.model.Myself
...
...
app/src/main/java/chat/rocket/android/server/presentation/ChangeServerNavigator.kt
View file @
1a137f9a
...
@@ -4,6 +4,7 @@ import android.content.Intent
...
@@ -4,6 +4,7 @@ import android.content.Intent
import
chat.rocket.android.authentication.ui.newServerIntent
import
chat.rocket.android.authentication.ui.newServerIntent
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.server.ui.ChangeServerActivity
import
chat.rocket.android.server.ui.ChangeServerActivity
import
chat.rocket.android.server.ui.INTENT_CHAT_ROOM_ID
class
ChangeServerNavigator
(
internal
val
activity
:
ChangeServerActivity
)
{
class
ChangeServerNavigator
(
internal
val
activity
:
ChangeServerActivity
)
{
fun
toServerScreen
()
{
fun
toServerScreen
()
{
...
@@ -11,8 +12,10 @@ class ChangeServerNavigator (internal val activity: ChangeServerActivity) {
...
@@ -11,8 +12,10 @@ class ChangeServerNavigator (internal val activity: ChangeServerActivity) {
activity
.
finish
()
activity
.
finish
()
}
}
fun
toChatRooms
()
{
fun
toChatRooms
(
chatRoomId
:
String
?
=
null
)
{
activity
.
startActivity
(
Intent
(
activity
,
MainActivity
::
class
.
java
))
activity
.
startActivity
(
Intent
(
activity
,
MainActivity
::
class
.
java
).
also
{
it
.
putExtra
(
INTENT_CHAT_ROOM_ID
,
chatRoomId
)
})
activity
.
finish
()
activity
.
finish
()
}
}
...
...
app/src/main/java/chat/rocket/android/server/presentation/ChangeServerPresenter.kt
View file @
1a137f9a
...
@@ -21,7 +21,7 @@ class ChangeServerPresenter @Inject constructor(
...
@@ -21,7 +21,7 @@ class ChangeServerPresenter @Inject constructor(
private
val
localRepository
:
LocalRepository
,
private
val
localRepository
:
LocalRepository
,
private
val
connectionManager
:
ConnectionManagerFactory
private
val
connectionManager
:
ConnectionManagerFactory
)
{
)
{
fun
loadServer
(
newUrl
:
String
?)
{
fun
loadServer
(
newUrl
:
String
?
,
chatRoomId
:
String
?
=
null
)
{
launchUI
(
strategy
)
{
launchUI
(
strategy
)
{
view
.
showProgress
()
view
.
showProgress
()
var
url
=
newUrl
var
url
=
newUrl
...
@@ -56,7 +56,7 @@ class ChangeServerPresenter @Inject constructor(
...
@@ -56,7 +56,7 @@ class ChangeServerPresenter @Inject constructor(
saveCurrentServerInteractor
.
save
(
serverUrl
)
saveCurrentServerInteractor
.
save
(
serverUrl
)
view
.
hideProgress
()
view
.
hideProgress
()
navigator
.
toChatRooms
()
navigator
.
toChatRooms
(
chatRoomId
)
}.
ifNull
{
}.
ifNull
{
view
.
hideProgress
()
view
.
hideProgress
()
navigator
.
toServerScreen
()
navigator
.
toServerScreen
()
...
...
app/src/main/java/chat/rocket/android/server/ui/ChangeServerActivity.kt
View file @
1a137f9a
...
@@ -21,7 +21,8 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
...
@@ -21,7 +21,8 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
super
.
onCreate
(
savedInstanceState
)
super
.
onCreate
(
savedInstanceState
)
val
serverUrl
:
String
?
=
intent
.
getStringExtra
(
INTENT_SERVER_URL
)
val
serverUrl
:
String
?
=
intent
.
getStringExtra
(
INTENT_SERVER_URL
)
presenter
.
loadServer
(
serverUrl
)
val
chatRoomId
:
String
?
=
intent
.
getStringExtra
(
INTENT_CHAT_ROOM_ID
)
presenter
.
loadServer
(
serverUrl
,
chatRoomId
)
}
}
override
fun
showInvalidCredentials
()
{
override
fun
showInvalidCredentials
()
{
...
@@ -40,11 +41,13 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
...
@@ -40,11 +41,13 @@ class ChangeServerActivity : AppCompatActivity(), ChangeServerView {
private
const
val
INTENT_SERVER_URL
=
"INTENT_SERVER_URL"
private
const
val
INTENT_SERVER_URL
=
"INTENT_SERVER_URL"
private
const
val
INTENT_CHAT_ROOM_NAME
=
"INTENT_CHAT_ROOM_NAME"
private
const
val
INTENT_CHAT_ROOM_NAME
=
"INTENT_CHAT_ROOM_NAME"
private
const
val
INTENT_CHAT_ROOM_TYPE
=
"INTENT_CHAT_ROOM_TYPE"
private
const
val
INTENT_CHAT_ROOM_TYPE
=
"INTENT_CHAT_ROOM_TYPE"
const
val
INTENT_CHAT_ROOM_ID
=
"INTENT_CHAT_ROOM_ID"
fun
Context
.
changeServerIntent
(
serverUrl
:
String
?
=
null
):
Intent
{
fun
Context
.
changeServerIntent
(
serverUrl
:
String
?
=
null
,
chatRoomId
:
String
?
=
""
):
Intent
{
return
Intent
(
this
,
ChangeServerActivity
::
class
.
java
).
apply
{
return
Intent
(
this
,
ChangeServerActivity
::
class
.
java
).
apply
{
serverUrl
?.
let
{
url
->
serverUrl
?.
let
{
url
->
putExtra
(
INTENT_SERVER_URL
,
url
)
putExtra
(
INTENT_SERVER_URL
,
url
)
putExtra
(
INTENT_CHAT_ROOM_ID
,
chatRoomId
)
}
}
flags
=
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
or
Intent
.
FLAG_ACTIVITY_CLEAR_TASK
flags
=
Intent
.
FLAG_ACTIVITY_CLEAR_TOP
or
Intent
.
FLAG_ACTIVITY_CLEAR_TASK
}
}
...
...
app/src/main/java/chat/rocket/android/settings/ui/SettingsFragment.kt
View file @
1a137f9a
...
@@ -9,12 +9,10 @@ import android.view.View
...
@@ -9,12 +9,10 @@ import android.view.View
import
android.view.ViewGroup
import
android.view.ViewGroup
import
android.widget.AdapterView
import
android.widget.AdapterView
import
chat.rocket.android.R
import
chat.rocket.android.R
import
chat.rocket.android.main.ui.MainActivity
import
chat.rocket.android.settings.about.ui.AboutActivity
import
chat.rocket.android.settings.about.ui.AboutActivity
import
chat.rocket.android.settings.password.ui.PasswordActivity
import
chat.rocket.android.settings.password.ui.PasswordActivity
import
chat.rocket.android.settings.presentation.SettingsView
import
chat.rocket.android.settings.presentation.SettingsView
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.inflate
import
kotlinx.android.synthetic.main.app_bar.*
import
kotlinx.android.synthetic.main.fragment_settings.*
import
kotlinx.android.synthetic.main.fragment_settings.*
import
kotlin.reflect.KClass
import
kotlin.reflect.KClass
...
@@ -47,7 +45,7 @@ class SettingsFragment: Fragment(), SettingsView, AdapterView.OnItemClickListene
...
@@ -47,7 +45,7 @@ class SettingsFragment: Fragment(), SettingsView, AdapterView.OnItemClickListene
}
}
private
fun
setupToolbar
()
{
private
fun
setupToolbar
()
{
(
activity
as
MainActivity
).
toolbar
.
title
=
getString
(
R
.
string
.
title_settings
)
(
activity
as
AppCompatActivity
?)
?.
supportActionBar
?
.
title
=
getString
(
R
.
string
.
title_settings
)
}
}
private
fun
startNewActivity
(
classType
:
KClass
<
out
AppCompatActivity
>)
{
private
fun
startNewActivity
(
classType
:
KClass
<
out
AppCompatActivity
>)
{
...
...
app/src/main/res/layout/fragment_authentication_log_in.xml
View file @
1a137f9a
...
@@ -32,6 +32,18 @@
...
@@ -32,6 +32,18 @@
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_headline"
/>
app:layout_constraintTop_toBottomOf=
"@+id/text_headline"
/>
<ImageView
android:id=
"@+id/image_key"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"10dp"
android:src=
"@drawable/ic_vpn_key_black_24dp"
android:tint=
"@color/colorDrawableTintGrey"
android:visibility=
"gone"
app:layout_constraintBottom_toBottomOf=
"@+id/text_username_or_email"
app:layout_constraintEnd_toEndOf=
"@+id/text_username_or_email"
app:layout_constraintTop_toTopOf=
"@+id/text_username_or_email"
/>
<EditText
<EditText
android:id=
"@+id/text_password"
android:id=
"@+id/text_password"
style=
"@style/Authentication.EditText"
style=
"@style/Authentication.EditText"
...
...
app/src/main/res/layout/fragment_files.xml
View file @
1a137f9a
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:tools=
"http://schemas.android.com/tools"
android:id=
"@+id/root_layout"
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
tools:context=
".files.ui.FilesFragment"
>
tools:context=
".files.ui.FilesFragment"
>
...
...
app/src/main/res/values/api_keys.xml
deleted
100644 → 0
View file @
a1694c8e
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- This is the Cordova GCM sender id-->
<string
name=
"gcm_sender_id"
translatable=
"false"
>
673693445664
</string>
</resources>
\ No newline at end of file
debug.keystore
0 → 100644
View file @
1a137f9a
File added
dependencies.gradle
View file @
1a137f9a
...
@@ -14,7 +14,8 @@ ext {
...
@@ -14,7 +14,8 @@ ext {
androidKtx
:
'0.3'
,
androidKtx
:
'0.3'
,
dagger
:
'2.14.1'
,
dagger
:
'2.14.1'
,
exoPlayer
:
'2.6.0'
,
exoPlayer
:
'2.6.0'
,
playServices
:
'11.8.0'
,
playServices
:
'15.0.0'
,
firebase
:
'15.0.0'
,
room
:
'1.0.0'
,
room
:
'1.0.0'
,
lifecycle
:
'1.1.1'
,
lifecycle
:
'1.1.1'
,
rxKotlin
:
'2.2.0'
,
rxKotlin
:
'2.2.0'
,
...
@@ -63,7 +64,7 @@ ext {
...
@@ -63,7 +64,7 @@ ext {
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerSupport
:
"com.google.dagger:dagger-android-support:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
daggerProcessor
:
"com.google.dagger:dagger-compiler:${versions.dagger}"
,
daggerAndroidApt
:
"com.google.dagger:dagger-android-processor:${versions.dagger}"
,
daggerAndroidApt
:
"com.google.dagger:dagger-android-processor:${versions.dagger}"
,
playServicesGcm
:
"com.google.android.gms:play-services-gcm:${versions.playServices
}"
,
fcm
:
"com.google.firebase:firebase-messaging:${versions.firebase
}"
,
playServicesAuth
:
"com.google.android.gms:play-services-auth:${versions.playServices}"
,
playServicesAuth
:
"com.google.android.gms:play-services-auth:${versions.playServices}"
,
exoPlayer
:
"com.google.android.exoplayer:exoplayer:${versions.exoPlayer}"
,
exoPlayer
:
"com.google.android.exoplayer:exoplayer:${versions.exoPlayer}"
,
...
@@ -102,19 +103,19 @@ ext {
...
@@ -102,19 +103,19 @@ ext {
aVLoadingIndicatorView:
"com.wang.avi:library:${versions.aVLoadingIndicatorView}"
,
aVLoadingIndicatorView:
"com.wang.avi:library:${versions.aVLoadingIndicatorView}"
,
// For testing
junit
:
"junit:junit:$versions.junit"
,
espressoCore
:
"com.android.support.test.espresso:espresso-core:${versions.espresso}"
,
espressoIntents
:
"com.android.support.test.espresso:espresso-intents:${versions.espresso}"
,
roomTest
:
"android.arch.persistence.room:testing:${versions.room}"
,
truth
:
"com.google.truth:truth:$versions.truth"
,
//For the wear app
//For the wear app
wearable
:
"com.google.android.support:wearable:${versions.wear}"
,
wearable
:
"com.google.android.support:wearable:${versions.wear}"
,
playServicesWearable
:
"com.google.android.gms:play-services-wearable:${versions.playServicesWearable}"
,
playServicesWearable
:
"com.google.android.gms:play-services-wearable:${versions.playServicesWearable}"
,
percentLayout
:
"com.android.support:percent:${versions.supportWearable}"
,
percentLayout
:
"com.android.support:percent:${versions.supportWearable}"
,
supportWearable
:
"com.android.support:support-v4:${versions.supportWearable}"
,
supportWearable
:
"com.android.support:support-v4:${versions.supportWearable}"
,
wearableRecyclerView
:
"com.android.support:recyclerview-v7:${versions.supportWearable}"
,
wearableRecyclerView
:
"com.android.support:recyclerview-v7:${versions.supportWearable}"
,
wearSupport
:
"com.android.support:wear:${versions.supportWearable}"
wearSupport
:
"com.android.support:wear:${versions.supportWearable}"
,
// For testing
junit
:
"junit:junit:$versions.junit"
,
espressoCore
:
"com.android.support.test.espresso:espresso-core:${versions.espresso}"
,
espressoIntents
:
"com.android.support.test.espresso:espresso-intents:${versions.espresso}"
,
roomTest
:
"android.arch.persistence.room:testing:${versions.room}"
,
truth
:
"com.google.truth:truth:$versions.truth"
]
]
}
}
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