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
7ffa0e14
Unverified
Commit
7ffa0e14
authored
Apr 05, 2018
by
Filipe de Lima Brito
Committed by
GitHub
Apr 05, 2018
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop-2.x' into ver-name-number
parents
958b7c35
c0a12e1e
Changes
48
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
48 changed files
with
680 additions
and
359 deletions
+680
-359
build.gradle
app/build.gradle
+2
-2
DrawableHelper.kt
app/src/main/java/chat/rocket/android/app/DrawableHelper.kt
+28
-0
RocketChatApplication.kt
...ain/java/chat/rocket/android/app/RocketChatApplication.kt
+5
-4
LoginPresenter.kt
...droid/authentication/login/presentation/LoginPresenter.kt
+29
-28
RegisterUsernamePresenter.kt
...egisterusername/presentation/RegisterUsernamePresenter.kt
+9
-5
ServerPresenter.kt
...oid/authentication/server/presentation/ServerPresenter.kt
+2
-2
SignupPresenter.kt
...oid/authentication/signup/presentation/SignupPresenter.kt
+13
-11
TwoFAPresenter.kt
...d/authentication/twofactor/presentation/TwoFAPresenter.kt
+9
-6
ChatRoomPresenter.kt
...rocket/android/chatroom/presentation/ChatRoomPresenter.kt
+80
-43
ChatRoomView.kt
...chat/rocket/android/chatroom/presentation/ChatRoomView.kt
+3
-1
ChatRoomFragment.kt
.../java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
+56
-50
ViewModelMapper.kt
...chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
+2
-2
ChatRoomsPresenter.kt
...cket/android/chatrooms/presentation/ChatRoomsPresenter.kt
+5
-2
ChatRoomsAdapter.kt
...java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
+2
-2
ChatRoomsFragment.kt
...ava/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
+6
-5
OauthHelper.kt
app/src/main/java/chat/rocket/android/helper/OauthHelper.kt
+5
-3
UrlHelper.kt
app/src/main/java/chat/rocket/android/helper/UrlHelper.kt
+0
-74
AccountViewHolder.kt
...ava/chat/rocket/android/main/adapter/AccountViewHolder.kt
+1
-0
AccountsAdapter.kt
.../java/chat/rocket/android/main/adapter/AccountsAdapter.kt
+26
-12
StatusViewHolder.kt
...java/chat/rocket/android/main/adapter/StatusViewHolder.kt
+18
-0
MainPresenter.kt
...va/chat/rocket/android/main/presentation/MainPresenter.kt
+27
-5
MainView.kt
...in/java/chat/rocket/android/main/presentation/MainView.kt
+16
-0
MainActivity.kt
...src/main/java/chat/rocket/android/main/ui/MainActivity.kt
+27
-10
NavHeaderViewModelMapper.kt
...rocket/android/main/viewmodel/NavHeaderViewModelMapper.kt
+4
-3
MembersPresenter.kt
...t/rocket/android/members/presentation/MembersPresenter.kt
+4
-1
MemberViewModel.kt
.../chat/rocket/android/members/viewmodel/MemberViewModel.kt
+2
-2
ProfilePresenter.kt
...t/rocket/android/profile/presentation/ProfilePresenter.kt
+6
-5
FirebaseTokenService.kt
...ain/java/chat/rocket/android/push/FirebaseTokenService.kt
+0
-1
CheckServerPresenter.kt
...ocket/android/server/presentation/CheckServerPresenter.kt
+18
-13
PasswordPresenter.kt
...droid/settings/password/presentation/PasswordPresenter.kt
+5
-1
IO.kt
app/src/main/java/chat/rocket/android/util/IO.kt
+29
-0
RocketChatClient.kt
...a/chat/rocket/android/util/extensions/RocketChatClient.kt
+4
-1
String.kt
...c/main/java/chat/rocket/android/util/extensions/String.kt
+25
-0
Uri.kt
app/src/main/java/chat/rocket/android/util/extensions/Uri.kt
+4
-4
black_gradient.xml
app/src/main/res/drawable/black_gradient.xml
+2
-3
ic_status_away_24dp.xml
app/src/main/res/drawable/ic_status_away_24dp.xml
+19
-0
ic_status_busy_24dp.xml
app/src/main/res/drawable/ic_status_busy_24dp.xml
+19
-0
ic_status_invisible_24dp.xml
app/src/main/res/drawable/ic_status_invisible_24dp.xml
+19
-0
ic_status_online_24dp.xml
app/src/main/res/drawable/ic_status_online_24dp.xml
+19
-0
activity_main.xml
app/src/main/res/layout/activity_main.xml
+7
-5
item_account.xml
app/src/main/res/layout/item_account.xml
+16
-15
item_add_account.xml
app/src/main/res/layout/item_add_account.xml
+4
-9
item_change_status.xml
app/src/main/res/layout/item_change_status.xml
+54
-0
nav_header.xml
app/src/main/res/layout/nav_header.xml
+32
-25
strings.xml
app/src/main/res/values-hi-rIN/strings.xml
+4
-0
strings.xml
app/src/main/res/values-pt-rBR/strings.xml
+6
-2
dimens.xml
app/src/main/res/values/dimens.xml
+2
-1
strings.xml
app/src/main/res/values/strings.xml
+5
-1
No files found.
app/build.gradle
View file @
7ffa0e14
...
...
@@ -13,8 +13,8 @@ android {
applicationId
"chat.rocket.android"
minSdkVersion
21
targetSdkVersion
versions
.
targetSdk
versionCode
200
3
versionName
"2.0.0-beta
3
"
versionCode
200
5
versionName
"2.0.0-beta
4
"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled
true
}
...
...
app/src/main/java/chat/rocket/android/app/DrawableHelper.kt
View file @
7ffa0e14
...
...
@@ -117,4 +117,32 @@ object DrawableHelper {
}
return
userStatusDrawable
}
// TODO Why we need to UserStatus?
/**
* Returns the user status drawable.
*
* @param userStatus The user status.
* @param context The context.
* @sse [chat.rocket.core.internal.realtime.UserStatus]
* @return The user status drawable.
*/
fun
getUserStatusDrawable
(
userStatus
:
chat
.
rocket
.
core
.
internal
.
realtime
.
UserStatus
,
context
:
Context
):
Drawable
{
return
when
(
userStatus
)
{
is
chat
.
rocket
.
core
.
internal
.
realtime
.
UserStatus
.
Online
->
{
getDrawableFromId
(
R
.
drawable
.
ic_status_online_24dp
,
context
)
}
is
chat
.
rocket
.
core
.
internal
.
realtime
.
UserStatus
.
Away
->
{
getDrawableFromId
(
R
.
drawable
.
ic_status_away_24dp
,
context
)
}
is
chat
.
rocket
.
core
.
internal
.
realtime
.
UserStatus
.
Busy
->
{
getDrawableFromId
(
R
.
drawable
.
ic_status_busy_24dp
,
context
)
}
else
->
getDrawableFromId
(
R
.
drawable
.
ic_status_invisible_24dp
,
context
)
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/app/RocketChatApplication.kt
View file @
7ffa0e14
...
...
@@ -18,10 +18,11 @@ import chat.rocket.android.app.migration.model.RealmUser
import
chat.rocket.android.authentication.domain.model.toToken
import
chat.rocket.android.dagger.DaggerAppComponent
import
chat.rocket.android.helper.CrashlyticsTree
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.android.widget.emoji.EmojiRepository
import
chat.rocket.common.model.Token
import
chat.rocket.core.model.Value
...
...
@@ -148,12 +149,12 @@ class RocketChatApplication : Application(), HasActivityInjector, HasServiceInje
private
fun
migrateServerInfo
(
url
:
String
,
authToken
:
String
,
settings
:
PublicSettings
,
user
:
RealmUser
)
{
val
userId
=
user
.
_id
val
avatar
=
UrlHelper
.
getAvatarUrl
(
url
,
user
.
username
!!
)
val
avatar
=
url
.
avatarUrl
(
user
.
username
!!
)
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
url
,
it
)
url
.
serverLogoUrl
(
it
)
}
val
logo
=
settings
.
wideTile
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
url
,
it
)
url
.
serverLogoUrl
(
it
)
}
val
account
=
Account
(
url
,
icon
,
logo
,
user
.
username
!!
,
avatar
)
launch
(
CommonPool
)
{
...
...
app/src/main/java/chat/rocket/android/authentication/login/presentation/LoginPresenter.kt
View file @
7ffa0e14
package
chat.rocket.android.authentication.login.presentation
import
chat.rocket.android.BuildConfig
import
chat.rocket.android.authentication.presentation.AuthenticationNavigator
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.helper.OauthHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.presentation.CheckServerPresenter
import
chat.rocket.android.util.VersionInfo
import
chat.rocket.android.util.extensions.*
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.RocketChatTwoFactorException
import
chat.rocket.common.model.Token
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.*
import
chat.rocket.core.model.Myself
import
kotlinx.coroutines.experimental.delay
import
timber.log.Timber
import
java.util.concurrent.TimeUnit
...
...
@@ -103,7 +100,7 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private
fun
setupCasView
()
{
if
(
settings
.
isCasAuthenticationEnabled
())
{
val
token
=
generateRandomString
(
17
)
view
.
setupCasButtonListener
(
UrlHelper
.
getCasUrl
(
settings
.
casLoginUrl
(),
currentServer
,
token
),
token
)
view
.
setupCasButtonListener
(
settings
.
casLoginUrl
().
casUrl
(
currentServer
,
token
),
token
)
view
.
showCasButton
()
}
}
...
...
@@ -118,7 +115,9 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private
fun
setupOauthServicesView
()
{
launchUI
(
strategy
)
{
try
{
val
services
=
client
.
settingsOauth
().
services
val
services
=
retryIO
(
"settingsOauth()"
)
{
client
.
settingsOauth
().
services
}
if
(
services
.
isNotEmpty
())
{
val
state
=
"{\"loginStyle\":\"popup\",\"credentialToken\":\"${generateRandomString(40)}\",\"isCordova\":true}"
.
encodeToBase64
()
var
totalSocialAccountsEnabled
=
0
...
...
@@ -195,30 +194,32 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
view
.
disableUserInput
()
view
.
showLoading
()
try
{
val
token
=
when
(
loginType
)
{
TYPE_LOGIN_USER_EMAIL
->
{
if
(
usernameOrEmail
.
isEmail
())
{
client
.
loginWithEmail
(
usernameOrEmail
,
password
)
}
else
{
if
(
settings
.
isLdapAuthenticationEnabled
())
{
client
.
loginWithLdap
(
usernameOrEmail
,
password
)
val
token
=
retryIO
(
"login"
)
{
when
(
loginType
)
{
TYPE_LOGIN_USER_EMAIL
->
{
if
(
usernameOrEmail
.
isEmail
())
{
client
.
loginWithEmail
(
usernameOrEmail
,
password
)
}
else
{
client
.
login
(
usernameOrEmail
,
password
)
if
(
settings
.
isLdapAuthenticationEnabled
())
{
client
.
loginWithLdap
(
usernameOrEmail
,
password
)
}
else
{
client
.
login
(
usernameOrEmail
,
password
)
}
}
}
}
TYPE_LOGIN_CAS
->
{
delay
(
3
,
TimeUnit
.
SECONDS
)
client
.
loginWithCas
(
credentialToken
)
}
TYPE_LOGIN_OAUTH
->
{
client
.
loginWithOauth
(
credentialToken
,
credentialSecret
)
}
else
->
{
throw
IllegalStateException
(
"Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH"
)
TYPE_LOGIN_CAS
->
{
delay
(
3
,
TimeUnit
.
SECONDS
)
client
.
loginWithCas
(
credentialToken
)
}
TYPE_LOGIN_OAUTH
->
{
client
.
loginWithOauth
(
credentialToken
,
credentialSecret
)
}
else
->
{
throw
IllegalStateException
(
"Expected TYPE_LOGIN_USER_EMAIL, TYPE_LOGIN_CAS or TYPE_LOGIN_OAUTH"
)
}
}
}
val
username
=
client
.
me
().
username
val
username
=
retryIO
(
"me()"
)
{
client
.
me
().
username
}
if
(
username
!=
null
)
{
localRepository
.
save
(
LocalRepository
.
CURRENT_USERNAME_KEY
,
username
)
saveAccount
(
username
)
...
...
@@ -258,12 +259,12 @@ class LoginPresenter @Inject constructor(private val view: LoginView,
private
suspend
fun
saveAccount
(
username
:
String
)
{
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
logo
=
settings
.
wideTile
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
thumb
=
UrlHelper
.
getAvatarUrl
(
currentServer
,
username
)
val
thumb
=
currentServer
.
avatarUrl
(
username
)
val
account
=
Account
(
currentServer
,
icon
,
logo
,
username
,
thumb
)
saveAccountInteractor
.
save
(
account
)
}
...
...
app/src/main/java/chat/rocket/android/authentication/registerusername/presentation/RegisterUsernamePresenter.kt
View file @
7ffa0e14
...
...
@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.registerusername.presentation
import
chat.rocket.android.authentication.presentation.AuthenticationNavigator
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.registerPushToken
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.Token
import
chat.rocket.common.util.ifNull
...
...
@@ -41,7 +43,9 @@ class RegisterUsernamePresenter @Inject constructor(
if
(
NetworkHelper
.
hasInternetAccess
())
{
view
.
showLoading
()
try
{
val
me
=
client
.
updateOwnBasicInformation
(
username
=
username
)
val
me
=
retryIO
(
"updateOwnBasicInformation(username = $username)"
)
{
client
.
updateOwnBasicInformation
(
username
=
username
)
}
val
registeredUsername
=
me
.
username
if
(
registeredUsername
!=
null
)
{
saveAccount
(
registeredUsername
)
...
...
@@ -75,12 +79,12 @@ class RegisterUsernamePresenter @Inject constructor(
private
suspend
fun
saveAccount
(
username
:
String
)
{
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
logo
=
settings
.
wideTile
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
thumb
=
UrlHelper
.
getAvatarUrl
(
currentServer
,
username
)
val
thumb
=
currentServer
.
avatarUrl
(
username
)
val
account
=
Account
(
currentServer
,
icon
,
logo
,
username
,
thumb
)
saveAccountInteractor
.
save
(
account
)
}
...
...
app/src/main/java/chat/rocket/android/authentication/server/presentation/ServerPresenter.kt
View file @
7ffa0e14
...
...
@@ -3,10 +3,10 @@ package chat.rocket.android.authentication.server.presentation
import
chat.rocket.android.authentication.presentation.AuthenticationNavigator
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.GetAccountsInteractor
import
chat.rocket.android.server.domain.RefreshSettingsInteractor
import
chat.rocket.android.server.domain.SaveCurrentServerInteractor
import
chat.rocket.android.util.extensions.isValidUrl
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.common.util.ifNull
import
javax.inject.Inject
...
...
@@ -18,7 +18,7 @@ class ServerPresenter @Inject constructor(private val view: ServerView,
private
val
refreshSettingsInteractor
:
RefreshSettingsInteractor
,
private
val
getAccountsInteractor
:
GetAccountsInteractor
)
{
fun
connect
(
server
:
String
)
{
if
(!
UrlHelper
.
isValidUrl
(
server
))
{
if
(!
server
.
isValidUrl
(
))
{
view
.
showInvalidServerUrlMessage
()
}
else
{
launchUI
(
strategy
)
{
...
...
app/src/main/java/chat/rocket/android/authentication/signup/presentation/SignupPresenter.kt
View file @
7ffa0e14
...
...
@@ -3,20 +3,22 @@ package chat.rocket.android.authentication.signup.presentation
import
chat.rocket.android.authentication.presentation.AuthenticationNavigator
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.main.viewmodel.NavHeaderViewModel
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.privacyPolicyUrl
import
chat.rocket.android.util.extensions.registerPushToken
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.android.util.extensions.termsOfServiceUrl
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.login
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.registerPushToken
import
chat.rocket.core.internal.rest.signup
import
chat.rocket.core.model.Myself
import
javax.inject.Inject
...
...
@@ -60,10 +62,10 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
try
{
// TODO This function returns a user so should we save it?
client
.
signup
(
email
,
name
,
username
,
password
)
retryIO
(
"signup"
)
{
client
.
signup
(
email
,
name
,
username
,
password
)
}
// TODO This function returns a user token so should we save it?
client
.
login
(
username
,
password
)
val
me
=
client
.
me
()
retryIO
(
"login"
)
{
client
.
login
(
username
,
password
)
}
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
localRepository
.
save
(
LocalRepository
.
CURRENT_USERNAME_KEY
,
me
.
username
)
saveAccount
(
me
)
registerPushToken
()
...
...
@@ -88,13 +90,13 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
fun
termsOfService
()
{
serverInteractor
.
get
()
?.
let
{
navigator
.
toWebPage
(
UrlHelper
.
getTermsOfServiceUrl
(
it
))
navigator
.
toWebPage
(
it
.
termsOfServiceUrl
(
))
}
}
fun
privacyPolicy
()
{
serverInteractor
.
get
()
?.
let
{
navigator
.
toWebPage
(
UrlHelper
.
getPrivacyPolicyUrl
(
it
))
navigator
.
toWebPage
(
it
.
privacyPolicyUrl
(
))
}
}
...
...
@@ -108,12 +110,12 @@ class SignupPresenter @Inject constructor(private val view: SignupView,
private
suspend
fun
saveAccount
(
me
:
Myself
)
{
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
logo
=
settings
.
wideTile
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
thumb
=
UrlHelper
.
getAvatarUrl
(
currentServer
,
me
.
username
!!
)
val
thumb
=
currentServer
.
avatarUrl
(
me
.
username
!!
)
val
account
=
Account
(
currentServer
,
icon
,
logo
,
me
.
username
!!
,
thumb
)
saveAccountInteractor
.
save
(
account
)
}
...
...
app/src/main/java/chat/rocket/android/authentication/twofactor/presentation/TwoFAPresenter.kt
View file @
7ffa0e14
...
...
@@ -3,13 +3,15 @@ package chat.rocket.android.authentication.twofactor.presentation
import
chat.rocket.android.authentication.presentation.AuthenticationNavigator
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.NetworkHelper
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.registerPushToken
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatAuthException
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
...
...
@@ -50,9 +52,10 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
view
.
showLoading
()
try
{
// The token is saved via the client TokenProvider
val
token
=
val
token
=
retryIO
(
"login"
)
{
client
.
login
(
usernameOrEmail
,
password
,
twoFactorAuthenticationCode
)
val
me
=
client
.
me
()
}
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
saveAccount
(
me
)
tokenRepository
.
save
(
server
,
token
)
registerPushToken
()
...
...
@@ -90,12 +93,12 @@ class TwoFAPresenter @Inject constructor(private val view: TwoFAView,
private
suspend
fun
saveAccount
(
me
:
Myself
)
{
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
logo
=
settings
.
wideTile
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
thumb
=
UrlHelper
.
getAvatarUrl
(
currentServer
,
me
.
username
!!
)
val
thumb
=
currentServer
.
avatarUrl
(
me
.
username
!!
)
val
account
=
Account
(
currentServer
,
icon
,
logo
,
me
.
username
!!
,
thumb
)
saveAccountInteractor
.
save
(
account
)
}
...
...
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomPresenter.kt
View file @
7ffa0e14
This diff is collapsed.
Click to expand it.
app/src/main/java/chat/rocket/android/chatroom/presentation/ChatRoomView.kt
View file @
7ffa0e14
...
...
@@ -92,8 +92,10 @@ interface ChatRoomView : LoadingView, MessageView {
/**
* Enables the send message button.
*
* @param sendFailed Whether the sent message has failed.
*/
fun
enableSendMessageButton
()
fun
enableSendMessageButton
(
sendFailed
:
Boolean
)
/**
* Clears the message composition.
...
...
app/src/main/java/chat/rocket/android/chatroom/ui/ChatRoomFragment.kt
View file @
7ffa0e14
...
...
@@ -35,7 +35,6 @@ import kotlinx.android.synthetic.main.fragment_chat_room.*
import
kotlinx.android.synthetic.main.message_attachment_options.*
import
kotlinx.android.synthetic.main.message_composer.*
import
kotlinx.android.synthetic.main.message_list.*
import
timber.log.Timber
import
java.util.concurrent.atomic.AtomicInteger
import
javax.inject.Inject
import
kotlin.math.absoluteValue
...
...
@@ -126,7 +125,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
activity
?.
apply
{
(
this
as
?
ChatRoomActivity
)
?.
showRoomTypeIcon
(
true
)
}
}
override
fun
onActivityCreated
(
savedInstanceState
:
Bundle
?)
{
...
...
@@ -135,6 +133,10 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
override
fun
onDestroyView
()
{
recycler_view
.
removeOnScrollListener
(
endlessRecyclerViewScrollListener
)
recycler_view
.
removeOnScrollListener
(
onScrollListener
)
recycler_view
.
removeOnLayoutChangeListener
(
layoutChangeListener
)
presenter
.
unsubscribeMessages
(
chatRoomId
)
handler
.
removeCallbacksAndMessages
(
null
)
unsubscribeTextMessage
()
...
...
@@ -208,53 +210,16 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
recycler_view
.
layoutManager
=
linearLayoutManager
recycler_view
.
itemAnimator
=
DefaultItemAnimator
()
if
(
dataSet
.
size
>=
30
)
{
recycler_view
.
addOnScrollListener
(
object
:
EndlessRecyclerViewScrollListener
(
linearLayoutManager
)
{
endlessRecyclerViewScrollListener
=
object
:
EndlessRecyclerViewScrollListener
(
recycler_view
.
layoutManager
as
LinearLayoutManager
)
{
override
fun
onLoadMore
(
page
:
Int
,
totalItemsCount
:
Int
,
recyclerView
:
RecyclerView
?)
{
presenter
.
loadMessages
(
chatRoomId
,
chatRoomType
,
page
*
30L
)
}
})
}
recycler_view
.
addOnLayoutChangeListener
{
_
,
_
,
_
,
_
,
bottom
,
_
,
_
,
_
,
oldBottom
->
val
y
=
oldBottom
-
bottom
if
(
y
.
absoluteValue
>
0
)
{
// if y is positive the keyboard is up else it's down
recycler_view
.
post
{
if
(
y
>
0
||
verticalScrollOffset
.
get
().
absoluteValue
>=
y
.
absoluteValue
)
{
recycler_view
.
scrollBy
(
0
,
y
)
}
else
{
recycler_view
.
scrollBy
(
0
,
verticalScrollOffset
.
get
())
}
}
}
recycler_view
.
addOnScrollListener
(
endlessRecyclerViewScrollListener
)
}
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
var
state
=
AtomicInteger
(
RecyclerView
.
SCROLL_STATE_IDLE
)
override
fun
onScrollStateChanged
(
recyclerView
:
RecyclerView
,
newState
:
Int
)
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
when
(
newState
)
{
RecyclerView
.
SCROLL_STATE_IDLE
->
{
if
(!
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_SETTLING
,
newState
))
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
RecyclerView
.
SCROLL_STATE_DRAGGING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
}
RecyclerView
.
SCROLL_STATE_SETTLING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
}
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
if
(
state
.
get
()
!=
RecyclerView
.
SCROLL_STATE_IDLE
)
{
verticalScrollOffset
.
getAndAdd
(
dy
)
}
}
})
recycler_view
.
addOnLayoutChangeListener
(
layoutChangeListener
)
recycler_view
.
addOnScrollListener
(
onScrollListener
)
}
val
oldMessagesCount
=
adapter
.
itemCount
...
...
@@ -267,6 +232,49 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
}
}
private
val
layoutChangeListener
=
View
.
OnLayoutChangeListener
{
_
,
_
,
_
,
_
,
bottom
,
_
,
_
,
_
,
oldBottom
->
val
y
=
oldBottom
-
bottom
if
(
y
.
absoluteValue
>
0
&&
isAdded
)
{
// if y is positive the keyboard is up else it's down
recycler_view
.
post
{
if
(
y
>
0
||
verticalScrollOffset
.
get
().
absoluteValue
>=
y
.
absoluteValue
)
{
recycler_view
.
scrollBy
(
0
,
y
)
}
else
{
recycler_view
.
scrollBy
(
0
,
verticalScrollOffset
.
get
())
}
}
}
}
private
lateinit
var
endlessRecyclerViewScrollListener
:
EndlessRecyclerViewScrollListener
private
val
onScrollListener
=
object
:
RecyclerView
.
OnScrollListener
()
{
var
state
=
AtomicInteger
(
RecyclerView
.
SCROLL_STATE_IDLE
)
override
fun
onScrollStateChanged
(
recyclerView
:
RecyclerView
,
newState
:
Int
)
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
when
(
newState
)
{
RecyclerView
.
SCROLL_STATE_IDLE
->
{
if
(!
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_SETTLING
,
newState
))
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
RecyclerView
.
SCROLL_STATE_DRAGGING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_IDLE
,
newState
)
}
RecyclerView
.
SCROLL_STATE_SETTLING
->
{
state
.
compareAndSet
(
RecyclerView
.
SCROLL_STATE_DRAGGING
,
newState
)
}
}
}
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
if
(
state
.
get
()
!=
RecyclerView
.
SCROLL_STATE_IDLE
)
{
verticalScrollOffset
.
getAndAdd
(
dy
)
}
}
}
override
fun
sendMessage
(
text
:
String
)
{
if
(!
text
.
isBlank
())
{
if
(!
text
.
startsWith
(
"/"
))
{
...
...
@@ -296,10 +304,12 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
button_send
.
isEnabled
=
false
}
override
fun
enableSendMessageButton
()
{
override
fun
enableSendMessageButton
(
sendFailed
:
Boolean
)
{
button_send
.
isEnabled
=
true
text_message
.
isEnabled
=
true
text_message
.
erase
()
if
(!
sendFailed
)
{
clearMessageComposition
()
}
}
override
fun
clearMessageComposition
()
{
...
...
@@ -465,7 +475,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
private
fun
setupRecyclerView
()
{
recycler_view
.
addOnScrollListener
(
object
:
RecyclerView
.
OnScrollListener
()
{
override
fun
onScrolled
(
recyclerView
:
RecyclerView
,
dx
:
Int
,
dy
:
Int
)
{
Timber
.
i
(
"Scrolling vertically: $dy"
)
if
(!
recyclerView
.
canScrollVertically
(
1
))
{
button_fab
.
hide
()
}
else
{
...
...
@@ -522,8 +531,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
var
textMessage
=
citation
?:
""
textMessage
+=
text_message
.
textContent
sendMessage
(
textMessage
)
clearMessageComposition
()
}
button_show_attachment_options
.
setOnClickListener
{
...
...
@@ -588,7 +595,6 @@ class ChatRoomFragment : Fragment(), ChatRoomView, EmojiKeyboardListener, EmojiR
text_message
.
requestFocus
()
emojiKeyboardPopup
.
showAtBottomPending
()
KeyboardHelper
.
showSoftKeyboard
(
text_message
)
}
setReactionButtonIcon
(
R
.
drawable
.
ic_keyboard_black_24dp
)
}
else
{
...
...
app/src/main/java/chat/rocket/android/chatroom/viewmodel/ViewModelMapper.kt
View file @
7ffa0e14
...
...
@@ -9,9 +9,9 @@ import android.text.style.ForegroundColorSpan
import
android.text.style.StyleSpan
import
chat.rocket.android.R
import
chat.rocket.android.helper.MessageParser
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.widget.emoji.EmojiParser
import
chat.rocket.core.model.Message
import
chat.rocket.core.model.MessageType
...
...
@@ -235,7 +235,7 @@ class ViewModelMapper @Inject constructor(private val context: Context,
val
username
=
message
.
sender
?.
username
?:
"?"
return
baseUrl
?.
let
{
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
)
baseUrl
.
avatarUrl
(
username
)
}
}
...
...
app/src/main/java/chat/rocket/android/chatrooms/presentation/ChatRoomsPresenter.kt
View file @
7ffa0e14
...
...
@@ -12,6 +12,7 @@ import chat.rocket.android.server.infraestructure.ConnectionManagerFactory
import
chat.rocket.android.server.infraestructure.chatRooms
import
chat.rocket.android.server.infraestructure.state
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.BaseRoom
import
chat.rocket.common.model.RoomType
...
...
@@ -94,7 +95,9 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
try
{
val
roomList
=
getChatRoomsInteractor
.
getByName
(
currentServer
,
name
)
if
(
roomList
.
isEmpty
())
{
val
(
users
,
rooms
)
=
client
.
spotlight
(
name
)
val
(
users
,
rooms
)
=
retryIO
(
"spotlight($name)"
)
{
client
.
spotlight
(
name
)
}
val
chatRoomsCombined
=
mutableListOf
<
ChatRoom
>()
chatRoomsCombined
.
addAll
(
usersToChatRooms
(
users
))
chatRoomsCombined
.
addAll
(
roomsToChatRooms
(
rooms
))
...
...
@@ -161,7 +164,7 @@ class ChatRoomsPresenter @Inject constructor(private val view: ChatRoomsView,
}
private
suspend
fun
loadRooms
():
List
<
ChatRoom
>
{
val
chatRooms
=
manager
.
chatRooms
().
update
val
chatRooms
=
retryIO
(
"chatRooms"
)
{
manager
.
chatRooms
().
update
}
val
sortedRooms
=
sortRooms
(
chatRooms
)
Timber
.
d
(
"Loaded rooms: ${sortedRooms.size}"
)
saveChatRoomsInteractor
.
save
(
currentServer
,
sortedRooms
)
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsAdapter.kt
View file @
7ffa0e14
...
...
@@ -12,11 +12,11 @@ import android.view.View
import
android.view.ViewGroup
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.infrastructure.checkIfMyself
import
chat.rocket.android.server.domain.PublicSettings
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.content
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.android.util.extensions.setVisible
...
...
@@ -75,7 +75,7 @@ class ChatRoomsAdapter(private val context: Context,
private
fun
bindAvatar
(
chatRoom
:
ChatRoom
,
drawee
:
SimpleDraweeView
)
{
val
avatarId
=
if
(
chatRoom
.
type
is
RoomType
.
DirectMessage
)
chatRoom
.
name
else
"@${chatRoom.name}"
drawee
.
setImageURI
(
UrlHelper
.
getAvatarUrl
(
chatRoom
.
client
.
url
,
avatarId
))
drawee
.
setImageURI
(
chatRoom
.
client
.
url
.
avatarUrl
(
avatarId
))
}
private
fun
bindName
(
chatRoom
:
ChatRoom
,
textView
:
TextView
)
{
...
...
app/src/main/java/chat/rocket/android/chatrooms/ui/ChatRoomsFragment.kt
View file @
7ffa0e14
...
...
@@ -158,15 +158,16 @@ class ChatRoomsFragment : Fragment(), ChatRoomsView {
listJob
?.
cancel
()
listJob
=
launch
(
UI
)
{
val
adapter
=
recycler_view
.
adapter
as
SimpleSectionedRecyclerViewAdapter
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android
.dev/issues/5a90d4718cb3c2fa63b3f557?time=last-seven-days
// TODO - fix this bug to reenable DiffUtil
val
diff
=
async
(
CommonPool
)
{
// FIXME https://fabric.io/rocketchat3/android/apps/chat.rocket.android
/issues/5ac2916c36c7b235275ccccf
// TODO - fix this bug to re
-
enable DiffUtil
/*
val diff = async(CommonPool) {
DiffUtil.calculateDiff(RoomsDiffCallback(adapter.baseAdapter.dataSet, newDataSet))
}.
await
()
}.await()
*/
if
(
isActive
)
{
adapter
.
baseAdapter
.
updateRooms
(
newDataSet
)
diff
.
dispatchUpdatesTo
(
adapter
)
// TODO - fix crash to re-enable diff.dispatchUpdatesTo(adapter)
adapter
.
notifyDataSetChanged
()
//Set sections always after data set is updated
setSections
()
...
...
app/src/main/java/chat/rocket/android/helper/OauthHelper.kt
View file @
7ffa0e14
package
chat.rocket.android.helper
import
chat.rocket.android.util.extensions.removeTrailingSlash
object
OauthHelper
{
/**
...
...
@@ -27,7 +29,7 @@ object OauthHelper {
fun
getGoogleOauthUrl
(
clientId
:
String
,
serverUrl
:
String
,
state
:
String
):
String
{
return
"https://accounts.google.com/o/oauth2/v2/auth"
+
"?client_id=$clientId"
+
"&redirect_uri=${
UrlHelper.removeTrailingSlash(serverUrl
)}/_oauth/google?close"
+
"&redirect_uri=${
serverUrl.removeTrailingSlash(
)}/_oauth/google?close"
+
"&state=$state"
+
"&response_type=code"
+
"&scope=email%20profile"
...
...
@@ -44,7 +46,7 @@ object OauthHelper {
fun
getLinkedinOauthUrl
(
clientId
:
String
,
serverUrl
:
String
,
state
:
String
):
String
{
return
"https://linkedin.com/oauth/v2/authorization"
+
"?client_id=$clientId"
+
"&redirect_uri=${
UrlHelper.removeTrailingSlash(serverUrl
)}/_oauth/linkedin?close"
+
"&redirect_uri=${
serverUrl.removeTrailingSlash(
)}/_oauth/linkedin?close"
+
"&state=$state"
+
"&response_type=code"
}
...
...
@@ -60,7 +62,7 @@ object OauthHelper {
fun
getGitlabOauthUrl
(
clientId
:
String
,
serverUrl
:
String
,
state
:
String
):
String
{
return
"https://gitlab.com/oauth/authorize"
+
"?client_id=$clientId"
+
"&redirect_uri=${
UrlHelper.removeTrailingSlash(serverUrl
)}/_oauth/gitlab?close"
+
"&redirect_uri=${
serverUrl.removeTrailingSlash(
)}/_oauth/gitlab?close"
+
"&state=$state"
+
"&response_type=code"
+
"&scope=read_user"
...
...
app/src/main/java/chat/rocket/android/helper/UrlHelper.kt
deleted
100644 → 0
View file @
958b7c35
package
chat.rocket.android.helper
import
android.util.Patterns
object
UrlHelper
{
/**
* Returns the avatar URL.
*
* @param serverUrl The server URL.
* @param avatarName The avatar name.
* @return The avatar URL.
*/
fun
getAvatarUrl
(
serverUrl
:
String
,
avatarName
:
String
,
format
:
String
=
"jpeg"
):
String
=
removeTrailingSlash
(
serverUrl
)
+
"/avatar/"
+
removeTrailingSlash
(
avatarName
)
+
"?format=$format"
/**
* Returns the server logo URL.
*
* @param serverUrl The server URL.
* @param favicon The faviconLarge from the server settings.
* @return The server logo URL.
*/
fun
getServerLogoUrl
(
serverUrl
:
String
,
favicon
:
String
):
String
=
removeTrailingSlash
(
serverUrl
)
+
"/$favicon"
/**
* Returns the CAS URL.
*
* @param casLoginUrl The CAS login URL from the server settings.
* @param serverUrl The server URL.
* @param token The token to be send to the CAS server.
* @return The avatar URL.
*/
fun
getCasUrl
(
casLoginUrl
:
String
,
serverUrl
:
String
,
token
:
String
):
String
=
removeTrailingSlash
(
casLoginUrl
)
+
"?service="
+
removeTrailingSlash
(
serverUrl
)
+
"/_cas/"
+
token
/**
* Returns the server's Terms of Service URL.
*
* @param serverUrl The server URL.
* @return The server's Terms of Service URL.
*/
fun
getTermsOfServiceUrl
(
serverUrl
:
String
)
=
removeTrailingSlash
(
serverUrl
)
+
"/terms-of-service"
/**
* Returns the server's Privacy Policy URL.
*
* @param serverUrl The server URL.
* @return The server's Privacy Policy URL.
*/
fun
getPrivacyPolicyUrl
(
serverUrl
:
String
)
=
removeTrailingSlash
(
serverUrl
)
+
"/privacy-policy"
/**
* Returns an URL without trailing slash.
*
* @param serverUrl The URL to remove the trailing slash (if exists).
* @return An URL without trailing slash.
*/
fun
removeTrailingSlash
(
serverUrl
:
String
):
String
{
return
if
(
serverUrl
[
serverUrl
.
length
-
1
]
==
'/'
)
{
serverUrl
.
replace
(
"/+$"
,
""
)
}
else
{
serverUrl
}
}
/**
* Checks if the given URL is valid or not.
* @param url The url to check its valid.
* @return True if url is valid, false otherwise.
*/
fun
isValidUrl
(
url
:
String
):
Boolean
=
Patterns
.
WEB_URL
.
matcher
(
url
).
matches
()
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/adapter/AccountViewHolder.kt
View file @
7ffa0e14
...
...
@@ -6,6 +6,7 @@ import chat.rocket.android.server.domain.model.Account
import
kotlinx.android.synthetic.main.item_account.view.*
class
AccountViewHolder
(
itemView
:
View
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
fun
bind
(
account
:
Account
)
{
with
(
itemView
)
{
server_logo
.
setImageURI
(
account
.
serverLogo
)
...
...
app/src/main/java/chat/rocket/android/main/adapter/AccountsAdapter.kt
View file @
7ffa0e14
...
...
@@ -5,50 +5,64 @@ import android.view.ViewGroup
import
chat.rocket.android.R
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.util.extensions.inflate
import
chat.rocket.core.internal.realtime.UserStatus
private
const
val
VIEW_TYPE_CHANGE_STATUS
=
0
private
const
val
VIEW_TYPE_ACCOUNT
=
1
private
const
val
VIEW_TYPE_ADD_ACCOUNT
=
2
class
AccountsAdapter
(
private
val
accounts
:
List
<
Account
>,
private
val
selector
:
Account
Selector
private
val
selector
:
Selector
)
:
RecyclerView
.
Adapter
<
RecyclerView
.
ViewHolder
>()
{
override
fun
onCreateViewHolder
(
parent
:
ViewGroup
,
viewType
:
Int
):
RecyclerView
.
ViewHolder
{
return
when
(
viewType
)
{
VIEW_TYPE_CHANGE_STATUS
->
StatusViewHolder
(
parent
.
inflate
(
R
.
layout
.
item_change_status
))
VIEW_TYPE_ACCOUNT
->
AccountViewHolder
(
parent
.
inflate
(
R
.
layout
.
item_account
))
else
->
AddAccountViewHolder
(
parent
.
inflate
(
R
.
layout
.
item_add_account
))
}
}
override
fun
getItemCount
()
=
accounts
.
size
+
1
override
fun
getItemCount
()
=
accounts
.
size
+
2
override
fun
getItemViewType
(
position
:
Int
)
=
if
(
position
==
accounts
.
size
)
VIEW_TYPE_ADD_ACCOUNT
else
VIEW_TYPE_ACCOUNT
override
fun
getItemViewType
(
position
:
Int
):
Int
{
return
when
{
position
==
0
->
VIEW_TYPE_CHANGE_STATUS
position
<=
accounts
.
size
->
VIEW_TYPE_ACCOUNT
else
->
VIEW_TYPE_ADD_ACCOUNT
}
}
override
fun
onBindViewHolder
(
holder
:
RecyclerView
.
ViewHolder
,
position
:
Int
)
{
when
(
holder
)
{
is
StatusViewHolder
->
bindStatusViewHolder
(
holder
)
is
AccountViewHolder
->
bindAccountViewHolder
(
holder
,
position
)
is
AddAccountViewHolder
->
bindAddAccountViewHolder
(
holder
,
position
)
is
AddAccountViewHolder
->
bindAddAccountViewHolder
(
holder
)
}
}
private
fun
bindStatusViewHolder
(
holder
:
StatusViewHolder
)
{
holder
.
bind
{
userStatus
->
selector
.
onStatusSelected
(
userStatus
)
}
}
private
fun
bindAccountViewHolder
(
holder
:
AccountViewHolder
,
position
:
Int
)
{
val
account
=
accounts
[
position
]
val
account
=
accounts
[
position
-
1
]
holder
.
bind
(
account
)
holder
.
itemView
.
setOnClickListener
{
selector
.
onAccountSelected
(
account
.
serverUrl
)
}
}
private
fun
bindAddAccountViewHolder
(
holder
:
AddAccountViewHolder
,
position
:
Int
)
{
private
fun
bindAddAccountViewHolder
(
holder
:
AddAccountViewHolder
)
{
holder
.
itemView
.
setOnClickListener
{
selector
.
onAddedAccountSelected
()
}
}
}
interface
AccountSelector
{
interface
Selector
{
fun
onStatusSelected
(
userStatus
:
UserStatus
)
fun
onAccountSelected
(
serverUrl
:
String
)
fun
onAddedAccountSelected
()
}
private
const
val
VIEW_TYPE_ACCOUNT
=
0
private
const
val
VIEW_TYPE_ADD_ACCOUNT
=
1
\ No newline at end of file
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/adapter/StatusViewHolder.kt
0 → 100644
View file @
7ffa0e14
package
chat.rocket.android.main.adapter
import
android.support.v7.widget.RecyclerView
import
android.view.View
import
chat.rocket.core.internal.realtime.UserStatus
import
kotlinx.android.synthetic.main.item_change_status.view.*
class
StatusViewHolder
(
itemView
:
View
)
:
RecyclerView
.
ViewHolder
(
itemView
)
{
fun
bind
(
listener
:
(
UserStatus
)
->
Unit
)
{
with
(
itemView
)
{
text_online
.
setOnClickListener
{
listener
(
UserStatus
.
Online
)
}
text_away
.
setOnClickListener
{
listener
(
UserStatus
.
Away
)
}
text_busy
.
setOnClickListener
{
listener
(
UserStatus
.
Busy
)
}
text_invisible
.
setOnClickListener
{
listener
(
UserStatus
.
Offline
)
}
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/presentation/MainPresenter.kt
View file @
7ffa0e14
package
chat.rocket.android.main.presentation
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.main.viewmodel.NavHeaderViewModel
import
chat.rocket.android.main.viewmodel.NavHeaderViewModelMapper
...
...
@@ -12,10 +11,14 @@ import chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.server.presentation.CheckServerPresenter
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.extensions.registerPushToken
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatAuthException
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.realtime.UserStatus
import
chat.rocket.core.internal.realtime.setDefaultStatus
import
chat.rocket.core.internal.rest.logout
import
chat.rocket.core.internal.rest.me
import
chat.rocket.core.internal.rest.unregisterPushToken
...
...
@@ -52,7 +55,7 @@ class MainPresenter @Inject constructor(
checkServerInfo
()
launchUI
(
strategy
)
{
try
{
val
me
=
client
.
me
()
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
val
model
=
navHeaderMapper
.
mapToViewModel
(
me
)
saveAccount
(
model
)
...
...
@@ -77,7 +80,7 @@ class MainPresenter @Inject constructor(
private
suspend
fun
saveAccount
(
me
:
NavHeaderViewModel
)
{
val
icon
=
settings
.
favicon
()
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
currentServer
.
serverLogoUrl
(
it
)
}
val
account
=
Account
(
currentServer
,
icon
,
me
.
serverLogo
,
me
.
username
,
me
.
avatar
)
saveAccountInteractor
.
save
(
account
)
...
...
@@ -90,7 +93,7 @@ class MainPresenter @Inject constructor(
launchUI
(
strategy
)
{
try
{
clearTokens
()
client
.
logout
()
retryIO
(
"logout"
)
{
client
.
logout
()
}
}
catch
(
exception
:
RocketChatException
)
{
Timber
.
d
(
exception
,
"Error calling logout"
)
exception
.
message
?.
let
{
...
...
@@ -117,7 +120,11 @@ class MainPresenter @Inject constructor(
serverInteractor
.
clear
()
val
pushToken
=
localRepository
.
get
(
LocalRepository
.
KEY_PUSH_TOKEN
)
if
(
pushToken
!=
null
)
{
client
.
unregisterPushToken
(
pushToken
)
try
{
retryIO
(
"unregisterPushToken"
)
{
client
.
unregisterPushToken
(
pushToken
)
}
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error unregistering push token"
)
}
}
localRepository
.
clearAllFromServer
(
currentServer
)
}
...
...
@@ -142,6 +149,21 @@ class MainPresenter @Inject constructor(
navigator
.
toServerScreen
()
}
fun
changeStatus
(
userStatus
:
UserStatus
)
{
launchUI
(
strategy
)
{
try
{
client
.
setDefaultStatus
(
userStatus
)
view
.
showUserStatus
(
userStatus
)
}
catch
(
ex
:
RocketChatException
)
{
ex
.
message
?.
let
{
view
.
showMessage
(
it
)
}.
ifNull
{
view
.
showGenericErrorMessage
()
}
}
}
}
suspend
fun
refreshToken
(
token
:
String
?)
{
token
?.
let
{
localRepository
.
save
(
LocalRepository
.
KEY_PUSH_TOKEN
,
token
)
...
...
app/src/main/java/chat/rocket/android/main/presentation/MainView.kt
View file @
7ffa0e14
...
...
@@ -4,8 +4,24 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
import
chat.rocket.android.core.behaviours.MessageView
import
chat.rocket.android.main.viewmodel.NavHeaderViewModel
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.core.internal.realtime.UserStatus
interface
MainView
:
MessageView
,
VersionCheckView
{
/**
* Shows the current user status.
*
* @see [UserStatus]
*/
fun
showUserStatus
(
userStatus
:
UserStatus
)
/**
* Setups the navigation header.
*
* @param model The [NavHeaderViewModel].
* @param accounts The list of accounts.
*/
fun
setupNavHeader
(
model
:
NavHeaderViewModel
,
accounts
:
List
<
Account
>)
fun
closeServerSelection
()
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/main/ui/MainActivity.kt
View file @
7ffa0e14
...
...
@@ -11,7 +11,7 @@ import android.view.MenuItem
import
android.view.View
import
chat.rocket.android.BuildConfig
import
chat.rocket.android.R
import
chat.rocket.android.main.adapter.
Account
Selector
import
chat.rocket.android.main.adapter.Selector
import
chat.rocket.android.main.adapter.AccountsAdapter
import
chat.rocket.android.main.presentation.MainPresenter
import
chat.rocket.android.main.presentation.MainView
...
...
@@ -21,6 +21,7 @@ import chat.rocket.android.util.extensions.fadeIn
import
chat.rocket.android.util.extensions.fadeOut
import
chat.rocket.android.util.extensions.rotateBy
import
chat.rocket.android.util.extensions.showToast
import
chat.rocket.core.internal.realtime.UserStatus
import
com.google.android.gms.gcm.GoogleCloudMessaging
import
com.google.android.gms.iid.InstanceID
import
dagger.android.AndroidInjection
...
...
@@ -42,6 +43,7 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
@Inject
lateinit
var
presenter
:
MainPresenter
private
var
isFragmentAdded
:
Boolean
=
false
private
var
expanded
=
false
private
val
headerLayout
by
lazy
{
view_navigation
.
getHeaderView
(
0
)
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
AndroidInjection
.
inject
(
this
)
...
...
@@ -79,14 +81,26 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
}
}
override
fun
showUserStatus
(
userStatus
:
UserStatus
)
{
headerLayout
.
apply
{
image_user_status
.
setImageDrawable
(
DrawableHelper
.
getUserStatusDrawable
(
userStatus
,
this
.
context
)
)
}
}
override
fun
setupNavHeader
(
model
:
NavHeaderViewModel
,
accounts
:
List
<
Account
>)
{
Timber
.
d
(
"Setting up nav header: $model"
)
val
headerLayout
=
view_navigation
.
getHeaderView
(
0
)
headerLayout
.
text_name
.
text
=
model
.
username
headerLayout
.
text_server
.
text
=
model
.
server
headerLayout
.
image_avatar
.
setImageURI
(
model
.
avatar
)
headerLayout
.
server_logo
.
setImageURI
(
model
.
serverLogo
)
setupAccountsList
(
headerLayout
,
accounts
)
with
(
headerLayout
)
{
text_user_name
.
text
=
model
.
username
text_server_url
.
text
=
model
.
server
image_avatar
.
setImageURI
(
model
.
avatar
)
server_logo
.
setImageURI
(
model
.
serverLogo
)
setupAccountsList
(
headerLayout
,
accounts
)
}
}
override
fun
closeServerSelection
()
{
...
...
@@ -112,7 +126,11 @@ 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
:
AccountSelector
{
accounts_list
.
adapter
=
AccountsAdapter
(
accounts
,
object
:
Selector
{
override
fun
onStatusSelected
(
userStatus
:
UserStatus
)
{
presenter
.
changeStatus
(
userStatus
)
}
override
fun
onAccountSelected
(
serverUrl
:
String
)
{
presenter
.
changeServer
(
serverUrl
)
}
...
...
@@ -120,11 +138,10 @@ class MainActivity : AppCompatActivity(), MainView, HasActivityInjector, HasSupp
override
fun
onAddedAccountSelected
()
{
presenter
.
addNewServer
()
}
})
header
.
account_container
.
setOnClickListener
{
header
.
account_expand
.
rotateBy
(
180f
)
header
.
image_
account_expand
.
rotateBy
(
180f
)
if
(
expanded
)
{
accounts_list
.
fadeOut
()
}
else
{
...
...
app/src/main/java/chat/rocket/android/main/viewmodel/NavHeaderViewModelMapper.kt
View file @
7ffa0e14
package
chat.rocket.android.main.viewmodel
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.*
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.serverLogoUrl
import
chat.rocket.core.model.Myself
import
javax.inject.Inject
...
...
@@ -12,9 +13,9 @@ class NavHeaderViewModelMapper @Inject constructor(serverInteractor: GetCurrentS
fun
mapToViewModel
(
me
:
Myself
):
NavHeaderViewModel
{
val
username
=
mapUsername
(
me
)
val
thumb
=
me
.
username
?.
let
{
UrlHelper
.
getAvatarUrl
(
currentServer
,
it
)
}
val
thumb
=
me
.
username
?.
let
{
currentServer
.
avatarUrl
(
it
)
}
val
image
=
settings
.
wideTile
()
?:
settings
.
faviconLarge
()
val
logo
=
image
?.
let
{
UrlHelper
.
getServerLogoUrl
(
currentServer
,
it
)
}
val
logo
=
image
?.
let
{
currentServer
.
serverLogoUrl
(
it
)
}
return
NavHeaderViewModel
(
username
,
currentServer
,
thumb
,
logo
)
}
...
...
app/src/main/java/chat/rocket/android/members/presentation/MembersPresenter.kt
View file @
7ffa0e14
...
...
@@ -6,6 +6,7 @@ import chat.rocket.android.members.viewmodel.MemberViewModelMapper
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.model.roomTypeOf
import
chat.rocket.common.util.ifNull
...
...
@@ -26,7 +27,9 @@ class MembersPresenter @Inject constructor(private val view: MembersView,
try
{
view
.
showLoading
()
val
members
=
client
.
getMembers
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
60
)
val
members
=
retryIO
(
"getMembers($chatRoomId, $chatRoomType, $offset)"
)
{
client
.
getMembers
(
chatRoomId
,
roomTypeOf
(
chatRoomType
),
offset
,
60
)
}
val
memberViewModels
=
mapper
.
mapToViewModelList
(
members
.
result
)
view
.
showMembers
(
memberViewModels
,
members
.
total
)
}
catch
(
ex
:
RocketChatException
)
{
...
...
app/src/main/java/chat/rocket/android/members/viewmodel/MemberViewModel.kt
View file @
7ffa0e14
package
chat.rocket.android.members.viewmodel
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.useRealName
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.common.model.User
import
chat.rocket.core.model.Value
...
...
@@ -25,7 +25,7 @@ class MemberViewModel(private val member: User, private val settings: Map<String
private
fun
getUserAvatar
():
String
?
{
val
username
=
member
.
username
?:
"?"
return
baseUrl
?.
let
{
UrlHelper
.
getAvatarUrl
(
baseUrl
,
username
,
"png"
)
baseUrl
.
avatarUrl
(
username
,
"png"
)
}
}
...
...
app/src/main/java/chat/rocket/android/profile/presentation/ProfilePresenter.kt
View file @
7ffa0e14
package
chat.rocket.android.profile.presentation
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.helper.UrlHelper
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.avatarUrl
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.common.util.ifNull
import
chat.rocket.core.RocketChatClient
...
...
@@ -25,9 +26,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
launchUI
(
strategy
)
{
view
.
showLoading
()
try
{
val
myself
=
client
.
me
()
val
myself
=
retryIO
(
"me"
)
{
client
.
me
()
}
myselfId
=
myself
.
id
val
avatarUrl
=
UrlHelper
.
getAvatarUrl
(
serverUrl
,
myself
.
username
!!
)
val
avatarUrl
=
serverUrl
.
avatarUrl
(
myself
.
username
!!
)
view
.
showProfile
(
avatarUrl
,
myself
.
name
?:
""
,
...
...
@@ -51,9 +52,9 @@ class ProfilePresenter @Inject constructor(private val view: ProfileView,
view
.
showLoading
()
try
{
if
(
avatarUrl
!=
""
)
{
client
.
setAvatar
(
avatarUrl
)
retryIO
{
client
.
setAvatar
(
avatarUrl
)
}
}
val
user
=
client
.
updateProfile
(
myselfId
,
email
,
name
,
username
)
val
user
=
retryIO
{
client
.
updateProfile
(
myselfId
,
email
,
name
,
username
)
}
view
.
showProfileUpdateSuccessfullyMessage
()
loadUserProfile
()
}
catch
(
exception
:
RocketChatException
)
{
...
...
app/src/main/java/chat/rocket/android/push/FirebaseTokenService.kt
View file @
7ffa0e14
...
...
@@ -5,7 +5,6 @@ import chat.rocket.android.infrastructure.LocalRepository
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.registerPushToken
import
com.google.android.gms.gcm.GoogleCloudMessaging
import
com.google.android.gms.iid.InstanceID
...
...
app/src/main/java/chat/rocket/android/server/presentation/CheckServerPresenter.kt
View file @
7ffa0e14
...
...
@@ -5,6 +5,7 @@ import chat.rocket.android.authentication.server.presentation.VersionCheckView
import
chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.util.VersionInfo
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.serverInfo
import
timber.log.Timber
...
...
@@ -14,21 +15,25 @@ abstract class CheckServerPresenter constructor(private val strategy: CancelStra
private
val
view
:
VersionCheckView
)
{
internal
fun
checkServerInfo
()
{
launchUI
(
strategy
)
{
val
serverInfo
=
client
.
serverInfo
()
val
thisServerVersion
=
serverInfo
.
version
val
isRequiredVersion
=
isRequiredServerVersion
(
thisServerVersion
)
val
isRecommendedVersion
=
isRecommendedServerVersion
(
thisServerVersion
)
if
(
isRequiredVersion
)
{
if
(
isRecommendedVersion
)
{
Timber
.
i
(
"Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)"
)
try
{
val
serverInfo
=
retryIO
(
description
=
"serverInfo"
,
times
=
5
)
{
client
.
serverInfo
()
}
val
thisServerVersion
=
serverInfo
.
version
val
isRequiredVersion
=
isRequiredServerVersion
(
thisServerVersion
)
val
isRecommendedVersion
=
isRecommendedServerVersion
(
thisServerVersion
)
if
(
isRequiredVersion
)
{
if
(
isRecommendedVersion
)
{
Timber
.
i
(
"Your version is nice! (Requires: 0.62.0, Yours: $thisServerVersion)"
)
}
else
{
view
.
alertNotRecommendedVersion
()
}
}
else
{
view
.
alertNotRecommendedVersion
()
}
}
else
{
if
(!
isRecommendedVersion
)
{
view
.
blockAndAlertNotRequiredVersion
()
Timber
.
i
(
"Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!"
)
if
(!
isRecommendedVersion
)
{
view
.
blockAndAlertNotRequiredVersion
()
Timber
.
i
(
"Oops. Looks like your server is out-of-date! Minimum server version required ${BuildConfig.REQUIRED_SERVER_VERSION}!"
)
}
}
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error getting server info"
)
}
}
}
...
...
app/src/main/java/chat/rocket/android/settings/password/presentation/PasswordPresenter.kt
View file @
7ffa0e14
...
...
@@ -4,6 +4,7 @@ import chat.rocket.android.core.lifecycle.CancelStrategy
import
chat.rocket.android.server.domain.GetCurrentServerInteractor
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.extensions.launchUI
import
chat.rocket.android.util.retryIO
import
chat.rocket.common.RocketChatException
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.me
...
...
@@ -22,7 +23,10 @@ class PasswordPresenter @Inject constructor (private val view: PasswordView,
try
{
view
.
showLoading
()
client
.
updateProfile
(
client
.
me
().
id
,
null
,
null
,
password
,
null
)
val
me
=
retryIO
(
"me"
)
{
client
.
me
()
}
retryIO
(
"updateProfile(${me.id})"
)
{
client
.
updateProfile
(
me
.
id
,
null
,
null
,
password
,
null
)
}
view
.
showPasswordSuccessfullyUpdatedMessage
()
view
.
hideLoading
()
...
...
app/src/main/java/chat/rocket/android/util/IO.kt
0 → 100644
View file @
7ffa0e14
package
chat.rocket.android.util
import
chat.rocket.common.RocketChatNetworkErrorException
import
kotlinx.coroutines.experimental.delay
import
timber.log.Timber
const
val
DEFAULT_RETRY
=
3
suspend
fun
<
T
>
retryIO
(
description
:
String
=
"<missing description>"
,
times
:
Int
=
DEFAULT_RETRY
,
initialDelay
:
Long
=
100
,
// 0.1 second
maxDelay
:
Long
=
1000
,
// 1 second
factor
:
Double
=
2.0
,
block
:
suspend
()
->
T
):
T
{
var
currentDelay
=
initialDelay
repeat
(
times
-
1
)
{
currentTry
->
try
{
return
block
()
}
catch
(
e
:
RocketChatNetworkErrorException
)
{
Timber
.
d
(
e
,
"failed call($currentTry): $description"
)
e
.
printStackTrace
()
}
delay
(
currentDelay
)
currentDelay
=
(
currentDelay
*
factor
).
toLong
().
coerceAtMost
(
maxDelay
)
}
return
block
()
// last attempt
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/extensions/RocketChatClient.kt
View file @
7ffa0e14
...
...
@@ -2,6 +2,7 @@ package chat.rocket.android.util.extensions
import
chat.rocket.android.server.domain.model.Account
import
chat.rocket.android.server.infraestructure.RocketChatClientFactory
import
chat.rocket.android.util.retryIO
import
chat.rocket.core.RocketChatClient
import
chat.rocket.core.internal.rest.registerPushToken
import
kotlinx.coroutines.experimental.CommonPool
...
...
@@ -16,7 +17,9 @@ suspend fun RocketChatClient.registerPushToken(
launch
(
CommonPool
)
{
accounts
.
forEach
{
account
->
try
{
factory
.
create
(
account
.
serverUrl
).
registerPushToken
(
token
)
retryIO
(
description
=
"register push token: ${account.serverUrl}"
)
{
factory
.
create
(
account
.
serverUrl
).
registerPushToken
(
token
)
}
}
catch
(
ex
:
Exception
)
{
Timber
.
d
(
ex
,
"Error registering Push token for ${account.serverUrl}"
)
ex
.
printStackTrace
()
...
...
app/src/main/java/chat/rocket/android/util/extensions/String.kt
0 → 100644
View file @
7ffa0e14
package
chat.rocket.android.util.extensions
import
android.util.Patterns
fun
String
.
removeTrailingSlash
():
String
{
return
if
(
isNotEmpty
()
&&
this
[
length
-
1
]
==
'/'
)
{
this
.
replace
(
"/+$"
,
""
)
}
else
{
this
}
}
fun
String
.
avatarUrl
(
avatar
:
String
,
format
:
String
=
"jpeg"
)
=
"${removeTrailingSlash()}/avatar/${avatar.removeTrailingSlash()}?format=$format"
fun
String
.
serverLogoUrl
(
favicon
:
String
)
=
"${removeTrailingSlash()}/$favicon"
fun
String
.
casUrl
(
serverUrl
:
String
,
token
:
String
)
=
"${removeTrailingSlash()}?service=${serverUrl.removeTrailingSlash()}/_cas/$token"
fun
String
.
termsOfServiceUrl
()
=
"${removeTrailingSlash()}/terms-of-service"
fun
String
.
privacyPolicyUrl
()
=
"${removeTrailingSlash()}/privacy-policy"
fun
String
.
isValidUrl
():
Boolean
=
Patterns
.
WEB_URL
.
matcher
(
this
).
matches
()
\ No newline at end of file
app/src/main/java/chat/rocket/android/util/extensions/Uri.kt
View file @
7ffa0e14
...
...
@@ -29,14 +29,14 @@ fun Uri.getFileName(context: Context): String? {
fun
Uri
.
getFileSize
(
context
:
Context
):
Int
{
val
cursor
=
context
.
contentResolver
.
query
(
this
,
null
,
null
,
null
,
null
,
null
)
var
fileSize
:
String
?
=
null
cursor
.
use
{
cursor
->
val
fileSize
=
cursor
?.
use
{
val
sizeIndex
=
cursor
.
getColumnIndex
(
OpenableColumns
.
SIZE
)
if
(
cursor
!=
null
&&
cursor
.
moveToFirst
())
{
if
(
cursor
.
moveToFirst
())
{
if
(!
cursor
.
isNull
(
sizeIndex
))
{
fileSize
=
cursor
.
getString
(
sizeIndex
)
return
@use
cursor
.
getString
(
sizeIndex
)
}
}
return
@use
null
}
return
fileSize
?.
toIntOrNull
()
?:
-
1
}
...
...
app/src/main/res/drawable/black_gradient.xml
View file @
7ffa0e14
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
<shape
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:shape=
"rectangle"
>
<gradient
android:angle=
"90"
android:endColor=
"#00000000"
android:centerColor=
"#30000000"
android:endColor=
"#00000000"
android:startColor=
"#C0000000"
android:type=
"linear"
/>
</shape>
\ No newline at end of file
app/src/main/res/drawable/ic_status_away_24dp.xml
0 → 100644
View file @
7ffa0e14
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportHeight=
"24"
android:viewportWidth=
"24"
>
<path
android:fillColor=
"#FFFFD100"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeColor=
"#00000000"
android:strokeWidth=
"1"
/>
<path
android:fillColor=
"#00000000"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor=
"#FFFFFFFF"
android:strokeWidth=
"2"
/>
</vector>
app/src/main/res/drawable/ic_status_busy_24dp.xml
0 → 100644
View file @
7ffa0e14
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportHeight=
"24"
android:viewportWidth=
"24"
>
<path
android:fillColor=
"#FFFF2A57"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeColor=
"#00000000"
android:strokeWidth=
"1"
/>
<path
android:fillColor=
"#00000000"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor=
"#FFFFFFFF"
android:strokeWidth=
"2"
/>
</vector>
app/src/main/res/drawable/ic_status_invisible_24dp.xml
0 → 100644
View file @
7ffa0e14
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportHeight=
"24"
android:viewportWidth=
"24"
>
<path
android:fillColor=
"#FFCBCED1"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeColor=
"#00000000"
android:strokeWidth=
"1"
/>
<path
android:fillColor=
"#00000000"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor=
"#FFFFFFFF"
android:strokeWidth=
"2"
/>
</vector>
app/src/main/res/drawable/ic_status_online_24dp.xml
0 → 100644
View file @
7ffa0e14
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportHeight=
"24"
android:viewportWidth=
"24"
>
<path
android:fillColor=
"#FF2DE0A5"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-10,0a10,10 0,1 1,20 0a10,10 0,1 1,-20 0"
android:strokeColor=
"#00000000"
android:strokeWidth=
"1"
/>
<path
android:fillColor=
"#00000000"
android:fillType=
"evenOdd"
android:pathData=
"M12,12m-11,0a11,11 0,1 1,22 0a11,11 0,1 1,-22 0"
android:strokeColor=
"#FFFFFFFF"
android:strokeWidth=
"2"
/>
</vector>
app/src/main/res/layout/activity_main.xml
View file @
7ffa0e14
...
...
@@ -28,21 +28,23 @@
android:layout_width=
"wrap_content"
android:layout_height=
"match_parent"
android:layout_gravity=
"start"
>
<android.support.design.widget.NavigationView
android:id=
"@+id/view_navigation"
android:layout_width=
"wrap_content"
android:layout_height=
"match_parent"
app:menu=
"@menu/navigation"
app:headerLayout=
"@layout/nav_header"
/>
app:headerLayout=
"@layout/nav_header"
app:menu=
"@menu/navigation"
/>
<android.support.v7.widget.RecyclerView
android:id=
"@+id/accounts_list"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_marginTop=
"@dimen/nav_header_height"
android:elevation=
"20dp"
android:background=
"@color/white"
android:alpha=
"0"
android:visibility=
"gone"
/>
android:background=
"@color/white"
android:elevation=
"20dp"
android:visibility=
"gone"
/>
</FrameLayout>
</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
app/src/main/res/layout/item_account.xml
View file @
7ffa0e14
<?xml version="1.0" encoding="utf-8"?>
<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:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?selectableItemBackground"
>
android:paddingEnd=
"16dp"
android:paddingStart=
"16dp"
android:paddingTop=
"16dp"
android:background=
"?selectableItemBackground"
>
<com.facebook.drawee.view.SimpleDraweeView
android:id=
"@+id/server_logo"
android:layout_width=
"40dp"
android:layout_height=
"40dp"
android:layout_marginTop=
"8dp"
android:layout_marginStart=
"16dp"
android:layout_marginBottom=
"8dp"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:actualImageScaleType=
"centerInside"
app:layout_constraintBottom_toBottomOf=
"parent"
app:actualImageScaleType=
"centerInside"
/>
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/text_server_url"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:layout_marginEnd=
"16dp"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textStyle=
"bold"
app:layout_constraintTop_toTopOf=
"@id/server_logo"
app:layout_constraintStart_toEndOf=
"@id/server_logo"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toEndOf=
"@id/server_logo"
app:layout_constraintTop_toTopOf=
"@id/server_logo"
tools:text=
"https://open.rocket.chat"
/>
<TextView
...
...
@@ -36,10 +36,11 @@
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:layout_marginEnd=
"16dp"
android:ellipsize=
"end"
android:maxLines=
"1"
app:layout_constraintBottom_toBottomOf=
"@id/server_logo"
app:layout_constraintStart_toEndOf=
"@id/server_logo"
app:layout_constraintEnd_toEndOf=
"parent"
tools:text=
"Lucio Maciel"
/>
app:layout_constraintStart_toEndOf=
"@id/server_logo"
tools:text=
"Lucio Maciel"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/item_add_account.xml
View file @
7ffa0e14
<?xml version="1.0" encoding="utf-8"?>
<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:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:background=
"?selectableItemBackground"
>
android:padding=
"16dp"
android:background=
"?selectableItemBackground"
>
<ImageView
android:id=
"@+id/server_logo"
android:layout_width=
"40dp"
android:layout_height=
"40dp"
android:layout_marginTop=
"8dp"
android:layout_marginStart=
"16dp"
android:layout_marginBottom=
"8dp"
android:src=
"@drawable/ic_add_24dp"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
...
...
@@ -23,8 +19,7 @@
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginStart=
"8dp"
android:layout_marginEnd=
"16dp"
android:textAppearance=
"@style/TextAppearance.AppCompat.Medium"
android:textAppearance=
"@style/TextAppearance.AppCompat.Body2"
android:text=
"@string/action_add_account"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"parent"
...
...
app/src/main/res/layout/item_change_status.xml
0 → 100644
View file @
7ffa0e14
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:divider=
"?android:dividerHorizontal"
android:orientation=
"vertical"
android:showDividers=
"end"
>
<TextView
android:id=
"@+id/text_online"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"10dp"
android:layout_marginEnd=
"16dp"
android:layout_marginStart=
"16dp"
android:layout_marginTop=
"16dp"
android:drawablePadding=
"10dp"
android:drawableStart=
"@drawable/ic_status_online_24dp"
android:text=
"@string/action_online"
/>
<TextView
android:id=
"@+id/text_away"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"10dp"
android:layout_marginEnd=
"16dp"
android:layout_marginStart=
"16dp"
android:drawablePadding=
"10dp"
android:drawableStart=
"@drawable/ic_status_away_24dp"
android:text=
"@string/action_away"
/>
<TextView
android:id=
"@+id/text_busy"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"10dp"
android:layout_marginEnd=
"16dp"
android:layout_marginStart=
"16dp"
android:drawablePadding=
"10dp"
android:drawableStart=
"@drawable/ic_status_busy_24dp"
android:text=
"@string/action_busy"
/>
<TextView
android:id=
"@+id/text_invisible"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"16dp"
android:layout_marginEnd=
"16dp"
android:layout_marginStart=
"16dp"
android:drawablePadding=
"10dp"
android:drawableStart=
"@drawable/ic_status_invisible_24dp"
android:text=
"@string/action_invisible"
/>
</LinearLayout>
\ No newline at end of file
app/src/main/res/layout/nav_header.xml
View file @
7ffa0e14
<?xml version="1.0" encoding="utf-8"?>
<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:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
...
...
@@ -11,11 +10,11 @@
android:layout_width=
"0dp"
android:layout_height=
"0dp"
android:foreground=
"@drawable/black_gradient"
app:actualImageScaleType=
"centerCrop"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:actualImageScaleType=
"centerCrop"
tools:src=
"@tools:sample/backgrounds/scenic"
/>
<com.facebook.drawee.view.SimpleDraweeView
...
...
@@ -33,50 +32,58 @@
android:id=
"@+id/account_container"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:layout_marginBottom=
"8dp"
android:layout_marginStart=
"12dp"
android:layout_marginEnd=
"12dp"
android:layout_marginTop=
"16dp"
android:padding=
"4dp"
android:elevation=
"2dp"
android:background=
"?selectableItemBackground"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintStart_toStartOf=
"@+id/image_avatar"
android:elevation=
"2dp"
android:paddingBottom=
"4dp"
android:paddingEnd=
"12dp"
android:paddingStart=
"12dp"
android:paddingTop=
"4dp"
app:layout_constraintTop_toBottomOf=
"@+id/image_avatar"
>
<ImageView
android:id=
"@+id/image_user_status"
android:layout_width=
"14dp"
android:layout_height=
"14dp"
android:src=
"@drawable/ic_status_online_24dp"
app:layout_constraintStart_toStartOf=
"parent"
/>
<TextView
android:id=
"@+id/text_name"
android:id=
"@+id/text_user_name"
style=
"@style/Sender.Name.TextView"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"
8
dp"
android:
textAppearance=
"@style/TextAppearance.AppCompat.Medium
"
android:layout_marginEnd=
"
10
dp"
android:
layout_marginStart=
"10dp
"
android:textColor=
"@color/white"
app:layout_constraintEnd_toStartOf=
"@+id/account_expand"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"@+id/image_user_status"
app:layout_constraintEnd_toStartOf=
"@+id/image_account_expand"
app:layout_constraintStart_toEndOf=
"@+id/image_user_status"
app:layout_constraintTop_toTopOf=
"@+id/image_user_status"
tools:text=
"Lucio Maciel"
/>
<TextView
android:id=
"@+id/text_server"
android:id=
"@+id/text_server
_url
"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"8dp"
android:layout_marginEnd=
"10dp"
android:ellipsize=
"end"
android:maxLines=
"1"
android:textAppearance=
"@style/TextAppearance.AppCompat.Small"
android:textColor=
"@color/white"
app:layout_constraintEnd_toStartOf=
"@+id/account_expand"
app:layout_constraintEnd_toStartOf=
"@+id/
image_
account_expand"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_name"
app:layout_constraintTop_toBottomOf=
"@+id/text_
user_
name"
tools:text=
"https://open.rocket.chat"
/>
<ImageView
android:id=
"@+id/account_expand"
android:id=
"@+id/
image_
account_expand"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginEnd=
"8dp"
android:src=
"@drawable/ic_expand_more_24dp"
android:tint=
"@color/whitesmoke"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
android:tint=
"@color/white"
app:layout_constraintBottom_toBottomOf=
"@+id/text_server_url"
app:layout_constraintEnd_toEndOf=
"parent"
/>
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/values-hi-rIN/strings.xml
View file @
7ffa0e14
...
...
@@ -29,6 +29,10 @@
<string
name=
"action_confirm_password"
>
पासवर्ड परिवर्तन की पुष्टि करें
</string>
<string
name=
"action_join_chat"
>
चैट में शामिल हों
</string>
<string
name=
"action_add_account"
>
खाता जोड़ो
</string>
<string
name=
"action_online"
>
ऑनलाइन
</string>
<string
name=
"action_away"
>
दूर
</string>
<string
name=
"action_busy"
>
व्यस्त
</string>
<string
name=
"action_invisible"
>
अदृश्य
</string>
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
...
...
app/src/main/res/values-pt-rBR/strings.xml
View file @
7ffa0e14
...
...
@@ -28,7 +28,11 @@
<string
name=
"action_files"
>
Arquivos
</string>
<string
name=
"action_confirm_password"
>
Confirme a nova senha
</string>
<string
name=
"action_join_chat"
>
Entrar no Chat
</string>
<string
name=
"action_add_account"
>
Adicionar Conta
</string>
<string
name=
"action_add_account"
>
Adicionar conta
</string>
<string
name=
"action_online"
>
Online
</string>
<string
name=
"action_away"
>
Ausente
</string>
<string
name=
"action_busy"
>
Ocupado
</string>
<string
name=
"action_invisible"
>
Invisível
</string>
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
...
...
@@ -52,7 +56,7 @@
<string
name=
"msg_new_user_agreement"
>
Ao proceder você concorda com nossos %1$s e %2$s
</string>
<string
name=
"msg_2fa_code"
>
Código 2FA
</string>
<string
name=
"msg_yesterday"
>
ontem
</string>
<string
name=
"msg_message"
>
Me
s
sagem
</string>
<string
name=
"msg_message"
>
Me
n
sagem
</string>
<string
name=
"msg_this_room_is_read_only"
>
Este chat é apenas de leitura
</string>
<string
name=
"msg_invalid_2fa_code"
>
Código 2FA inválido
</string>
<string
name=
"msg_invalid_file"
>
Arquivo inválido
</string>
...
...
app/src/main/res/values/dimens.xml
View file @
7ffa0e14
...
...
@@ -21,6 +21,8 @@
<dimen
name=
"message_time_text_size"
>
12sp
</dimen>
<dimen
name=
"nav_header_height"
>
140dp
</dimen>
<!-- Emoji -->
<dimen
name=
"picker_padding_bottom"
>
16dp
</dimen>
<dimen
name=
"supposed_keyboard_height"
>
252dp
</dimen>
...
...
@@ -35,6 +37,5 @@
<!-- Autocomplete Popup -->
<dimen
name=
"popup_max_height"
>
150dp
</dimen>
<dimen
name=
"suggestions_box_max_height"
>
250dp
</dimen>
<dimen
name=
"nav_header_height"
>
160dp
</dimen>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
7ffa0e14
...
...
@@ -29,7 +29,11 @@
<string
name=
"action_files"
>
Files
</string>
<string
name=
"action_confirm_password"
>
Confirm Password Change
</string>
<string
name=
"action_join_chat"
>
Join Chat
</string>
<string
name=
"action_add_account"
>
Add Account
</string>
<string
name=
"action_add_account"
>
Add account
</string>
<string
name=
"action_online"
>
Online
</string>
<string
name=
"action_away"
>
Away
</string>
<string
name=
"action_busy"
>
Busy
</string>
<string
name=
"action_invisible"
>
Invisible
</string>
<!-- Settings List -->
<string-array
name=
"settings_actions"
>
...
...
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