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
cc23374c
Unverified
Commit
cc23374c
authored
Dec 14, 2017
by
Rafael Kellermann Streit
Committed by
GitHub
Dec 14, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #635 from RocketChat/pack-of-fixes
[FIX] Pack of fixes
parents
8407c568
f9bf712c
Changes
71
Hide whitespace changes
Inline
Side-by-side
Showing
71 changed files
with
4216 additions
and
2864 deletions
+4216
-2864
build.gradle
android-ddp/build.gradle
+6
-5
DDPClient.java
...-ddp/src/main/java/chat/rocket/android_ddp/DDPClient.java
+5
-1
DDPClientImpl.java
.../src/main/java/chat/rocket/android_ddp/DDPClientImpl.java
+426
-399
build.gradle
app/build.gradle
+37
-32
OkHttpHelper.kt
...src/debug/java/chat/rocket/android/helper/OkHttpHelper.kt
+4
-5
AndroidManifest.xml
app/src/main/AndroidManifest.xml
+31
-26
ConnectionStatusManager.kt
.../main/java/chat/rocket/android/ConnectionStatusManager.kt
+90
-0
RocketChatApplication.java
.../main/java/chat/rocket/android/RocketChatApplication.java
+5
-2
RocketChatCache.java
app/src/main/java/chat/rocket/android/RocketChatCache.java
+0
-298
RocketChatCache.kt
app/src/main/java/chat/rocket/android/RocketChatCache.kt
+407
-0
RocketChatJobCreator.kt
...src/main/java/chat/rocket/android/RocketChatJobCreator.kt
+14
-0
AbstractAuthedActivity.java
.../chat/rocket/android/activity/AbstractAuthedActivity.java
+189
-190
LoginActivity.java
...main/java/chat/rocket/android/activity/LoginActivity.java
+74
-66
LoginPresenter.java
...ain/java/chat/rocket/android/activity/LoginPresenter.java
+1
-1
MainActivity.java
.../main/java/chat/rocket/android/activity/MainActivity.java
+87
-32
MainPresenter.java
...main/java/chat/rocket/android/activity/MainPresenter.java
+21
-17
MethodCallHelper.java
...c/main/java/chat/rocket/android/api/MethodCallHelper.java
+585
-536
DefaultCookieProvider.java
...a/chat/rocket/android/api/rest/DefaultCookieProvider.java
+27
-32
HelperExtensions.kt
...n/java/chat/rocket/android/extensions/HelperExtensions.kt
+9
-0
InputHostnameFragment.java
...et/android/fragment/add_server/InputHostnameFragment.java
+2
-3
InputHostnamePresenter.java
...t/android/fragment/add_server/InputHostnamePresenter.java
+3
-5
AbstractChatRoomFragment.java
...t/android/fragment/chatroom/AbstractChatRoomFragment.java
+1
-1
HomeFragment.java
...a/chat/rocket/android/fragment/chatroom/HomeFragment.java
+0
-18
HomeFragment.kt
...ava/chat/rocket/android/fragment/chatroom/HomeFragment.kt
+14
-0
RoomContract.java
...a/chat/rocket/android/fragment/chatroom/RoomContract.java
+0
-79
RoomContract.kt
...ava/chat/rocket/android/fragment/chatroom/RoomContract.kt
+76
-0
RoomFragment.java
...a/chat/rocket/android/fragment/chatroom/RoomFragment.java
+24
-23
RoomPresenter.java
.../chat/rocket/android/fragment/chatroom/RoomPresenter.java
+391
-362
MessageOptionsDialogFragment.java
...ragment/chatroom/dialog/MessageOptionsDialogFragment.java
+114
-116
OAuthPresenter.java
...va/chat/rocket/android/fragment/oauth/OAuthPresenter.java
+1
-1
AbstractServerConfigFragment.java
.../fragment/server_config/AbstractServerConfigFragment.java
+1
-1
LoginContract.java
.../rocket/android/fragment/server_config/LoginContract.java
+14
-10
LoginFragment.java
.../rocket/android/fragment/server_config/LoginFragment.java
+0
-143
LoginFragment.kt
...at/rocket/android/fragment/server_config/LoginFragment.kt
+133
-0
LoginPresenter.java
...rocket/android/fragment/server_config/LoginPresenter.java
+0
-98
LoginPresenter.kt
...t/rocket/android/fragment/server_config/LoginPresenter.kt
+114
-0
RetryLoginPresenter.java
...t/android/fragment/server_config/RetryLoginPresenter.java
+1
-1
SidebarMainFragment.java
.../rocket/android/fragment/sidebar/SidebarMainFragment.java
+1
-4
SidebarMainPresenter.java
...rocket/android/fragment/sidebar/SidebarMainPresenter.java
+11
-14
AddChannelDialogFragment.java
...oid/fragment/sidebar/dialog/AddChannelDialogFragment.java
+1
-1
AddDirectMessageDialogFragment.java
...agment/sidebar/dialog/AddDirectMessageDialogFragment.java
+2
-2
Logger.java
app/src/main/java/chat/rocket/android/helper/Logger.java
+0
-12
Logger.kt
app/src/main/java/chat/rocket/android/helper/Logger.kt
+16
-0
MessagePopup.java
...at/rocket/android/layouthelper/chatroom/MessagePopup.java
+3
-5
RoomListItemViewHolder.java
...ayouthelper/chatroom/roomlist/RoomListItemViewHolder.java
+1
-1
PushManager.kt
app/src/main/java/chat/rocket/android/push/PushManager.kt
+10
-6
ConnectivityManagerApi.java
...a/chat/rocket/android/service/ConnectivityManagerApi.java
+4
-0
KeepAliveJob.kt
...src/main/java/chat/rocket/android/service/KeepAliveJob.kt
+53
-0
RealmBasedConnectivityManager.java
...rocket/android/service/RealmBasedConnectivityManager.java
+17
-6
RocketChatService.java
...n/java/chat/rocket/android/service/RocketChatService.java
+8
-6
RocketChatWebSocketThread.java
...hat/rocket/android/service/RocketChatWebSocketThread.java
+27
-21
ServerConnectivity.java
.../java/chat/rocket/android/service/ServerConnectivity.java
+45
-45
AbstractRocketChatCacheObserver.java
...oid/service/internal/AbstractRocketChatCacheObserver.java
+2
-6
StreamRoomMessageManager.java
...et/android/service/internal/StreamRoomMessageManager.java
+47
-49
GcmPushRegistrationObserver.java
...android/service/observer/GcmPushRegistrationObserver.java
+1
-1
SessionObserver.java
...chat/rocket/android/service/observer/SessionObserver.java
+4
-1
rotation.xml
app/src/main/res/anim/rotation.xml
+9
-0
community.xml
app/src/main/res/drawable/community.xml
+782
-0
ic_loading.xml
app/src/main/res/drawable/ic_loading.xml
+20
-0
ic_loading_animated.xml
app/src/main/res/drawable/ic_loading_animated.xml
+10
-0
crouton_status_ticker.xml
app/src/main/res/layout/crouton_status_ticker.xml
+36
-0
fragment_login.xml
app/src/main/res/layout/fragment_login.xml
+132
-132
colors.xml
app/src/main/res/values/colors.xml
+1
-0
strings.xml
app/src/main/res/values/strings.xml
+1
-1
styles.xml
app/src/main/res/values/styles.xml
+0
-2
OkHttpHelper.kt
...c/release/java/chat/rocket/android/helper/OkHttpHelper.kt
+2
-2
build.gradle
build.gradle
+2
-2
dependencies.gradle
dependencies.gradle
+20
-3
build.gradle
persistence-realm/build.gradle
+14
-13
build.gradle
rocket-chat-android-widgets/build.gradle
+21
-21
build.gradle
rocket-chat-core/build.gradle
+6
-5
No files found.
android-ddp/build.gradle
View file @
cc23374c
...
...
@@ -21,9 +21,10 @@ android {
}
}
dependencies
{
compile
project
(
':log-wrapper'
)
compile
extraDependencies
.
okHTTP
compile
extraDependencies
.
rxJava
compile
extraDependencies
.
boltTask
compile
supportDependencies
.
annotation
api
project
(
':log-wrapper'
)
implementation
extraDependencies
.
okHTTP
implementation
extraDependencies
.
rxJava
implementation
extraDependencies
.
rxKotlin
implementation
extraDependencies
.
boltTask
implementation
supportDependencies
.
annotation
}
\ No newline at end of file
android-ddp/src/main/java/chat/rocket/android_ddp/DDPClient.java
View file @
cc23374c
...
...
@@ -84,7 +84,11 @@ public class DDPClient {
}
public
void
close
()
{
impl
.
close
(
REASON_CLOSED_BY_USER
,
"closed by DDPClient#close()"
);
close
(
REASON_CLOSED_BY_USER
);
}
public
void
close
(
int
reason
)
{
impl
.
close
(
reason
,
"closed by DDPClient#close()"
);
}
/**
...
...
android-ddp/src/main/java/chat/rocket/android_ddp/DDPClientImpl.java
View file @
cc23374c
...
...
@@ -22,440 +22,467 @@ import io.reactivex.disposables.CompositeDisposable;
import
okhttp3.OkHttpClient
;
public
class
DDPClientImpl
{
private
final
DDPClient
client
;
private
RxWebSocket
websocket
;
private
Flowable
<
RxWebSocketCallback
.
Base
>
flowable
;
private
CompositeDisposable
disposables
;
private
String
currentSession
;
public
DDPClientImpl
(
DDPClient
self
,
OkHttpClient
client
)
{
websocket
=
new
RxWebSocket
(
client
);
this
.
client
=
self
;
}
private
static
JSONObject
toJson
(
String
s
)
{
if
(
TextUtils
.
isEmpty
(
s
))
{
return
null
;
private
final
DDPClient
client
;
private
RxWebSocket
websocket
;
private
Flowable
<
RxWebSocketCallback
.
Base
>
flowable
;
private
CompositeDisposable
disposables
;
private
String
currentSession
;
/* package */
DDPClientImpl
(
DDPClient
self
,
OkHttpClient
client
)
{
websocket
=
new
RxWebSocket
(
client
);
this
.
client
=
self
;
}
try
{
return
new
JSONObject
(
s
);
}
catch
(
JSONException
e
)
{
return
null
;
private
static
JSONObject
toJson
(
String
s
)
{
if
(
TextUtils
.
isEmpty
(
s
))
{
return
null
;
}
try
{
return
new
JSONObject
(
s
);
}
catch
(
JSONException
e
)
{
return
null
;
}
}
}
private
static
String
extractMsg
(
JSONObject
response
)
{
if
(
response
==
null
||
response
.
isNull
(
"msg"
))
{
return
null
;
}
else
{
return
response
.
optString
(
"msg"
);
private
static
String
extractMsg
(
JSONObject
response
)
{
if
(
response
==
null
||
response
.
isNull
(
"msg"
))
{
return
null
;
}
else
{
return
response
.
optString
(
"msg"
);
}
}
}
/* package */
void
connect
(
final
TaskCompletionSource
<
DDPClientCallback
.
Connect
>
task
,
final
String
url
,
String
session
)
{
try
{
flowable
=
websocket
.
connect
(
url
).
autoConnect
(
2
);
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Open
)
.
subscribe
(
callback
->
sendMessage
(
"connect"
,
json
->
(
TextUtils
.
isEmpty
(
session
)
?
json
:
json
.
put
(
"session"
,
DDPClientImpl
.
this
.
currentSession
))
.
put
(
"version"
,
"pre2"
)
.
put
(
"support"
,
new
JSONArray
().
put
(
"pre2"
).
put
(
"pre1"
)),
task
),
RCLog:
:
e
)
);
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
timeout
(
7
,
TimeUnit
.
SECONDS
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"connected"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"session"
))
{
currentSession
=
response
.
optString
(
"session"
);
task
.
trySetResult
(
new
DDPClientCallback
.
Connect
(
client
,
response
.
optString
(
"session"
)));
disposables
.
clear
();
}
else
if
(
"error"
.
equals
(
msg
)
&&
"Already connected"
.
equals
(
response
.
optString
(
"reason"
)))
{
task
.
trySetResult
(
new
DDPClientCallback
.
Connect
(
client
,
null
));
disposables
.
clear
();
}
else
if
(
"failed"
.
equals
(
msg
))
{
task
.
trySetError
(
new
DDPClientCallback
.
Connect
.
Failed
(
client
,
response
.
optString
(
"version"
)));
disposables
.
clear
();
}
},
err
->
task
.
trySetError
(
new
DDPClientCallback
.
Connect
.
Timeout
(
client
))
)
);
addErrorCallback
(
disposables
,
task
);
/* package */
void
connect
(
final
TaskCompletionSource
<
DDPClientCallback
.
Connect
>
task
,
final
String
url
,
String
session
)
{
try
{
flowable
=
websocket
.
connect
(
url
).
autoConnect
(
2
);
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Open
)
.
subscribe
(
callback
->
sendMessage
(
"connect"
,
json
->
(
TextUtils
.
isEmpty
(
session
)
?
json
:
json
.
put
(
"session"
,
DDPClientImpl
.
this
.
currentSession
))
.
put
(
"version"
,
"1"
)
.
put
(
"support"
,
new
JSONArray
().
put
(
"1"
).
put
(
"pre2"
).
put
(
"pre1"
)),
task
),
RCLog:
:
e
)
);
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
timeout
(
7
,
TimeUnit
.
SECONDS
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"connected"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"session"
))
{
currentSession
=
response
.
optString
(
"session"
);
task
.
trySetResult
(
new
DDPClientCallback
.
Connect
(
client
,
response
.
optString
(
"session"
)));
disposables
.
clear
();
}
else
if
(
"error"
.
equals
(
msg
)
&&
"Already connected"
.
equals
(
response
.
optString
(
"reason"
)))
{
task
.
trySetResult
(
new
DDPClientCallback
.
Connect
(
client
,
null
));
disposables
.
clear
();
}
else
if
(
"failed"
.
equals
(
msg
))
{
task
.
trySetError
(
new
DDPClientCallback
.
Connect
.
Failed
(
client
,
response
.
optString
(
"version"
)));
disposables
.
clear
();
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
Exception
(
"Your connection seems off…"
));
}
else
{
task
.
trySetError
(
new
Exception
(
"Ooops. Something's up!"
));
}
}
)
);
addErrorCallback
(
disposables
,
task
);
subscribeBaseListeners
();
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
subscribeBaseListeners
();
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
}
}
}
public
Maybe
<
DDPClientCallback
.
Base
>
ping
(
@Nullable
final
String
id
)
{
final
boolean
requested
=
(
TextUtils
.
isEmpty
(
id
))
?
sendMessage
(
"ping"
,
null
)
:
sendMessage
(
"ping"
,
json
->
json
.
put
(
"id"
,
id
));
if
(
requested
)
{
return
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
timeout
(
8
,
TimeUnit
.
SECONDS
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
filter
(
response
->
"pong"
.
equalsIgnoreCase
(
extractMsg
(
response
)))
.
doOnError
(
error
->
{
RCLog
.
e
(
error
,
"Heartbeat ping[%s] xxx failed xxx"
,
id
);
})
.
map
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"pong"
.
equals
(
msg
))
{
RCLog
.
d
(
"pong[%s] <"
,
id
);
if
(
response
.
isNull
(
"id"
))
{
return
new
DDPClientCallback
.
Ping
(
client
,
null
);
}
else
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
return
new
DDPClientCallback
.
Ping
(
client
,
_id
);
}
else
{
return
new
DDPClientCallback
.
Ping
.
UnMatched
(
client
,
_id
);
}
}
}
// if we receive anything other than a pong throw an exception
throw
new
DDPClientCallback
.
RPC
.
Error
(
client
,
id
,
response
);
}).
firstElement
();
}
else
{
return
Maybe
.
error
(
new
DDPClientCallback
.
Closed
(
client
));
/* package */
Maybe
<
DDPClientCallback
.
Base
>
ping
(
@Nullable
final
String
id
)
{
final
boolean
requested
=
(
TextUtils
.
isEmpty
(
id
))
?
sendMessage
(
"ping"
,
null
)
:
sendMessage
(
"ping"
,
json
->
json
.
put
(
"id"
,
id
));
if
(
requested
)
{
return
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
timeout
(
8
,
TimeUnit
.
SECONDS
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
filter
(
response
->
"pong"
.
equalsIgnoreCase
(
extractMsg
(
response
))
)
.
doOnError
(
error
->
{
RCLog
.
e
(
error
,
"Heartbeat ping[%s] xxx failed xxx"
,
id
);
})
.
map
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"pong"
.
equals
(
msg
))
{
RCLog
.
d
(
"pong[%s] <"
,
id
);
if
(
response
.
isNull
(
"id"
))
{
return
new
DDPClientCallback
.
Ping
(
client
,
null
);
}
else
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
return
new
DDPClientCallback
.
Ping
(
client
,
_id
);
}
else
{
return
new
DDPClientCallback
.
Ping
.
UnMatched
(
client
,
_id
);
}
}
}
// if we receive anything other than a pong throw an exception
throw
new
DDPClientCallback
.
RPC
.
Error
(
client
,
id
,
response
);
}).
firstElement
(
);
}
else
{
return
Maybe
.
error
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
}
public
void
ping
(
final
TaskCompletionSource
<
DDPClientCallback
.
Ping
>
task
,
@Nullable
final
String
id
)
{
final
boolean
requested
=
(
TextUtils
.
isEmpty
(
id
))
?
sendMessage
(
"ping"
,
null
)
:
sendMessage
(
"ping"
,
json
->
json
.
put
(
"id"
,
id
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
timeout
(
8
,
TimeUnit
.
SECONDS
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"pong"
.
equals
(
msg
))
{
if
(
response
.
isNull
(
"id"
))
{
task
.
setResult
(
new
DDPClientCallback
.
Ping
(
client
,
null
));
}
else
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPClientCallback
.
Ping
(
client
,
id
));
/* package */
void
ping
(
final
TaskCompletionSource
<
DDPClientCallback
.
Ping
>
task
,
@Nullable
final
String
id
)
{
final
boolean
requested
=
(
TextUtils
.
isEmpty
(
id
))
?
sendMessage
(
"ping"
,
null
)
:
sendMessage
(
"ping"
,
json
->
json
.
put
(
"id"
,
id
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
timeout
(
8
,
TimeUnit
.
SECONDS
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"pong"
.
equals
(
msg
))
{
if
(
response
.
isNull
(
"id"
))
{
task
.
setResult
(
new
DDPClientCallback
.
Ping
(
client
,
null
));
}
else
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPClientCallback
.
Ping
(
client
,
id
));
}
}
disposables
.
clear
();
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
Exception
(
"Your connection seems off…"
));
}
else
{
task
.
trySetError
(
new
Exception
(
"Ooops. Something's up!"
));
}
}
}
disposables
.
clear
();
}
},
err
->
task
.
trySetError
(
new
DDPClientCallback
.
Ping
.
Timeout
(
client
))
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
}
public
void
sub
(
final
TaskCompletionSource
<
DDPSubscription
.
Ready
>
task
,
String
name
,
JSONArray
params
,
String
id
)
{
final
boolean
requested
=
sendMessage
(
"sub"
,
json
->
json
.
put
(
"id"
,
id
).
put
(
"name"
,
name
).
put
(
"params"
,
params
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"ready"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"subs"
))
{
JSONArray
ids
=
response
.
optJSONArray
(
"subs"
);
for
(
int
i
=
0
;
i
<
ids
.
length
();
i
++)
{
String
_id
=
ids
.
optString
(
i
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPSubscription
.
Ready
(
client
,
id
));
disposables
.
clear
();
break
;
}
}
}
else
if
(
"nosub"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"id"
)
&&
!
response
.
isNull
(
"error"
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
trySetError
(
new
DDPSubscription
.
NoSub
.
Error
(
client
,
id
,
response
.
optJSONObject
(
"error"
)));
disposables
.
clear
();
}
}
},
RCLog:
:
e
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
/* package */
void
sub
(
final
TaskCompletionSource
<
DDPSubscription
.
Ready
>
task
,
String
name
,
JSONArray
params
,
String
id
)
{
final
boolean
requested
=
sendMessage
(
"sub"
,
json
->
json
.
put
(
"id"
,
id
).
put
(
"name"
,
name
).
put
(
"params"
,
params
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"ready"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"subs"
))
{
JSONArray
ids
=
response
.
optJSONArray
(
"subs"
);
for
(
int
i
=
0
;
i
<
ids
.
length
();
i
++)
{
String
_id
=
ids
.
optString
(
i
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPSubscription
.
Ready
(
client
,
id
));
disposables
.
clear
();
break
;
}
}
}
else
if
(
"nosub"
.
equals
(
msg
)
&&
!
response
.
isNull
(
"id"
)
&&
!
response
.
isNull
(
"error"
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
trySetError
(
new
DDPSubscription
.
NoSub
.
Error
(
client
,
id
,
response
.
optJSONObject
(
"error"
)));
disposables
.
clear
();
}
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
Exception
(
"Your connection seems off…"
));
}
else
{
task
.
trySetError
(
new
Exception
(
"Ooops. Something's up!"
));
}
}
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
}
public
void
unsub
(
final
TaskCompletionSource
<
DDPSubscription
.
NoSub
>
task
,
@Nullable
final
String
id
)
{
/* package */
void
unsub
(
final
TaskCompletionSource
<
DDPSubscription
.
NoSub
>
task
,
@Nullable
final
String
id
)
{
final
boolean
requested
=
sendMessage
(
"unsub"
,
json
->
json
.
put
(
"id"
,
id
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"nosub"
.
equals
(
msg
)
&&
response
.
isNull
(
"error"
)
&&
!
response
.
isNull
(
"id"
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPSubscription
.
NoSub
(
client
,
id
));
disposables
.
clear
();
}
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
Exception
(
"Your connection seems off…"
));
}
else
{
task
.
trySetError
(
new
Exception
(
"Ooops. Something's up!"
));
}
}
)
);
final
boolean
requested
=
sendMessage
(
"unsub"
,
json
->
json
.
put
(
"id"
,
id
));
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
/* package */
void
rpc
(
final
TaskCompletionSource
<
DDPClientCallback
.
RPC
>
task
,
String
method
,
JSONArray
params
,
String
id
,
long
timeoutMs
)
{
final
boolean
requested
=
sendMessage
(
"method"
,
json
->
json
.
put
(
"method"
,
method
).
put
(
"params"
,
params
).
put
(
"id"
,
id
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
timeout
(
timeoutMs
,
TimeUnit
.
MILLISECONDS
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"result"
.
equals
(
msg
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
if
(!
response
.
isNull
(
"error"
))
{
task
.
trySetError
(
new
DDPClientCallback
.
RPC
.
Error
(
client
,
id
,
response
.
optJSONObject
(
"error"
)));
}
else
{
String
result
=
response
.
optString
(
"result"
);
task
.
setResult
(
new
DDPClientCallback
.
RPC
(
client
,
id
,
result
));
}
disposables
.
clear
();
}
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
Exception
(
"Your connection seems off…"
));
}
else
{
task
.
trySetError
(
new
Exception
(
"Ooops. Something's up!"
));
}
}
)
);
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"nosub"
.
equals
(
msg
)
&&
response
.
isNull
(
"error"
)
&&
!
response
.
isNull
(
"id"
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
task
.
setResult
(
new
DDPSubscription
.
NoSub
(
client
,
id
));
disposables
.
clear
();
}
}
},
err
->
{
}
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
private
void
subscribeBaseListeners
()
{
if
(
disposables
!=
null
&&
disposables
.
size
()
>
0
&&
!
disposables
.
isDisposed
())
{
return
;
}
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"ping"
.
equals
(
msg
))
{
if
(
response
.
isNull
(
"id"
))
{
sendMessage
(
"pong"
,
null
);
}
else
{
sendMessage
(
"pong"
,
json
->
json
.
put
(
"id"
,
response
.
getString
(
"id"
)));
}
}
},
RCLog:
:
e
)
);
}
}
public
void
rpc
(
final
TaskCompletionSource
<
DDPClientCallback
.
RPC
>
task
,
String
method
,
JSONArray
params
,
String
id
,
long
timeoutMs
)
{
final
boolean
requested
=
sendMessage
(
"method"
,
json
->
json
.
put
(
"method"
,
method
).
put
(
"params"
,
params
).
put
(
"id"
,
id
));
if
(
requested
)
{
CompositeDisposable
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
timeout
(
timeoutMs
,
TimeUnit
.
MILLISECONDS
)
.
subscribe
(
response
->
{
/* package */
Flowable
<
DDPSubscription
.
Event
>
getDDPSubscription
()
{
String
[]
targetMsgs
=
{
"added"
,
"changed"
,
"removed"
,
"addedBefore"
,
"movedBefore"
};
return
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
filter
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"result"
.
equals
(
msg
))
{
String
_id
=
response
.
optString
(
"id"
);
if
(
id
.
equals
(
_id
))
{
if
(!
response
.
isNull
(
"error"
))
{
task
.
trySetError
(
new
DDPClientCallback
.
RPC
.
Error
(
client
,
id
,
response
.
optJSONObject
(
"error"
)));
}
else
{
String
result
=
response
.
optString
(
"result"
);
task
.
setResult
(
new
DDPClientCallback
.
RPC
(
client
,
id
,
result
));
for
(
String
m
:
targetMsgs
)
{
if
(
m
.
equals
(
msg
))
{
return
true
;
}
disposables
.
clear
();
}
}
},
err
->
{
if
(
err
instanceof
TimeoutException
)
{
task
.
trySetError
(
new
DDPClientCallback
.
RPC
.
Timeout
(
client
));
return
false
;
})
.
map
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"added"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Added
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
));
}
else
if
(
"addedBefore"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Added
.
Before
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
),
response
.
isNull
(
"before"
)
?
null
:
response
.
optString
(
"before"
));
}
else
if
(
"changed"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Changed
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
),
response
.
isNull
(
"cleared"
)
?
new
JSONArray
()
:
response
.
optJSONArray
(
"before"
));
}
else
if
(
"removed"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Removed
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
));
}
else
if
(
"movedBefore"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
MovedBefore
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"before"
)
?
null
:
response
.
optString
(
"before"
));
}
}
)
);
addErrorCallback
(
disposables
,
task
);
}
else
{
task
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
return
null
;
});
}
}
private
void
subscribeBaseListeners
()
{
if
(
disposables
!=
null
&&
disposables
.
size
()
>
0
&&
!
disposables
.
isDisposed
())
{
return
;
/* package */
void
un
subscribeBaseListeners
()
{
if
(
disposables
.
size
()
>
0
||
!
disposables
.
isDisposed
())
{
disposables
.
clear
();
}
}
disposables
=
new
CompositeDisposable
();
disposables
.
add
(
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
subscribe
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"ping"
.
equals
(
msg
))
{
if
(
response
.
isNull
(
"id"
))
{
sendMessage
(
"pong"
,
null
);
}
else
{
sendMessage
(
"pong"
,
json
->
json
.
put
(
"id"
,
response
.
getString
(
"id"
)));
}
}
},
RCLog:
:
e
)
);
}
public
Flowable
<
DDPSubscription
.
Event
>
getDDPSubscription
()
{
String
[]
targetMsgs
=
{
"added"
,
"changed"
,
"removed"
,
"addedBefore"
,
"movedBefore"
};
return
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Message
)
.
map
(
callback
->
((
RxWebSocketCallback
.
Message
)
callback
).
responseBodyString
)
.
map
(
DDPClientImpl:
:
toJson
)
.
filter
(
response
->
{
String
msg
=
extractMsg
(
response
);
for
(
String
m
:
targetMsgs
)
{
if
(
m
.
equals
(
msg
))
{
return
true
;
}
}
return
false
;
})
.
map
(
response
->
{
String
msg
=
extractMsg
(
response
);
if
(
"added"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Added
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
));
}
else
if
(
"addedBefore"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Added
.
Before
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
),
response
.
isNull
(
"before"
)
?
null
:
response
.
optString
(
"before"
));
}
else
if
(
"changed"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Changed
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"fields"
)
?
null
:
response
.
optJSONObject
(
"fields"
),
response
.
isNull
(
"cleared"
)
?
new
JSONArray
()
:
response
.
optJSONArray
(
"before"
));
}
else
if
(
"removed"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
Removed
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
));
}
else
if
(
"movedBefore"
.
equals
(
msg
))
{
return
new
DDPSubscription
.
MovedBefore
(
client
,
response
.
optString
(
"collection"
),
response
.
optString
(
"id"
),
response
.
isNull
(
"before"
)
?
null
:
response
.
optString
(
"before"
));
}
return
null
;
/* package */
Task
<
RxWebSocketCallback
.
Close
>
getOnCloseCallback
()
{
TaskCompletionSource
<
RxWebSocketCallback
.
Close
>
task
=
new
TaskCompletionSource
<>();
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Close
)
.
cast
(
RxWebSocketCallback
.
Close
.
class
)
.
subscribe
(
task:
:
setResult
,
err
->
setTaskError
(
task
,
err
)
);
return
task
.
getTask
().
onSuccessTask
(
_task
->
{
unsubscribeBaseListeners
();
return
_task
;
});
}
}
public
void
unsubscribeBaseListeners
()
{
if
(
disposables
.
size
()
>
0
||
!
disposables
.
isDisposed
())
{
disposables
.
clear
();
private
boolean
sendMessage
(
String
msg
,
@Nullable
JSONBuilder
json
)
{
try
{
JSONObject
origJson
=
new
JSONObject
().
put
(
"msg"
,
msg
);
String
msg2
=
(
json
==
null
?
origJson
:
json
.
create
(
origJson
)).
toString
();
return
websocket
.
sendText
(
msg2
);
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
return
false
;
}
}
}
public
Task
<
RxWebSocketCallback
.
Close
>
getOnCloseCallback
()
{
TaskCompletionSource
<
RxWebSocketCallback
.
Close
>
task
=
new
TaskCompletionSource
<>();
private
void
sendMessage
(
String
msg
,
@Nullable
JSONBuilder
json
,
TaskCompletionSource
<?>
taskForSetError
)
{
if
(!
sendMessage
(
msg
,
json
)
&&
taskForSetError
!=
null
)
{
taskForSetError
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
flowable
.
filter
(
callback
->
callback
instanceof
RxWebSocketCallback
.
Close
)
.
cast
(
RxWebSocketCallback
.
Close
.
class
)
.
subscribe
(
task:
:
setResult
,
err
->
setTaskError
(
task
,
err
)
private
void
addErrorCallback
(
CompositeDisposable
disposables
,
TaskCompletionSource
<?>
task
)
{
disposables
.
add
(
flowable
.
subscribe
(
base
->
{
if
(
base
instanceof
RxWebSocketCallback
.
Close
)
{
task
.
trySetError
(
new
Exception
(((
RxWebSocketCallback
.
Close
)
base
).
reason
));
}
},
err
->
{
setTaskError
(
task
,
new
Exception
(
err
));
disposables
.
clear
();
}
)
);
return
task
.
getTask
().
onSuccessTask
(
_task
->
{
unsubscribeBaseListeners
();
return
_task
;
});
}
private
boolean
sendMessage
(
String
msg
,
@Nullable
JSONBuilder
json
)
{
try
{
JSONObject
origJson
=
new
JSONObject
().
put
(
"msg"
,
msg
);
String
msg2
=
(
json
==
null
?
origJson
:
json
.
create
(
origJson
)).
toString
();
return
websocket
.
sendText
(
msg2
);
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
return
false
;
}
}
private
void
sendMessage
(
String
msg
,
@Nullable
JSONBuilder
json
,
TaskCompletionSource
<?>
taskForSetError
)
{
if
(!
sendMessage
(
msg
,
json
)
&&
taskForSetError
!=
null
)
{
taskForSetError
.
trySetError
(
new
DDPClientCallback
.
Closed
(
client
));
}
}
private
void
addErrorCallback
(
CompositeDisposable
disposables
,
TaskCompletionSource
<?>
task
)
{
disposables
.
add
(
flowable
.
subscribe
(
base
->
{
if
(
base
instanceof
RxWebSocketCallback
.
Close
)
{
task
.
trySetError
(
new
Exception
(((
RxWebSocketCallback
.
Close
)
base
).
reason
));
}
},
err
->
{
setTaskError
(
task
,
new
Exception
(
err
));
disposables
.
clear
();
}
)
);
}
public
void
close
(
int
code
,
String
reason
)
{
try
{
websocket
.
close
(
code
,
reason
);
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
public
void
close
(
int
code
,
String
reason
)
{
try
{
websocket
.
close
(
code
,
reason
);
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
}
}
}
private
void
setTaskError
(
TaskCompletionSource
task
,
Throwable
throwable
)
{
if
(
task
.
getTask
().
isCompleted
())
{
return
;
private
void
setTaskError
(
TaskCompletionSource
task
,
Throwable
throwable
)
{
if
(
task
.
getTask
().
isCompleted
())
{
return
;
}
if
(
throwable
instanceof
Exception
)
{
task
.
setError
((
Exception
)
throwable
);
}
else
{
task
.
setError
(
new
Exception
(
throwable
));
}
}
if
(
throwable
instanceof
Exception
)
{
task
.
setError
((
Exception
)
throwable
);
}
else
{
task
.
setError
(
new
Exception
(
throwable
));
}
}
private
interface
JSONBuilder
{
@NonNull
JSONObject
create
(
JSONObject
root
)
throws
JSONException
;
}
private
interface
JSONBuilder
{
@NonNull
JSONObject
create
(
JSONObject
root
)
throws
JSONException
;
}
}
app/build.gradle
View file @
cc23374c
...
...
@@ -98,7 +98,6 @@ play {
track
=
"${track}"
}
ext
{
playLibVersion
=
'11.6.0'
stethoVersion
=
'1.5.0'
stethoOkhttp3Version
=
'1.5.0'
stethoRealmVersion
=
'2.2.2'
...
...
@@ -109,43 +108,49 @@ ext {
}
dependencies
{
compile
project
(
':android-ddp'
)
compile
project
(
':rocket-chat-android-widgets'
)
compile
project
(
':persistence-realm'
)
compile
extraDependencies
.
okHTTP
compile
extraDependencies
.
rxJava
compile
extraDependencies
.
boltTask
compile
supportDependencies
.
multidex
compile
supportDependencies
.
designSupportLibrary
compile
supportDependencies
.
annotation
compile
rxbindingDependencies
.
rxBinding
compile
rxbindingDependencies
.
rxBindingSupport
compile
rxbindingDependencies
.
rxBindingAppcompact
compile
"org.jetbrains.kotlin:kotlin-stdlib-jre7:$rootProject.ext.kotlinVersion"
compile
"com.google.firebase:firebase-core:$playLibVersion"
compile
"com.google.firebase:firebase-crash:$playLibVersion"
compile
"com.google.android.gms:play-services-gcm:$playLibVersion"
compile
"com.trello.rxlifecycle2:rxlifecycle:$rxlifecycleVersion"
compile
"com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycleVersion"
compile
"com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycleVersion"
compile
'nl.littlerobots.rxlint:rxlint:1.2'
compile
"frankiesardo:icepick:$icepickVersion"
api
project
(
':android-ddp'
)
api
project
(
':rocket-chat-android-widgets'
)
api
project
(
':persistence-realm'
)
implementation
extraDependencies
.
okHTTP
implementation
extraDependencies
.
rxJava
implementation
extraDependencies
.
rxKotlin
implementation
extraDependencies
.
boltTask
implementation
supportDependencies
.
multidex
implementation
supportDependencies
.
designSupportLibrary
implementation
supportDependencies
.
annotation
implementation
rxbindingDependencies
.
rxBinding
implementation
rxbindingDependencies
.
rxBindingSupport
implementation
rxbindingDependencies
.
rxBindingAppcompact
api
"org.jetbrains.kotlin:kotlin-stdlib-jre8:$rootProject.ext.kotlinVersion"
implementation
"com.google.firebase:firebase-core:$rootProject.ext.playLibVersion"
implementation
"com.google.firebase:firebase-crash:$rootProject.ext.playLibVersion"
implementation
"com.google.android.gms:play-services-gcm:$rootProject.ext.playLibVersion"
implementation
"com.trello.rxlifecycle2:rxlifecycle:$rxlifecycleVersion"
implementation
"com.trello.rxlifecycle2:rxlifecycle-android:$rxlifecycleVersion"
implementation
"com.trello.rxlifecycle2:rxlifecycle-components:$rxlifecycleVersion"
implementation
'nl.littlerobots.rxlint:rxlint:1.2'
implementation
"frankiesardo:icepick:$icepickVersion"
annotationProcessor
"frankiesardo:icepick-processor:$icepickVersion"
compile
"com.github.hotchemi:permissionsdispatcher:$permissionsdispatcherVersion"
implementation
"com.github.hotchemi:permissionsdispatcher:$permissionsdispatcherVersion"
annotationProcessor
"com.github.hotchemi:permissionsdispatcher-processor:$permissionsdispatcherVersion"
compile
(
'com.crashlytics.sdk.android:crashlytics:2.6.8@aar'
)
{
implementation
(
'com.crashlytics.sdk.android:crashlytics:2.6.8@aar'
)
{
transitive
=
true
;
}
debugCompile
"com.facebook.stetho:stetho:$stethoVersion"
implementation
(
extraDependencies
.
crouton
)
{
exclude
group:
'com.android.support'
,
module:
'support-v4'
}
implementation
extraDependencies
.
androidJob
implementation
extraDependencies
.
jstate
debugImplementation
"com.facebook.stetho:stetho:$stethoVersion"
debugCompile
"com.facebook.stetho:stetho-okhttp3:$stethoOkhttp3Version"
debugCompile
"com.uphyca:stetho_realm:$stethoRealmVersion"
debugCompile
"com.tspoon.traceur:traceur:1.0.1"
test
Compile
'junit:junit:4.12'
test
Compile
'org.robolectric:robolectric:3.3'
test
Compile
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
test
Compile
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
test
Compile
"org.jetbrains.kotlin:kotlin-reflect:$rootProject.ext.kotlinVersion"
test
Compile
"com.nhaarman:mockito-kotlin:1.5.0"
test
Compile
'org.amshove.kluent:kluent:1.14'
test
Implementation
'junit:junit:4.12'
test
Implementation
'org.robolectric:robolectric:3.3'
test
Implementation
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
test
Implementation
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
test
Implementation
"org.jetbrains.kotlin:kotlin-reflect:$rootProject.ext.kotlinVersion"
test
Implementation
"com.nhaarman:mockito-kotlin:1.5.0"
test
Implementation
'org.amshove.kluent:kluent:1.14'
}
apply
plugin:
'com.google.gms.google-services'
app/src/debug/java/chat/rocket/android/helper/OkHttpHelper.kt
View file @
cc23374c
package
chat.rocket.android.helper
import
android.content.Context
import
chat.rocket.android.RocketChatCache
import
chat.rocket.android.api.rest.CookieInterceptor
import
chat.rocket.android.api.rest.DefaultCookieProvider
import
com.facebook.stetho.okhttp3.StethoInterceptor
...
...
@@ -24,17 +22,18 @@ object OkHttpHelper {
return
httpClientForUploadFile
?:
throw
AssertionError
(
"httpClientForUploadFile set to null by another thread"
)
}
fun
getClientForDownloadFile
(
context
:
Context
):
OkHttpClient
{
if
(
httpClientForDownloadFile
==
null
)
{
fun
getClientForDownloadFile
():
OkHttpClient
{
if
(
httpClientForDownloadFile
==
null
)
{
httpClientForDownloadFile
=
OkHttpClient
.
Builder
()
.
addNetworkInterceptor
(
StethoInterceptor
())
.
followRedirects
(
true
)
.
followSslRedirects
(
true
)
.
addInterceptor
(
CookieInterceptor
(
DefaultCookieProvider
(
RocketChatCache
(
context
)
)))
.
addInterceptor
(
CookieInterceptor
(
DefaultCookieProvider
()))
.
build
()
}
return
httpClientForDownloadFile
?:
throw
AssertionError
(
"httpClientForDownloadFile set to null by another thread"
)
}
/**
* Returns the OkHttpClient instance for WebSocket connection.
* @return The OkHttpClient WebSocket connection instance.
...
...
app/src/main/AndroidManifest.xml
View file @
cc23374c
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"chat.rocket.android"
>
package=
"chat.rocket.android"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.ACCESS_NETWORK_STATE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WAKE_LOCK"
/>
<uses-permission
android:name=
"android.permission.VIBRATE"
/>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.ACCESS_NETWORK_STATE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>
<uses-permission
android:name=
"android.permission.WAKE_LOCK"
/>
<uses-permission
android:name=
"android.permission.VIBRATE"
/>
<permission
android:name=
"chat.rocket.android.permission.C2D_MESSAGE"
android:protectionLevel=
"signature"
/>
android:protectionLevel=
"signature"
/>
<uses-permission
android:name=
"chat.rocket.android.permission.C2D_MESSAGE"
/>
<uses-permission
android:name=
"chat.rocket.android.permission.C2D_MESSAGE"
/>
<application
android:name=
".RocketChatApplication"
...
...
@@ -26,39 +27,40 @@
android:configChanges=
"orientation|screenSize"
android:windowSoftInputMode=
"adjustResize"
>
<intent-filter>
<action
android:name=
"android.intent.action.MAIN"
/>
<action
android:name=
"android.intent.action.MAIN"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
<category
android:name=
"android.intent.category.LAUNCHER"
/>
<category
android:name=
"android.intent.category.DEFAULT"
/>
<category
android:name=
"android.intent.category.LAUNCHER"
/>
</intent-filter>
</activity>
<activity
android:name=
".activity.room.RoomActivity"
android:configChanges=
"orientation|screenSize"
android:windowSoftInputMode=
"adjustResize"
/>
android:windowSoftInputMode=
"adjustResize"
/>
<activity
android:name=
".activity.AddServerActivity"
android:configChanges=
"orientation|screenSize"
android:
windowSoftInputMode=
"adjustResize
"
android:
launchMode=
"singleTop"
/>
android:
launchMode=
"singleTop
"
android:
windowSoftInputMode=
"adjustResize"
/>
<activity
android:name=
".activity.LoginActivity"
android:configChanges=
"orientation|screenSize"
android:windowSoftInputMode=
"adjustResize"
/>
android:windowSoftInputMode=
"adjustResize"
/>
<service
android:name=
".service.RocketChatService"
/>
<service
android:name=
".service.RocketChatService"
/>
<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=
"chat.rocket.android"
/>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
<action
android:name=
"com.google.android.c2dm.intent.REGISTRATION"
/>
<category
android:name=
"chat.rocket.android"
/>
</intent-filter>
</receiver>
...
...
@@ -67,8 +69,8 @@
android:exported=
"true"
android:permission=
"com.google.android.c2dm.permission.SEND"
>
<intent-filter>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
<category
android:name=
"chat.rocket.android"
/>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
<category
android:name=
"chat.rocket.android"
/>
</intent-filter>
</receiver>
...
...
@@ -76,7 +78,8 @@
android:name=
"com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported=
"false"
/>
<service
android:name=
"com.google.firebase.iid.FirebaseInstanceIdService"
<service
android:name=
"com.google.firebase.iid.FirebaseInstanceIdService"
android:exported=
"true"
>
<intent-filter
android:priority=
"-500"
>
<action
android:name=
"com.google.firebase.INSTANCE_ID_EVENT"
/>
...
...
@@ -87,7 +90,7 @@
android:name=
".push.gcm.GCMIntentService"
android:exported=
"false"
>
<intent-filter>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
<action
android:name=
"com.google.android.c2dm.intent.RECEIVE"
/>
</intent-filter>
</service>
...
...
@@ -95,14 +98,16 @@
android:name=
".push.gcm.GcmInstanceIDListenerService"
android:exported=
"false"
>
<intent-filter>
<action
android:name=
"com.google.android.gms.iid.InstanceID"
/>
<action
android:name=
"com.google.android.gms.iid.InstanceID"
/>
</intent-filter>
</service>
<receiver
android:name=
".push.PushManager$DeleteReceiver"
<receiver
android:name=
".push.PushManager$DeleteReceiver"
android:exported=
"false"
/>
<receiver
android:name=
".push.PushManager$ReplyReceiver"
<receiver
android:name=
".push.PushManager$ReplyReceiver"
android:exported=
"false"
/>
<meta-data
...
...
app/src/main/java/chat/rocket/android/ConnectionStatusManager.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android
import
chat.rocket.android.extensions.printStackTraceOnDebug
import
chat.rocket.android.service.KeepAliveJob
import
unquietcode.tools.esm.EnumStateMachine
import
unquietcode.tools.esm.TransitionException
object
ConnectionStatusManager
{
enum
class
State
{
OFFLINE
,
CONNECTING
,
ONLINE
}
private
const
val
DEBUG
=
false
private
val
DEFAULT_CALLBACK
=
object
:
TransitionCallback
{
override
fun
onTransitioned
(
success
:
Boolean
)
{
}
}
private
val
stateMachine
:
EnumStateMachine
<
State
>
init
{
stateMachine
=
EnumStateMachine
(
State
.
OFFLINE
)
stateMachine
.
addTransitions
(
State
.
OFFLINE
,
State
.
CONNECTING
)
stateMachine
.
addTransitions
(
State
.
CONNECTING
,
State
.
ONLINE
,
State
.
OFFLINE
)
stateMachine
.
addTransitions
(
State
.
ONLINE
,
State
.
OFFLINE
)
}
@Synchronized
fun
transitionCount
()
=
stateMachine
.
transitionCount
()
@Synchronized
fun
currentState
()
=
stateMachine
.
currentState
()
@Synchronized
fun
setOnline
(
callback
:
TransitionCallback
=
DEFAULT_CALLBACK
)
{
KeepAliveJob
.
cancelPendingJobRequests
()
tryTransitionTo
(
State
.
ONLINE
,
callback
)
}
@Synchronized
fun
setOnline
()
{
KeepAliveJob
.
cancelPendingJobRequests
()
tryTransitionTo
(
State
.
ONLINE
,
DEFAULT_CALLBACK
)
}
@Synchronized
fun
setConnecting
(
callback
:
TransitionCallback
=
DEFAULT_CALLBACK
)
{
KeepAliveJob
.
cancelPendingJobRequests
()
tryTransitionTo
(
State
.
CONNECTING
,
callback
)
}
@Synchronized
fun
setConnecting
()
{
KeepAliveJob
.
cancelPendingJobRequests
()
tryTransitionTo
(
State
.
CONNECTING
,
DEFAULT_CALLBACK
)
}
@Synchronized
fun
setConnectionError
(
callback
:
TransitionCallback
=
DEFAULT_CALLBACK
)
{
KeepAliveJob
.
schedule
()
tryTransitionTo
(
State
.
OFFLINE
,
callback
)
}
@Synchronized
fun
setConnectionError
()
{
KeepAliveJob
.
schedule
()
tryTransitionTo
(
State
.
OFFLINE
,
DEFAULT_CALLBACK
)
}
@Synchronized
fun
setOffline
()
{
stateMachine
.
reset
()
}
private
fun
tryTransitionTo
(
newState
:
State
,
callback
:
TransitionCallback
)
{
try
{
stateMachine
.
transition
(
newState
)
callback
.
onTransitioned
(
true
)
}
catch
(
e
:
TransitionException
)
{
if
(
DEBUG
)
{
e
.
printStackTraceOnDebug
()
}
callback
.
onTransitioned
(
false
)
}
}
interface
TransitionCallback
{
fun
onTransitioned
(
success
:
Boolean
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/RocketChatApplication.java
View file @
cc23374c
...
...
@@ -5,6 +5,7 @@ import android.support.multidex.MultiDexApplication;
import
android.support.v7.app.AppCompatDelegate
;
import
com.crashlytics.android.Crashlytics
;
import
com.evernote.android.job.JobManager
;
import
java.util.List
;
...
...
@@ -34,6 +35,8 @@ public class RocketChatApplication extends MultiDexApplication {
@Override
public
void
onCreate
()
{
super
.
onCreate
();
RocketChatCache
.
INSTANCE
.
initialize
(
this
);
JobManager
.
create
(
this
).
addJobCreator
(
new
RocketChatJobCreator
());
DDPClient
.
initialize
(
OkHttpHelper
.
INSTANCE
.
getClientForWebSocket
());
Fabric
.
with
(
this
,
new
Crashlytics
());
...
...
@@ -44,7 +47,7 @@ public class RocketChatApplication extends MultiDexApplication {
RealmStore
.
put
(
serverInfo
.
getHostname
());
}
RocketChatWidgets
.
initialize
(
this
,
OkHttpHelper
.
INSTANCE
.
getClientForDownloadFile
(
this
));
RocketChatWidgets
.
initialize
(
this
,
OkHttpHelper
.
INSTANCE
.
getClientForDownloadFile
());
if
(
Build
.
VERSION
.
SDK_INT
<
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
AppCompatDelegate
.
setCompatVectorFromResourcesEnabled
(
true
);
...
...
@@ -57,7 +60,7 @@ public class RocketChatApplication extends MultiDexApplication {
if
(
BuildConfig
.
DEBUG
)
{
e
.
printStackTrace
();
}
Logger
.
report
(
e
);
Logger
.
INSTANCE
.
report
(
e
);
});
instance
=
this
;
...
...
app/src/main/java/chat/rocket/android/RocketChatCache.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
;
import
android.content.Context
;
import
android.content.SharedPreferences
;
import
com.hadisatrio.optional.Optional
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.UUID
;
import
chat.rocket.android.helper.Logger
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.core.utils.Pair
;
import
io.reactivex.BackpressureStrategy
;
import
io.reactivex.Flowable
;
import
io.reactivex.annotations.NonNull
;
import
io.reactivex.annotations.Nullable
;
import
okhttp3.HttpUrl
;
/**
* sharedpreference-based cache.
*/
public
class
RocketChatCache
{
private
static
final
String
KEY_SELECTED_SERVER_HOSTNAME
=
"KEY_SELECTED_SERVER_HOSTNAME"
;
private
static
final
String
KEY_SELECTED_SITE_URL
=
"KEY_SELECTED_SITE_URL"
;
private
static
final
String
KEY_SELECTED_SITE_NAME
=
"KEY_SELECTED_SITE_NAME"
;
private
static
final
String
KEY_SELECTED_ROOM_ID
=
"KEY_SELECTED_ROOM_ID"
;
private
static
final
String
KEY_PUSH_ID
=
"KEY_PUSH_ID"
;
private
static
final
String
KEY_HOSTNAME_LIST
=
"KEY_HOSTNAME_LIST"
;
private
Context
context
;
public
RocketChatCache
(
Context
context
)
{
this
.
context
=
context
.
getApplicationContext
();
}
public
String
getSelectedServerHostname
()
{
return
getString
(
KEY_SELECTED_SERVER_HOSTNAME
,
null
);
}
public
void
setSelectedServerHostname
(
String
hostname
)
{
String
newHostname
=
null
;
if
(
hostname
!=
null
)
{
newHostname
=
hostname
.
toLowerCase
();
}
setString
(
KEY_SELECTED_SERVER_HOSTNAME
,
newHostname
);
}
public
void
addHostSiteName
(
@NonNull
String
currentHostname
,
@NonNull
String
siteName
)
{
try
{
String
hostSiteNamesJson
=
getHostSiteNamesJson
();
JSONObject
jsonObject
=
(
hostSiteNamesJson
==
null
)
?
new
JSONObject
()
:
new
JSONObject
(
hostSiteNamesJson
);
jsonObject
.
put
(
currentHostname
,
siteName
);
setString
(
KEY_SELECTED_SITE_NAME
,
jsonObject
.
toString
());
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
}
public
@NonNull
String
getHostSiteName
(
@NonNull
String
host
)
{
if
(
host
.
startsWith
(
"http"
))
{
HttpUrl
url
=
HttpUrl
.
parse
(
host
);
if
(
url
!=
null
)
{
host
=
url
.
host
();
}
}
try
{
String
hostSiteNamesJson
=
getHostSiteNamesJson
();
JSONObject
jsonObject
=
(
hostSiteNamesJson
==
null
)
?
new
JSONObject
()
:
new
JSONObject
(
hostSiteNamesJson
);
host
=
getSiteUrlFor
(
host
);
return
jsonObject
.
optString
(
host
);
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
return
""
;
}
private
@Nullable
String
getHostSiteNamesJson
()
{
return
getString
(
KEY_SELECTED_SITE_NAME
,
null
);
}
public
void
addHostnameSiteUrl
(
@Nullable
String
hostnameAlias
,
@NonNull
String
currentHostname
)
{
String
alias
=
null
;
if
(
hostnameAlias
!=
null
)
{
alias
=
hostnameAlias
.
toLowerCase
();
}
try
{
String
selectedHostnameAliasJson
=
getLoginHostnamesJson
();
JSONObject
jsonObject
=
selectedHostnameAliasJson
==
null
?
new
JSONObject
()
:
new
JSONObject
(
selectedHostnameAliasJson
);
jsonObject
.
put
(
alias
,
currentHostname
);
setString
(
KEY_SELECTED_SITE_URL
,
jsonObject
.
toString
());
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
}
public
@Nullable
String
getSiteUrlFor
(
String
hostname
)
{
try
{
String
selectedServerHostname
=
getSelectedServerHostname
();
if
(
getLoginHostnamesJson
()
==
null
||
getLoginHostnamesJson
().
isEmpty
())
{
return
null
;
}
return
new
JSONObject
(
getLoginHostnamesJson
())
.
optString
(
hostname
,
selectedServerHostname
);
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
return
null
;
}
private
@Nullable
String
getLoginHostnamesJson
()
{
return
getString
(
KEY_SELECTED_SITE_URL
,
null
);
}
public
void
addHostname
(
@NonNull
String
hostname
,
@Nullable
String
hostnameAvatarUri
,
String
siteName
)
{
String
hostnameList
=
getString
(
KEY_HOSTNAME_LIST
,
null
);
try
{
JSONObject
json
;
if
(
hostnameList
==
null
)
{
json
=
new
JSONObject
();
}
else
{
json
=
new
JSONObject
(
hostnameList
);
}
JSONObject
serverInfoJson
=
new
JSONObject
();
serverInfoJson
.
put
(
"avatar"
,
hostnameAvatarUri
);
serverInfoJson
.
put
(
"sitename"
,
siteName
);
// Replace server avatar uri if exists.
json
.
put
(
hostname
,
hostnameAvatarUri
==
null
?
JSONObject
.
NULL
:
serverInfoJson
);
setString
(
KEY_HOSTNAME_LIST
,
json
.
toString
());
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
}
public
List
<
Pair
<
String
,
Pair
<
String
,
String
>>>
getServerList
()
{
String
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
);
if
(
json
==
null
)
{
return
Collections
.
emptyList
();
}
try
{
JSONObject
jsonObj
=
new
JSONObject
(
json
);
List
<
Pair
<
String
,
Pair
<
String
,
String
>>>
serverList
=
new
ArrayList
<>();
for
(
Iterator
<
String
>
iter
=
jsonObj
.
keys
();
iter
.
hasNext
();)
{
String
hostname
=
iter
.
next
();
JSONObject
serverInfoJson
=
jsonObj
.
getJSONObject
(
hostname
);
serverList
.
add
(
new
Pair
<>(
hostname
,
new
Pair
<>(
"http://"
+
hostname
+
"/"
+
serverInfoJson
.
getString
(
"avatar"
),
serverInfoJson
.
getString
(
"sitename"
))));
}
return
serverList
;
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
return
Collections
.
emptyList
();
}
public
void
removeHostname
(
String
hostname
)
{
String
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
);
if
(
TextUtils
.
isEmpty
(
json
))
{
return
;
}
try
{
JSONObject
jsonObj
=
new
JSONObject
(
json
);
jsonObj
.
remove
(
hostname
);
String
result
=
jsonObj
.
length
()
==
0
?
null
:
jsonObj
.
toString
();
setString
(
KEY_HOSTNAME_LIST
,
result
);
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
}
@Nullable
public
String
getFirstLoggedHostnameIfAny
()
{
String
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
);
if
(
json
!=
null
)
{
try
{
JSONObject
jsonObj
=
new
JSONObject
(
json
);
if
(
jsonObj
.
length
()
>
0
&&
jsonObj
.
keys
().
hasNext
())
{
// Returns the first hostname on the list.
return
jsonObj
.
keys
().
next
();
}
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
}
return
null
;
}
public
String
getSelectedRoomId
()
{
try
{
JSONObject
jsonObject
=
getSelectedRoomIdJsonObject
();
return
jsonObject
.
optString
(
getSelectedServerHostname
(),
null
);
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
Logger
.
report
(
e
);
}
return
null
;
}
public
void
setSelectedRoomId
(
String
roomId
)
{
try
{
JSONObject
jsonObject
=
getSelectedRoomIdJsonObject
();
jsonObject
.
put
(
getSelectedServerHostname
(),
roomId
);
setString
(
KEY_SELECTED_ROOM_ID
,
jsonObject
.
toString
());
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
Logger
.
report
(
e
);
}
}
private
JSONObject
getSelectedRoomIdJsonObject
()
throws
JSONException
{
String
json
=
getString
(
KEY_SELECTED_ROOM_ID
,
null
);
if
(
json
==
null
)
{
return
new
JSONObject
();
}
return
new
JSONObject
(
json
);
}
public
String
getOrCreatePushId
()
{
SharedPreferences
preferences
=
getSharedPreferences
();
if
(!
preferences
.
contains
(
KEY_PUSH_ID
))
{
// generates one and save
String
newId
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
preferences
.
edit
()
.
putString
(
KEY_PUSH_ID
,
newId
)
.
apply
();
return
newId
;
}
return
preferences
.
getString
(
KEY_PUSH_ID
,
null
);
}
public
Flowable
<
Optional
<
String
>>
getSelectedServerHostnamePublisher
()
{
return
getValuePublisher
(
KEY_SELECTED_SERVER_HOSTNAME
);
}
public
Flowable
<
Optional
<
String
>>
getSelectedRoomIdPublisher
()
{
return
getValuePublisher
(
KEY_SELECTED_ROOM_ID
)
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
map
(
roomValue
->
Optional
.
ofNullable
(
new
JSONObject
(
roomValue
).
optString
(
getSelectedServerHostname
(),
null
)));
}
private
SharedPreferences
getSharedPreferences
()
{
return
context
.
getSharedPreferences
(
"cache"
,
Context
.
MODE_PRIVATE
);
}
private
SharedPreferences
.
Editor
getEditor
()
{
return
getSharedPreferences
().
edit
();
}
public
String
getString
(
String
key
,
String
defaultValue
)
{
return
getSharedPreferences
().
getString
(
key
,
defaultValue
);
}
private
void
setString
(
String
key
,
String
value
)
{
getEditor
().
putString
(
key
,
value
).
apply
();
}
private
Flowable
<
Optional
<
String
>>
getValuePublisher
(
final
String
key
)
{
return
Flowable
.
create
(
emitter
->
{
SharedPreferences
.
OnSharedPreferenceChangeListener
listener
=
(
sharedPreferences
,
changedKey
)
->
{
if
(
key
.
equals
(
changedKey
)
&&
!
emitter
.
isCancelled
())
{
String
value
=
getString
(
key
,
null
);
emitter
.
onNext
(
Optional
.
ofNullable
(
value
));
}
};
emitter
.
setCancellable
(()
->
getSharedPreferences
()
.
unregisterOnSharedPreferenceChangeListener
(
listener
));
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
listener
);
},
BackpressureStrategy
.
LATEST
);
}
public
void
removeSelectedRoomId
(
String
currentHostname
)
{
try
{
JSONObject
selectedRoomIdJsonObject
=
getSelectedRoomIdJsonObject
();
selectedRoomIdJsonObject
.
remove
(
currentHostname
);
String
result
=
selectedRoomIdJsonObject
.
length
()
==
0
?
null
:
selectedRoomIdJsonObject
.
toString
();
setString
(
KEY_SELECTED_ROOM_ID
,
result
);
}
catch
(
JSONException
e
)
{
Logger
.
report
(
e
);
RCLog
.
e
(
e
);
}
}
}
app/src/main/java/chat/rocket/android/RocketChatCache.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android
import
android.content.Context
import
android.content.SharedPreferences
import
android.text.TextUtils
import
chat.rocket.android.helper.Logger
import
chat.rocket.android.log.RCLog
import
chat.rocket.core.utils.Pair
import
com.hadisatrio.optional.Optional
import
io.reactivex.BackpressureStrategy
import
io.reactivex.Flowable
import
okhttp3.HttpUrl
import
org.json.JSONException
import
org.json.JSONObject
import
java.util.*
object
RocketChatCache
{
private
val
KEY_SELECTED_SERVER_HOSTNAME
=
"KEY_SELECTED_SERVER_HOSTNAME"
private
val
KEY_SELECTED_SITE_URL
=
"KEY_SELECTED_SITE_URL"
private
val
KEY_SELECTED_SITE_NAME
=
"KEY_SELECTED_SITE_NAME"
private
val
KEY_SELECTED_ROOM_ID
=
"KEY_SELECTED_ROOM_ID"
private
val
KEY_PUSH_ID
=
"KEY_PUSH_ID"
private
val
KEY_HOSTNAME_LIST
=
"KEY_HOSTNAME_LIST"
private
val
KEY_OPENED_ROOMS
=
"KEY_OPENED_ROOMS"
private
val
KEY_SESSION_TOKEN
=
"KEY_SESSION_TOKEN"
private
lateinit
var
sharedPreferences
:
SharedPreferences
fun
initialize
(
context
:
Context
)
{
sharedPreferences
=
context
.
getSharedPreferences
(
"cache"
,
Context
.
MODE_PRIVATE
)
}
fun
addOpenedRoom
(
roomId
:
String
,
lastSeen
:
Long
)
{
val
openedRooms
=
getOpenedRooms
()
try
{
val
room
=
JSONObject
().
put
(
"rid"
,
roomId
).
put
(
"ls"
,
lastSeen
)
openedRooms
.
put
(
roomId
,
room
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
setString
(
KEY_OPENED_ROOMS
,
openedRooms
.
toString
())
}
fun
removeOpenedRoom
(
roomId
:
String
)
{
val
openedRooms
=
getOpenedRooms
()
if
(
openedRooms
.
has
(
roomId
))
{
openedRooms
.
remove
(
roomId
)
}
}
fun
getOpenedRooms
():
JSONObject
{
val
openedRooms
=
getString
(
KEY_OPENED_ROOMS
,
""
)
openedRooms
?.
let
{
if
(
openedRooms
.
isEmpty
())
{
return
JSONObject
()
}
try
{
return
JSONObject
(
openedRooms
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
return
JSONObject
()
}
fun
getSelectedServerHostname
():
String
?
{
return
getString
(
KEY_SELECTED_SERVER_HOSTNAME
,
null
)
}
fun
setSelectedRoomId
(
roomId
:
String
?)
{
try
{
val
jsonObject
=
getSelectedRoomIdJsonObject
()
jsonObject
.
put
(
getSelectedServerHostname
(),
roomId
)
setString
(
KEY_SELECTED_ROOM_ID
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
Logger
.
report
(
e
)
}
}
@Throws
(
JSONException
::
class
)
private
fun
getSelectedRoomIdJsonObject
():
JSONObject
{
val
json
=
getString
(
KEY_SELECTED_ROOM_ID
,
null
)
?:
return
JSONObject
()
return
JSONObject
(
json
)
}
fun
getOrCreatePushId
():
String
?
{
val
preferences
=
sharedPreferences
if
(!
preferences
.
contains
(
KEY_PUSH_ID
))
{
// generates one and save
val
newId
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
)
preferences
.
edit
()
.
putString
(
KEY_PUSH_ID
,
newId
)
.
apply
()
return
newId
}
return
preferences
.
getString
(
KEY_PUSH_ID
,
null
)
}
fun
addSiteName
(
currentHostname
:
String
,
siteName
:
String
)
{
try
{
val
hostSiteNamesJson
=
getSiteName
()
val
jsonObject
=
if
(
hostSiteNamesJson
==
null
)
JSONObject
()
else
JSONObject
(
hostSiteNamesJson
)
jsonObject
.
put
(
currentHostname
,
siteName
)
setString
(
KEY_SELECTED_SITE_NAME
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
getHostSiteName
(
hostname
:
String
):
String
{
var
host
=
hostname
if
(
hostname
.
startsWith
(
"http"
))
{
val
url
=
HttpUrl
.
parse
(
hostname
)
if
(
url
!=
null
)
{
host
=
url
.
host
()
}
}
try
{
val
hostSiteNamesJson
=
getSiteName
()
val
jsonObject
=
if
(
hostSiteNamesJson
==
null
)
JSONObject
()
else
JSONObject
(
hostSiteNamesJson
)
val
siteUrlFor
=
getSiteUrlFor
(
host
)
return
if
(
siteUrlFor
==
null
)
""
else
jsonObject
.
optString
(
host
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
return
""
}
fun
removeSiteName
(
hostname
:
String
)
{
try
{
val
siteNameJson
=
getSiteName
()
val
jsonObject
=
if
(
siteNameJson
==
null
)
JSONObject
()
else
JSONObject
(
siteNameJson
)
if
(
jsonObject
.
has
(
hostname
))
{
jsonObject
.
remove
(
hostname
)
}
setString
(
KEY_SELECTED_SITE_NAME
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
addSiteUrl
(
hostnameAlias
:
String
?,
currentHostname
:
String
)
{
var
alias
:
String
?
=
null
if
(
hostnameAlias
!=
null
)
{
alias
=
hostnameAlias
.
toLowerCase
()
}
try
{
val
selectedHostnameAliasJson
=
getSiteUrlForAllServers
()
val
jsonObject
=
if
(
selectedHostnameAliasJson
==
null
)
JSONObject
()
else
JSONObject
(
selectedHostnameAliasJson
)
jsonObject
.
put
(
alias
,
currentHostname
)
setString
(
KEY_SELECTED_SITE_URL
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
getSiteUrlFor
(
hostname
:
String
):
String
?
{
try
{
val
selectedServerHostname
=
getSelectedServerHostname
()
return
if
(
getSiteUrlForAllServers
()
==
null
)
null
else
JSONObject
(
getSiteUrlForAllServers
())
.
optString
(
hostname
,
selectedServerHostname
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
return
null
}
fun
addHostname
(
hostname
:
String
,
hostnameAvatarUri
:
String
?,
siteName
:
String
)
{
val
hostnameList
=
getString
(
KEY_HOSTNAME_LIST
,
null
)
try
{
val
json
:
JSONObject
if
(
hostnameList
==
null
)
{
json
=
JSONObject
()
}
else
{
json
=
JSONObject
(
hostnameList
)
}
val
serverInfoJson
=
JSONObject
()
serverInfoJson
.
put
(
"avatar"
,
hostnameAvatarUri
)
serverInfoJson
.
put
(
"sitename"
,
siteName
)
// Replace server avatar uri if exists.
json
.
put
(
hostname
,
if
(
hostnameAvatarUri
==
null
)
JSONObject
.
NULL
else
serverInfoJson
)
setString
(
KEY_HOSTNAME_LIST
,
json
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
getServerList
():
List
<
Pair
<
String
,
Pair
<
String
,
String
>>>
{
val
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
)
?:
return
emptyList
()
try
{
val
jsonObj
=
JSONObject
(
json
)
val
serverList
=
ArrayList
<
Pair
<
String
,
Pair
<
String
,
String
>>>()
val
iter
=
jsonObj
.
keys
()
while
(
iter
.
hasNext
())
{
val
hostname
=
iter
.
next
()
val
serverInfoJson
=
jsonObj
.
getJSONObject
(
hostname
)
serverList
.
add
(
Pair
(
hostname
,
Pair
(
"http://"
+
hostname
+
"/"
+
serverInfoJson
.
getString
(
"avatar"
),
serverInfoJson
.
getString
(
"sitename"
))))
}
return
serverList
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
return
emptyList
()
}
/**
* Wipe all given hostname entries and references from cache.
*/
fun
clearSelectedHostnameReferences
()
{
val
hostname
=
getSelectedServerHostname
()
if
(
hostname
!=
null
)
{
setString
(
KEY_OPENED_ROOMS
,
null
)
removeSiteName
(
hostname
)
removeHostname
(
hostname
)
removeSiteUrl
(
hostname
)
setSelectedServerHostname
(
getFirstLoggedHostnameIfAny
())
}
}
fun
removeHostname
(
hostname
:
String
)
{
val
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
)
if
(
TextUtils
.
isEmpty
(
json
))
{
return
}
try
{
val
jsonObj
=
JSONObject
(
json
)
jsonObj
.
remove
(
hostname
)
val
result
=
if
(
jsonObj
.
length
()
==
0
)
null
else
jsonObj
.
toString
()
setString
(
KEY_HOSTNAME_LIST
,
result
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
setSelectedServerHostname
(
hostname
:
String
?)
{
var
newHostname
:
String
?
=
null
if
(
hostname
!=
null
)
{
newHostname
=
hostname
.
toLowerCase
()
}
setString
(
KEY_SELECTED_SERVER_HOSTNAME
,
newHostname
)
}
fun
getSelectedRoomId
():
String
?
{
try
{
val
jsonObject
=
getSelectedRoomIdJsonObject
()
return
jsonObject
.
optString
(
getSelectedServerHostname
(),
null
)
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
Logger
.
report
(
e
)
}
return
null
}
fun
removeSelectedRoomId
(
currentHostname
:
String
)
{
try
{
val
selectedRoomIdJsonObject
=
getSelectedRoomIdJsonObject
()
selectedRoomIdJsonObject
.
remove
(
currentHostname
)
val
result
=
if
(
selectedRoomIdJsonObject
.
length
()
==
0
)
null
else
selectedRoomIdJsonObject
.
toString
()
setString
(
KEY_SELECTED_ROOM_ID
,
result
)
}
catch
(
e
:
JSONException
)
{
Logger
.
report
(
e
)
RCLog
.
e
(
e
)
}
}
fun
getFirstLoggedHostnameIfAny
():
String
?
{
val
json
=
getString
(
KEY_HOSTNAME_LIST
,
null
)
if
(
json
!=
null
)
{
try
{
val
jsonObj
=
JSONObject
(
json
)
if
(
jsonObj
.
length
()
>
0
&&
jsonObj
.
keys
().
hasNext
())
{
// Returns the first hostname on the list.
return
jsonObj
.
keys
().
next
()
}
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
return
null
}
fun
setSessionToken
(
sessionToken
:
String
)
{
val
selectedServerHostname
=
getSelectedServerHostname
()
?:
throw
IllegalStateException
(
"Trying to set sessionToken to null hostname"
)
val
sessions
=
getString
(
KEY_SESSION_TOKEN
,
null
)
try
{
val
jsonObject
=
if
(
sessions
==
null
)
JSONObject
()
else
JSONObject
(
sessions
)
jsonObject
.
put
(
selectedServerHostname
,
sessionToken
)
setString
(
KEY_SESSION_TOKEN
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
fun
getSessionToken
():
String
?
{
val
selectedServerHostname
=
getSelectedServerHostname
()
val
sessions
=
getString
(
KEY_SESSION_TOKEN
,
null
)
if
(
sessions
==
null
||
selectedServerHostname
==
null
)
{
return
null
}
try
{
val
jsonObject
=
JSONObject
(
sessions
)
if
(
jsonObject
.
has
(
selectedServerHostname
))
{
return
jsonObject
.
optString
(
selectedServerHostname
,
null
)
}
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
return
null
}
private
fun
removeSiteUrl
(
hostname
:
String
)
{
try
{
val
siteUrlForAllServersJson
=
getSiteUrlForAllServers
()
val
jsonObject
=
if
(
siteUrlForAllServersJson
==
null
)
JSONObject
()
else
JSONObject
(
siteUrlForAllServersJson
)
val
keys
=
jsonObject
.
keys
()
while
(
keys
.
hasNext
())
{
val
alias
=
keys
.
next
()
if
(
hostname
==
jsonObject
.
getString
(
alias
))
{
jsonObject
.
remove
(
alias
)
break
}
}
setString
(
KEY_SELECTED_SITE_URL
,
jsonObject
.
toString
())
}
catch
(
e
:
JSONException
)
{
RCLog
.
e
(
e
)
}
}
private
fun
getString
(
key
:
String
,
defaultValue
:
String
?):
String
?
{
return
sharedPreferences
.
getString
(
key
,
defaultValue
)
}
private
fun
getSiteUrlForAllServers
():
String
?
{
return
getString
(
KEY_SELECTED_SITE_URL
,
null
)
}
private
fun
setString
(
key
:
String
,
value
:
String
?)
{
getEditor
().
putString
(
key
,
value
).
apply
()
}
private
fun
getSiteName
():
String
?
{
return
getString
(
KEY_SELECTED_SITE_NAME
,
null
)
}
private
fun
getEditor
():
SharedPreferences
.
Editor
{
return
sharedPreferences
.
edit
()
}
fun
getSelectedServerHostnamePublisher
():
Flowable
<
Optional
<
String
>>
{
return
getValuePublisher
(
KEY_SELECTED_SERVER_HOSTNAME
)
}
fun
getSelectedRoomIdPublisher
():
Flowable
<
Optional
<
String
>>
{
return
getValuePublisher
(
KEY_SELECTED_ROOM_ID
)
.
filter
{
it
.
isPresent
()
}
.
map
{
it
.
get
()
}
.
map
{
roomValue
->
Optional
.
ofNullable
(
JSONObject
(
roomValue
).
optString
(
getSelectedServerHostname
(),
null
))
}
}
private
fun
getValuePublisher
(
key
:
String
):
Flowable
<
Optional
<
String
>>
{
return
Flowable
.
create
({
emitter
->
val
listener
=
SharedPreferences
.
OnSharedPreferenceChangeListener
{
_
,
changedKey
->
if
(
key
==
changedKey
&&
!
emitter
.
isCancelled
)
{
val
value
=
getString
(
key
,
null
)
emitter
.
onNext
(
Optional
.
ofNullable
(
value
))
}
}
emitter
.
setCancellable
{
sharedPreferences
.
unregisterOnSharedPreferenceChangeListener
(
listener
)
}
sharedPreferences
.
registerOnSharedPreferenceChangeListener
(
listener
)
},
BackpressureStrategy
.
LATEST
)
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/RocketChatJobCreator.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android
import
chat.rocket.android.service.KeepAliveJob
import
com.evernote.android.job.Job
import
com.evernote.android.job.JobCreator
class
RocketChatJobCreator
:
JobCreator
{
override
fun
create
(
tag
:
String
):
Job
?
{
when
(
tag
)
{
KeepAliveJob
.
TAG
->
return
KeepAliveJob
()
else
->
return
null
}
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/activity/AbstractAuthedActivity.java
View file @
cc23374c
...
...
@@ -24,207 +24,206 @@ import io.reactivex.schedulers.Schedulers;
import
okhttp3.HttpUrl
;
abstract
class
AbstractAuthedActivity
extends
AbstractFragmentActivity
{
@State
protected
String
hostname
;
@State
protected
String
roomId
;
@State
protected
String
hostname
;
@State
protected
String
roomId
;
private
RocketChatCache
rocketChatCache
;
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
private
boolean
isNotification
;
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
private
boolean
isNotification
;
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
rocketChatCache
=
new
RocketChatCache
(
this
);
if
(
savedInstanceState
==
null
)
{
handleIntent
(
getIntent
());
}
updateHostnameIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
());
}
@Override
protected
void
onNewIntent
(
Intent
intent
)
{
super
.
onNewIntent
(
intent
);
handleIntent
(
intent
);
}
private
void
handleIntent
(
Intent
intent
)
{
if
(
intent
==
null
)
{
return
;
}
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_HOSTNAME
))
{
String
hostname
=
intent
.
getStringExtra
(
PushManager
.
EXTRA_HOSTNAME
);
HttpUrl
url
=
HttpUrl
.
parse
(
hostname
);
if
(
url
!=
null
)
{
String
hostnameFromPush
=
url
.
host
();
String
loginHostname
=
RocketChatCache
.
INSTANCE
.
getSiteUrlFor
(
hostnameFromPush
);
RocketChatCache
.
INSTANCE
.
setSelectedServerHostname
(
loginHostname
);
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_ROOM_ID
))
{
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
intent
.
getStringExtra
(
PushManager
.
EXTRA_ROOM_ID
));
}
}
PushManager
.
INSTANCE
.
clearNotificationsByHost
(
hostname
);
}
else
{
updateHostnameIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
());
}
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_NOT_ID
)
&&
intent
.
hasExtra
(
PushManager
.
EXTRA_HOSTNAME
))
{
isNotification
=
true
;
int
notificationId
=
intent
.
getIntExtra
(
PushManager
.
EXTRA_NOT_ID
,
0
);
String
hostname
=
intent
.
getStringExtra
(
PushManager
.
EXTRA_HOSTNAME
);
HttpUrl
url
=
HttpUrl
.
parse
(
hostname
);
if
(
url
!=
null
)
{
String
hostnameFromPush
=
url
.
host
();
String
loginHostname
=
RocketChatCache
.
INSTANCE
.
getSiteUrlFor
(
hostnameFromPush
);
PushManager
.
INSTANCE
.
clearNotificationsByHostAndNotificationId
(
loginHostname
,
notificationId
);
}
else
{
PushManager
.
INSTANCE
.
clearNotificationsByNotificationId
(
notificationId
);
}
}
}
private
void
updateHostnameIfNeeded
(
String
newHostname
)
{
if
(
hostname
==
null
)
{
if
(
newHostname
!=
null
&&
assertServerRealmStoreExists
(
newHostname
))
{
updateHostname
(
newHostname
);
updateRoomIdIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
());
}
else
{
recoverFromHostnameError
();
}
}
else
{
if
(
hostname
.
equals
(
newHostname
))
{
updateHostname
(
newHostname
);
updateRoomIdIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
());
return
;
}
if
(
assertServerRealmStoreExists
(
newHostname
))
{
Intent
intent
=
new
Intent
(
this
,
MainActivity
.
class
);
startActivity
(
intent
);
finish
();
overridePendingTransition
(
R
.
anim
.
slide_in
,
R
.
anim
.
slide_out
);
}
else
{
recoverFromHostnameError
();
}
}
}
private
boolean
assertServerRealmStoreExists
(
String
hostname
)
{
return
RealmStore
.
get
(
hostname
)
!=
null
;
}
private
void
updateHostname
(
String
hostname
)
{
this
.
hostname
=
hostname
;
onHostnameUpdated
();
}
private
void
recoverFromHostnameError
()
{
final
List
<
ServerInfo
>
serverInfoList
=
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getServerList
();
if
(
serverInfoList
==
null
||
serverInfoList
.
size
()
==
0
)
{
LaunchUtil
.
showAddServerActivity
(
this
);
return
;
}
// just connect to the first available
final
ServerInfo
serverInfo
=
serverInfoList
.
get
(
0
);
if
(
savedInstanceState
==
null
)
{
handleIntent
(
getIntent
()
);
RocketChatCache
.
INSTANCE
.
setSelectedServerHostname
(
serverInfo
.
getHostname
());
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
null
);
}
updateHostnameIfNeeded
(
rocketChatCache
.
getSelectedServerHostname
());
}
private
void
updateRoomIdIfNeeded
(
String
newRoomId
)
{
if
(
roomId
==
null
)
{
if
(
newRoomId
!=
null
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
}
}
else
{
if
(!
roomId
.
equals
(
newRoomId
)
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
}
}
}
@Override
protected
void
onNewIntent
(
Intent
intent
)
{
super
.
onNewIntent
(
intent
);
handleIntent
(
intent
);
}
private
boolean
assertRoomSubscriptionExists
(
String
roomId
)
{
if
(!
assertServerRealmStoreExists
(
hostname
))
{
return
false
;
}
private
void
handleIntent
(
Intent
intent
)
{
if
(
intent
==
null
)
{
return
;
RealmRoom
room
=
RealmStore
.
get
(
hostname
).
executeTransactionForRead
(
realm
->
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
RealmRoom
.
ROOM_ID
,
roomId
).
findFirst
());
if
(
room
==
null
)
{
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
null
);
return
false
;
}
return
true
;
}
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_HOSTNAME
))
{
String
hostname
=
intent
.
getStringExtra
(
PushManager
.
EXTRA_HOSTNAME
);
HttpUrl
url
=
HttpUrl
.
parse
(
hostname
);
if
(
url
!=
null
)
{
String
hostnameFromPush
=
url
.
host
();
String
loginHostname
=
rocketChatCache
.
getSiteUrlFor
(
hostnameFromPush
);
rocketChatCache
.
setSelectedServerHostname
(
loginHostname
);
private
void
updateRoomId
(
String
roomId
)
{
this
.
roomId
=
roomId
;
onRoomIdUpdated
();
}
protected
void
onHostnameUpdated
()
{
}
protected
void
onRoomIdUpdated
()
{
}
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_ROOM_ID
))
{
rocketChatCache
.
setSelectedRoomId
(
intent
.
getStringExtra
(
PushManager
.
EXTRA_ROOM_ID
));
@Override
protected
void
onResume
()
{
super
.
onResume
();
subscribeToConfigChanges
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
();
if
(
isNotification
)
{
updateHostnameIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
());
updateRoomIdIfNeeded
(
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
());
isNotification
=
false
;
}
}
PushManager
.
INSTANCE
.
clearNotificationsByHost
(
hostname
);
}
else
{
updateHostnameIfNeeded
(
rocketChatCache
.
getSelectedServerHostname
());
}
if
(
intent
.
hasExtra
(
PushManager
.
EXTRA_NOT_ID
)
&&
intent
.
hasExtra
(
PushManager
.
EXTRA_HOSTNAME
))
{
isNotification
=
true
;
int
notificationId
=
intent
.
getIntExtra
(
PushManager
.
EXTRA_NOT_ID
,
0
);
String
hostname
=
intent
.
getStringExtra
(
PushManager
.
EXTRA_HOSTNAME
);
HttpUrl
url
=
HttpUrl
.
parse
(
hostname
);
if
(
url
!=
null
)
{
String
hostnameFromPush
=
url
.
host
();
String
loginHostname
=
rocketChatCache
.
getSiteUrlFor
(
hostnameFromPush
);
PushManager
.
INSTANCE
.
clearNotificationsByHostAndNotificationId
(
loginHostname
,
notificationId
);
}
else
{
PushManager
.
INSTANCE
.
clearNotificationsByNotificationId
(
notificationId
);
}
}
}
private
void
updateHostnameIfNeeded
(
String
newHostname
)
{
if
(
hostname
==
null
)
{
if
(
newHostname
!=
null
&&
assertServerRealmStoreExists
(
newHostname
))
{
updateHostname
(
newHostname
);
updateRoomIdIfNeeded
(
rocketChatCache
.
getSelectedRoomId
());
}
else
{
recoverFromHostnameError
();
}
}
else
{
if
(
hostname
.
equals
(
newHostname
))
{
updateHostname
(
newHostname
);
updateRoomIdIfNeeded
(
rocketChatCache
.
getSelectedRoomId
());
return
;
}
if
(
assertServerRealmStoreExists
(
newHostname
))
{
Intent
intent
=
new
Intent
(
this
,
MainActivity
.
class
);
startActivity
(
intent
);
finish
();
overridePendingTransition
(
R
.
anim
.
slide_in
,
R
.
anim
.
slide_out
);
}
else
{
recoverFromHostnameError
();
}
}
}
private
boolean
assertServerRealmStoreExists
(
String
hostname
)
{
return
RealmStore
.
get
(
hostname
)
!=
null
;
}
private
void
updateHostname
(
String
hostname
)
{
this
.
hostname
=
hostname
;
onHostnameUpdated
();
}
private
void
recoverFromHostnameError
()
{
final
List
<
ServerInfo
>
serverInfoList
=
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getServerList
();
if
(
serverInfoList
==
null
||
serverInfoList
.
size
()
==
0
)
{
LaunchUtil
.
showAddServerActivity
(
this
);
return
;
}
// just connect to the first available
final
ServerInfo
serverInfo
=
serverInfoList
.
get
(
0
);
rocketChatCache
.
setSelectedServerHostname
(
serverInfo
.
getHostname
());
rocketChatCache
.
setSelectedRoomId
(
null
);
}
private
void
updateRoomIdIfNeeded
(
String
newRoomId
)
{
if
(
roomId
==
null
)
{
if
(
newRoomId
!=
null
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
}
}
else
{
if
(!
roomId
.
equals
(
newRoomId
)
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
}
}
}
private
boolean
assertRoomSubscriptionExists
(
String
roomId
)
{
if
(!
assertServerRealmStoreExists
(
hostname
))
{
return
false
;
}
RealmRoom
room
=
RealmStore
.
get
(
hostname
).
executeTransactionForRead
(
realm
->
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
RealmRoom
.
ROOM_ID
,
roomId
).
findFirst
());
if
(
room
==
null
)
{
rocketChatCache
.
setSelectedRoomId
(
null
);
return
false
;
}
return
true
;
}
private
void
updateRoomId
(
String
roomId
)
{
this
.
roomId
=
roomId
;
onRoomIdUpdated
();
}
protected
void
onHostnameUpdated
()
{
}
protected
void
onRoomIdUpdated
()
{
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
subscribeToConfigChanges
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
();
if
(
isNotification
)
{
updateHostnameIfNeeded
(
rocketChatCache
.
getSelectedServerHostname
());
updateRoomIdIfNeeded
(
rocketChatCache
.
getSelectedRoomId
());
isNotification
=
false
;
}
}
@Override
protected
void
onPause
()
{
compositeDisposable
.
clear
();
super
.
onPause
();
}
@Override
protected
void
onSaveInstanceState
(
Bundle
outState
)
{
super
.
onSaveInstanceState
(
outState
);
}
private
void
subscribeToConfigChanges
()
{
compositeDisposable
.
add
(
rocketChatCache
.
getSelectedServerHostnamePublisher
()
.
map
(
Optional:
:
get
)
.
distinctUntilChanged
()
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateHostnameIfNeeded
,
Logger:
:
report
)
);
compositeDisposable
.
add
(
rocketChatCache
.
getSelectedRoomIdPublisher
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateRoomIdIfNeeded
,
Logger:
:
report
)
);
}
}
@Override
protected
void
onPause
()
{
compositeDisposable
.
clear
();
super
.
onPause
();
}
@Override
protected
void
onSaveInstanceState
(
Bundle
outState
)
{
super
.
onSaveInstanceState
(
outState
);
}
private
void
subscribeToConfigChanges
()
{
compositeDisposable
.
add
(
RocketChatCache
.
INSTANCE
.
getSelectedServerHostnamePublisher
()
.
map
(
Optional:
:
get
)
.
distinctUntilChanged
()
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateHostnameIfNeeded
,
Logger
.
INSTANCE
::
report
)
);
compositeDisposable
.
add
(
RocketChatCache
.
INSTANCE
.
getSelectedRoomIdPublisher
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateRoomIdIfNeeded
,
Logger
.
INSTANCE
::
report
)
);
}
}
app/src/main/java/chat/rocket/android/activity/LoginActivity.java
View file @
cc23374c
...
...
@@ -16,77 +16,85 @@ import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
* Activity for Login, Sign-up, and Retry connecting...
*/
public
class
LoginActivity
extends
AbstractFragmentActivity
implements
LoginContract
.
View
{
public
static
final
String
KEY_HOSTNAME
=
"hostname"
;
public
static
final
String
KEY_HOSTNAME
=
"hostname"
;
private
LoginContract
.
Presenter
presenter
;
private
LoginContract
.
Presenter
presenter
;
@Override
protected
int
getLayoutContainerForFragment
()
{
return
R
.
id
.
content
;
}
@Override
protected
int
getLayoutContainerForFragment
()
{
return
R
.
id
.
content
;
}
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
String
hostname
=
null
;
Intent
intent
=
getIntent
();
if
(
intent
!=
null
&&
intent
.
getExtras
()
!=
null
)
{
hostname
=
intent
.
getStringExtra
(
KEY_HOSTNAME
);
}
presenter
=
new
LoginPresenter
(
hostname
,
new
SessionInteractor
(
new
RealmSessionRepository
(
hostname
)),
ConnectivityManager
.
getInstance
(
getApplicationContext
())
);
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
presenter
.
bindView
(
this
);
}
@Override
protected
void
onDestroy
()
{
presenter
.
release
();
super
.
onDestroy
();
}
private
void
showFragment
(
Fragment
fragment
,
String
hostname
)
{
setContentView
(
R
.
layout
.
simple_screen
);
injectHostnameArgTo
(
fragment
,
hostname
);
super
.
showFragment
(
fragment
);
}
private
void
injectHostnameArgTo
(
Fragment
fragment
,
String
hostname
)
{
Bundle
args
=
fragment
.
getArguments
();
if
(
args
==
null
)
{
args
=
new
Bundle
();
}
args
.
putString
(
LoginActivity
.
KEY_HOSTNAME
,
hostname
);
fragment
.
setArguments
(
args
);
}
@Override
protected
void
onBackPressedNotHandled
()
{
moveTaskToBack
(
true
);
}
@Override
public
void
showLogin
(
String
hostname
)
{
showFragment
(
new
LoginFragment
(),
hostname
);
}
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
@Override
public
void
showRetryLogin
(
String
hostname
)
{
showFragment
(
new
RetryLoginFragment
(),
hostname
);
}
String
hostname
=
null
;
Intent
intent
=
getIntent
();
if
(
intent
!=
null
&&
intent
.
getExtras
()
!=
null
)
{
hostname
=
intent
.
getStringExtra
(
KEY_HOSTNAME
);
@Override
public
void
closeView
()
{
finish
();
overridePendingTransition
(
android
.
R
.
anim
.
fade_in
,
android
.
R
.
anim
.
fade_out
);
}
presenter
=
new
LoginPresenter
(
hostname
,
new
SessionInteractor
(
new
RealmSessionRepository
(
hostname
)),
ConnectivityManager
.
getInstance
(
getApplicationContext
())
);
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
presenter
.
bindView
(
this
);
}
@Override
protected
void
onDestroy
()
{
presenter
.
release
();
super
.
onDestroy
();
}
private
void
showFragment
(
Fragment
fragment
,
String
hostname
)
{
setContentView
(
R
.
layout
.
simple_screen
);
injectHostnameArgTo
(
fragment
,
hostname
);
super
.
showFragment
(
fragment
);
}
private
void
injectHostnameArgTo
(
Fragment
fragment
,
String
hostname
)
{
Bundle
args
=
fragment
.
getArguments
();
if
(
args
==
null
)
{
args
=
new
Bundle
();
@Override
protected
boolean
onBackPress
()
{
LoginFragment
loginFragment
=
(
LoginFragment
)
getSupportFragmentManager
()
.
findFragmentById
(
getLayoutContainerForFragment
());
loginFragment
.
goBack
();
return
true
;
}
args
.
putString
(
LoginActivity
.
KEY_HOSTNAME
,
hostname
);
fragment
.
setArguments
(
args
);
}
@Override
protected
void
onBackPressedNotHandled
()
{
moveTaskToBack
(
true
);
}
@Override
public
void
showLogin
(
String
hostname
)
{
showFragment
(
new
LoginFragment
(),
hostname
);
}
@Override
public
void
showRetryLogin
(
String
hostname
)
{
showFragment
(
new
RetryLoginFragment
(),
hostname
);
}
@Override
public
void
closeView
()
{
finish
();
overridePendingTransition
(
android
.
R
.
anim
.
fade_in
,
android
.
R
.
anim
.
fade_out
);
}
}
app/src/main/java/chat/rocket/android/activity/LoginPresenter.java
View file @
cc23374c
...
...
@@ -66,7 +66,7 @@ public class LoginPresenter extends BasePresenter<LoginContract.View>
view
.
closeView
();
}
},
Logger:
:
report
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
...
...
app/src/main/java/chat/rocket/android/activity/MainActivity.java
View file @
cc23374c
...
...
@@ -5,7 +5,7 @@ import android.graphics.drawable.Drawable;
import
android.os.Bundle
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.
design.widget.Snackbar
;
import
android.support.
graphics.drawable.AnimatedVectorDrawableCompat
;
import
android.support.v4.app.Fragment
;
import
android.support.v4.content.ContextCompat
;
import
android.support.v4.widget.SlidingPaneLayout
;
...
...
@@ -18,7 +18,9 @@ import android.widget.TextView;
import
com.facebook.drawee.view.SimpleDraweeView
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicReference
;
import
chat.rocket.android.ConnectionStatusManager
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.R
;
import
chat.rocket.android.RocketChatCache
;
...
...
@@ -30,6 +32,7 @@ import chat.rocket.android.helper.KeyboardHelper;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android.service.ConnectivityManagerApi
;
import
chat.rocket.android.widget.RoomToolbar
;
import
chat.rocket.android.widget.helper.DebouncingOnClickListener
;
import
chat.rocket.android.widget.helper.FrescoHelper
;
import
chat.rocket.core.interactors.CanCreateRoomInteractor
;
import
chat.rocket.core.interactors.RoomInteractor
;
...
...
@@ -40,6 +43,8 @@ import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
import
chat.rocket.persistence.realm.repositories.RealmRoomRepository
;
import
chat.rocket.persistence.realm.repositories.RealmSessionRepository
;
import
chat.rocket.persistence.realm.repositories.RealmUserRepository
;
import
de.keyboardsurfer.android.widget.crouton.Configuration
;
import
de.keyboardsurfer.android.widget.crouton.Crouton
;
import
hugo.weaving.DebugLog
;
/**
...
...
@@ -49,7 +54,11 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private
RoomToolbar
toolbar
;
private
SlidingPaneLayout
pane
;
private
MainContract
.
Presenter
presenter
;
private
volatile
Snackbar
statusTicker
;
private
volatile
AtomicReference
<
Crouton
>
croutonStatusTicker
=
new
AtomicReference
<>();
private
View
croutonView
;
private
ImageView
croutonTryAgainImage
;
private
TextView
croutonText
;
private
AnimatedVectorDrawableCompat
tryAgainSpinnerAnimatedDrawable
;
@Override
public
int
getLayoutContainerForFragment
()
{
...
...
@@ -62,6 +71,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
setContentView
(
R
.
layout
.
activity_main
);
toolbar
=
findViewById
(
R
.
id
.
activity_main_toolbar
);
pane
=
findViewById
(
R
.
id
.
sliding_pane
);
loadCroutonViewIfNeeded
();
setupToolbar
();
}
...
...
@@ -71,7 +81,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
ConnectivityManagerApi
connectivityManager
=
ConnectivityManager
.
getInstance
(
getApplicationContext
());
if
(
hostname
==
null
||
presenter
==
null
)
{
String
previousHostname
=
hostname
;
hostname
=
new
RocketChatCache
(
getApplicationContext
())
.
getSelectedServerHostname
();
hostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
if
(
hostname
==
null
)
{
showAddServerScreen
();
}
else
{
...
...
@@ -85,7 +95,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
connectivityManager
.
keepAliveServer
();
presenter
.
bindView
(
this
);
presenter
.
loadSignedInServers
(
hostname
);
roomId
=
new
RocketChatCache
(
getApplicationContext
())
.
getSelectedRoomId
();
roomId
=
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
();
}
}
...
...
@@ -94,8 +104,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
if
(
presenter
!=
null
)
{
presenter
.
release
();
}
// Dismiss any status ticker
if
(
statusTicker
!=
null
)
statusTicker
.
dismiss
();
Crouton
.
cancelAllCroutons
();
super
.
onPause
();
}
...
...
@@ -177,15 +186,12 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
PublicSettingRepository
publicSettingRepository
=
new
RealmPublicSettingRepository
(
hostname
);
RocketChatCache
rocketChatCache
=
new
RocketChatCache
(
this
);
presenter
=
new
MainPresenter
(
roomInteractor
,
createRoomInteractor
,
sessionInteractor
,
new
MethodCallHelper
(
this
,
hostname
),
ConnectivityManager
.
getInstance
(
getApplicationContext
()),
rocketChatCache
,
publicSettingRepository
);
...
...
@@ -194,12 +200,12 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
presenter
.
bindView
(
this
);
presenter
.
loadSignedInServers
(
hostname
);
roomId
=
rocketChatCache
.
getSelectedRoomId
();
roomId
=
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
();
}
private
void
updateSidebarMainFragment
()
{
closeSidebarIfNeeded
();
String
selectedServerHostname
=
new
RocketChatCache
(
this
)
.
getSelectedServerHostname
();
String
selectedServerHostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
Fragment
sidebarFragment
=
findFragmentByTag
(
selectedServerHostname
);
if
(
sidebarFragment
==
null
)
{
sidebarFragment
=
SidebarMainFragment
.
create
(
selectedServerHostname
);
...
...
@@ -250,34 +256,84 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
}
@Override
public
synchronized
void
showConnectionError
()
{
dismissStatusTickerIfShowing
();
statusTicker
=
Snackbar
.
make
(
findViewById
(
getLayoutContainerForFragment
()),
R
.
string
.
fragment_retry_login_error_title
,
Snackbar
.
LENGTH_INDEFINITE
)
.
setAction
(
R
.
string
.
fragment_retry_login_retry_title
,
view
->
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
());
statusTicker
.
show
();
public
void
showConnectionError
()
{
ConnectionStatusManager
.
INSTANCE
.
setConnectionError
(
this
::
showConnectionErrorCrouton
);
}
@Override
public
synchronized
void
showConnecting
()
{
dismissStatusTickerIfShowing
();
statusTicker
=
Snackbar
.
make
(
findViewById
(
getLayoutContainerForFragment
()),
R
.
string
.
server_config_activity_authenticating
,
Snackbar
.
LENGTH_INDEFINITE
);
statusTicker
.
show
();
public
void
showConnecting
()
{
ConnectionStatusManager
.
INSTANCE
.
setConnecting
(
this
::
showConnectingCrouton
);
}
@Override
public
synchronized
void
showConnectionOk
()
{
dismissStatusTickerIfShowing
();
public
void
showConnectionOk
()
{
ConnectionStatusManager
.
INSTANCE
.
setOnline
(
this
::
dismissStatusTickerIfShowing
);
}
private
void
showConnectingCrouton
(
boolean
success
)
{
if
(
success
)
{
croutonText
.
setText
(
R
.
string
.
server_config_activity_authenticating
);
croutonTryAgainImage
.
setOnClickListener
(
null
);
tryAgainSpinnerAnimatedDrawable
.
start
();
Crouton
.
cancelAllCroutons
();
updateCrouton
();
croutonStatusTicker
.
get
().
show
();
}
}
private
void
showConnectionErrorCrouton
(
boolean
success
)
{
if
(
success
)
{
tryAgainSpinnerAnimatedDrawable
.
stop
();
croutonText
.
setText
(
R
.
string
.
fragment_retry_login_error_title
);
croutonTryAgainImage
.
setOnClickListener
(
new
DebouncingOnClickListener
()
{
@Override
public
void
doClick
(
View
v
)
{
retryConnection
();
}
});
Crouton
.
cancelAllCroutons
();
updateCrouton
();
croutonStatusTicker
.
get
().
show
();
}
}
private
void
dismissStatusTickerIfShowing
()
{
if
(
statusTicker
!=
null
)
{
statusTicker
.
dismiss
();
private
void
loadCroutonViewIfNeeded
()
{
if
(
croutonView
==
null
)
{
croutonView
=
LayoutInflater
.
from
(
this
).
inflate
(
R
.
layout
.
crouton_status_ticker
,
null
);
croutonTryAgainImage
=
croutonView
.
findViewById
(
R
.
id
.
try_again_image
);
croutonText
=
croutonView
.
findViewById
(
R
.
id
.
text_view_status
);
tryAgainSpinnerAnimatedDrawable
=
AnimatedVectorDrawableCompat
.
create
(
this
,
R
.
drawable
.
ic_loading_animated
);
croutonTryAgainImage
.
setImageDrawable
(
tryAgainSpinnerAnimatedDrawable
);
updateCrouton
();
}
}
private
void
updateCrouton
()
{
Configuration
configuration
=
new
Configuration
.
Builder
()
.
setDuration
(
Configuration
.
DURATION_INFINITE
).
build
();
Crouton
crouton
=
Crouton
.
make
(
this
,
croutonView
,
getLayoutContainerForFragment
())
.
setConfiguration
(
configuration
);
croutonStatusTicker
.
set
(
crouton
);
}
private
void
dismissStatusTickerIfShowing
(
boolean
success
)
{
if
(
success
&&
croutonStatusTicker
.
get
()
!=
null
)
{
croutonStatusTicker
.
get
().
hide
();
}
}
private
void
retryConnection
()
{
croutonStatusTicker
.
set
(
null
);
showConnecting
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
();
}
@Override
public
void
showSignedInServers
(
List
<
Pair
<
String
,
Pair
<
String
,
String
>>>
serverList
)
{
final
SlidingPaneLayout
subPane
=
findViewById
(
R
.
id
.
sub_sliding_pane
);
...
...
@@ -334,21 +390,20 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
Fragment
fragment
=
getSupportFragmentManager
().
findFragmentById
(
getLayoutContainerForFragment
());
if
(
fragment
!=
null
&&
fragment
instanceof
RoomFragment
)
{
RoomFragment
roomFragment
=
(
RoomFragment
)
fragment
;
roomFragment
.
loadMessages
();
roomFragment
.
loadM
issedM
essages
();
}
}
private
void
changeServerIfNeeded
(
String
serverHostname
)
{
if
(!
hostname
.
equalsIgnoreCase
(
serverHostname
))
{
RocketChatCache
rocketChatCache
=
new
RocketChatCache
(
getApplicationContext
());
rocketChatCache
.
setSelectedServerHostname
(
serverHostname
);
RocketChatCache
.
INSTANCE
.
setSelectedServerHostname
(
serverHostname
);
}
}
@DebugLog
public
void
onLogout
()
{
presenter
.
prepareToLogout
();
if
(
new
RocketChatCache
(
getApplicationContext
())
.
getSelectedServerHostname
()
==
null
)
{
if
(
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
()
==
null
)
{
finish
();
LaunchUtil
.
showMainActivity
(
this
);
}
else
{
...
...
app/src/main/java/chat/rocket/android/activity/MainPresenter.java
View file @
cc23374c
...
...
@@ -41,7 +41,6 @@ public class MainPresenter extends BasePresenter<MainContract.View>
private
final
SessionInteractor
sessionInteractor
;
private
final
MethodCallHelper
methodCallHelper
;
private
final
ConnectivityManagerApi
connectivityManagerApi
;
private
final
RocketChatCache
rocketChatCache
;
private
final
PublicSettingRepository
publicSettingRepository
;
public
MainPresenter
(
RoomInteractor
roomInteractor
,
...
...
@@ -49,13 +48,12 @@ public class MainPresenter extends BasePresenter<MainContract.View>
SessionInteractor
sessionInteractor
,
MethodCallHelper
methodCallHelper
,
ConnectivityManagerApi
connectivityManagerApi
,
RocketChatCache
rocketChatCache
,
PublicSettingRepository
publicSettingRepository
)
{
PublicSettingRepository
publicSettingRepository
)
{
this
.
roomInteractor
=
roomInteractor
;
this
.
canCreateRoomInteractor
=
canCreateRoomInteractor
;
this
.
sessionInteractor
=
sessionInteractor
;
this
.
methodCallHelper
=
methodCallHelper
;
this
.
connectivityManagerApi
=
connectivityManagerApi
;
this
.
rocketChatCache
=
rocketChatCache
;
this
.
publicSettingRepository
=
publicSettingRepository
;
}
...
...
@@ -97,12 +95,13 @@ public class MainPresenter extends BasePresenter<MainContract.View>
subscribeToNetworkChanges
();
subscribeToUnreadCount
();
subscribeToSession
();
setUserOnline
();
}
@Override
public
void
release
()
{
setUserAway
();
if
(
RocketChatCache
.
INSTANCE
.
getSessionToken
()
!=
null
)
{
setUserAway
();
}
super
.
release
();
}
...
...
@@ -120,7 +119,7 @@ public class MainPresenter extends BasePresenter<MainContract.View>
view
.
showHome
();
}
},
Logger:
:
report
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
...
...
@@ -157,13 +156,13 @@ public class MainPresenter extends BasePresenter<MainContract.View>
String
logoUrl
=
(
jsonObject
.
has
(
"url"
))
?
jsonObject
.
optString
(
"url"
)
:
jsonObject
.
optString
(
"defaultUrl"
);
String
siteName
=
serverInfoPair
.
second
;
rocketChatCache
.
addHostname
(
hostname
.
toLowerCase
(),
logoUrl
,
siteName
);
return
rocketChatCache
.
getServerList
();
RocketChatCache
.
INSTANCE
.
addHostname
(
hostname
.
toLowerCase
(),
logoUrl
,
siteName
);
return
RocketChatCache
.
INSTANCE
.
getServerList
();
}
private
void
openRoom
()
{
String
hostname
=
rocketChatCache
.
getSelectedServerHostname
();
String
roomId
=
rocketChatCache
.
getSelectedRoomId
();
String
hostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
String
roomId
=
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
();
if
(
roomId
==
null
||
roomId
.
length
()
==
0
)
{
view
.
showHome
();
...
...
@@ -183,7 +182,7 @@ public class MainPresenter extends BasePresenter<MainContract.View>
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
pair
->
view
.
showUnreadCount
(
pair
.
first
,
pair
.
second
),
Logger:
:
report
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
...
...
@@ -211,10 +210,11 @@ public class MainPresenter extends BasePresenter<MainContract.View>
view
.
showConnecting
();
return
;
}
view
.
showConnectionOk
();
// TODO: Should we remove below and above calls to view?
// view.showConnectionOk();
RocketChatCache
.
INSTANCE
.
setSessionToken
(
session
.
getToken
());
},
Logger:
:
report
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
...
...
@@ -227,17 +227,21 @@ public class MainPresenter extends BasePresenter<MainContract.View>
.
subscribe
(
connectivity
->
{
if
(
connectivity
.
state
==
ServerConnectivity
.
STATE_CONNECTED
)
{
view
.
showConnectionOk
();
view
.
refreshRoom
();
//TODO: notify almost connected or something like that.
// view.showConnectionOk
();
}
else
if
(
connectivity
.
state
==
ServerConnectivity
.
STATE_DISCONNECTED
)
{
if
(
connectivity
.
code
==
DDPClient
.
REASON_NETWORK_ERROR
)
{
view
.
showConnectionError
();
}
}
else
if
(
connectivity
.
state
==
ServerConnectivity
.
STATE_SESSION_ESTABLISHED
)
{
setUserOnline
();
view
.
refreshRoom
();
view
.
showConnectionOk
();
}
else
{
view
.
showConnecting
();
}
},
Logger:
:
report
RCLog:
:
e
);
addSubscription
(
disposable
);
...
...
app/src/main/java/chat/rocket/android/api/MethodCallHelper.java
View file @
cc23374c
...
...
@@ -7,6 +7,7 @@ import org.json.JSONArray;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
java.util.Iterator
;
import
java.util.UUID
;
import
bolts.Continuation
;
...
...
@@ -14,6 +15,7 @@ import bolts.Task;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.helper.CheckSum
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android_ddp.DDPClient
;
import
chat.rocket.android_ddp.DDPClientCallback
;
...
...
@@ -32,564 +34,611 @@ import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser;
import
chat.rocket.persistence.realm.models.internal.MethodCall
;
import
chat.rocket.persistence.realm.models.internal.RealmSession
;
import
hugo.weaving.DebugLog
;
import
io.realm.RealmQuery
;
import
okhttp3.HttpUrl
;
/**
* Utility class for creating/handling MethodCall or RPC.
*
*
<p>
* TODO: separate method into several manager classes (SubscriptionManager, MessageManager, ...).
*/
public
class
MethodCallHelper
{
protected
static
final
long
TIMEOUT_MS
=
20000
;
protected
static
final
Continuation
<
String
,
Task
<
JSONObject
>>
CONVERT_TO_JSON_OBJECT
=
task
->
Task
.
forResult
(
new
JSONObject
(
task
.
getResult
()));
protected
static
final
Continuation
<
String
,
Task
<
JSONArray
>>
CONVERT_TO_JSON_ARRAY
=
task
->
Task
.
forResult
(
new
JSONArray
(
task
.
getResult
()));
protected
final
Context
context
;
protected
final
RealmHelper
realmHelper
;
/**
* initialize with Context and hostname.
*/
public
MethodCallHelper
(
Context
context
,
String
hostname
)
{
this
.
context
=
context
.
getApplicationContext
();
this
.
realmHelper
=
RealmStore
.
getOrCreate
(
hostname
);
}
/**
* initialize with RealmHelper and DDPClient.
*/
public
MethodCallHelper
(
RealmHelper
realmHelper
)
{
this
.
context
=
null
;
this
.
realmHelper
=
realmHelper
;
}
public
MethodCallHelper
(
Context
context
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
.
getApplicationContext
();
this
.
realmHelper
=
realmHelper
;
}
@DebugLog
private
Task
<
String
>
executeMethodCall
(
String
methodName
,
String
param
,
long
timeout
)
{
if
(
DDPClient
.
get
()
!=
null
)
{
return
DDPClient
.
get
().
rpc
(
UUID
.
randomUUID
().
toString
(),
methodName
,
param
,
timeout
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
result
))
.
continueWithTask
(
task_
->
{
if
(
task_
.
isFaulted
())
{
return
Task
.
forError
(
task_
.
getError
());
}
return
Task
.
forResult
(
task_
.
getResult
());
});
}
else
{
return
MethodCall
.
execute
(
realmHelper
,
methodName
,
param
,
timeout
)
.
onSuccessTask
(
task
->
{
ConnectivityManager
.
getInstance
(
context
.
getApplicationContext
())
.
keepAliveServer
();
return
task
;
});
}
}
private
Task
<
String
>
injectErrorHandler
(
Task
<
String
>
task
)
{
return
task
.
continueWithTask
(
_task
->
{
if
(
_task
.
isFaulted
())
{
Exception
exception
=
_task
.
getError
();
if
(
exception
instanceof
MethodCall
.
Error
||
exception
instanceof
DDPClientCallback
.
RPC
.
Error
)
{
String
errMessageJson
;
if
(
exception
instanceof
DDPClientCallback
.
RPC
.
Error
)
{
errMessageJson
=
((
DDPClientCallback
.
RPC
.
Error
)
exception
).
error
.
toString
();
}
else
{
errMessageJson
=
exception
.
getMessage
();
}
if
(
TextUtils
.
isEmpty
(
errMessageJson
))
{
return
Task
.
forError
(
exception
);
}
String
errType
=
new
JSONObject
(
errMessageJson
).
optString
(
"error"
);
String
errMessage
=
new
JSONObject
(
errMessageJson
).
getString
(
"message"
);
if
(
TwoStepAuthException
.
TYPE
.
equals
(
errType
))
{
return
Task
.
forError
(
new
TwoStepAuthException
(
errMessage
));
}
return
Task
.
forError
(
new
Exception
(
errMessage
));
}
else
if
(
exception
instanceof
DDPClientCallback
.
RPC
.
Timeout
)
{
return
Task
.
forError
(
new
MethodCall
.
Timeout
());
}
else
if
(
exception
instanceof
DDPClientCallback
.
Closed
)
{
return
Task
.
forError
(
new
Exception
(
"Oops, your connection seems off..."
));
protected
static
final
long
TIMEOUT_MS
=
20000
;
protected
static
final
Continuation
<
String
,
Task
<
JSONObject
>>
CONVERT_TO_JSON_OBJECT
=
task
->
Task
.
forResult
(
new
JSONObject
(
task
.
getResult
()));
protected
static
final
Continuation
<
String
,
Task
<
JSONArray
>>
CONVERT_TO_JSON_ARRAY
=
task
->
Task
.
forResult
(
new
JSONArray
(
task
.
getResult
()));
protected
final
Context
context
;
protected
final
RealmHelper
realmHelper
;
/**
* initialize with Context and hostname.
*/
public
MethodCallHelper
(
Context
context
,
String
hostname
)
{
this
.
context
=
context
.
getApplicationContext
();
this
.
realmHelper
=
RealmStore
.
getOrCreate
(
hostname
);
}
/**
* initialize with RealmHelper and DDPClient.
*/
public
MethodCallHelper
(
RealmHelper
realmHelper
)
{
this
.
context
=
null
;
this
.
realmHelper
=
realmHelper
;
}
public
MethodCallHelper
(
Context
context
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
.
getApplicationContext
();
this
.
realmHelper
=
realmHelper
;
}
@DebugLog
private
Task
<
String
>
executeMethodCall
(
String
methodName
,
String
param
,
long
timeout
)
{
if
(
DDPClient
.
get
()
!=
null
)
{
return
DDPClient
.
get
().
rpc
(
UUID
.
randomUUID
().
toString
(),
methodName
,
param
,
timeout
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
result
))
.
continueWithTask
(
task_
->
{
if
(
task_
.
isFaulted
())
{
return
Task
.
forError
(
task_
.
getError
());
}
return
Task
.
forResult
(
task_
.
getResult
());
});
}
else
{
return
Task
.
forError
(
exception
);
return
MethodCall
.
execute
(
realmHelper
,
methodName
,
param
,
timeout
)
.
onSuccessTask
(
task
->
{
ConnectivityManager
.
getInstance
(
context
.
getApplicationContext
())
.
keepAliveServer
();
return
task
;
});
}
}
else
{
return
_task
;
}
});
}
protected
final
Task
<
String
>
call
(
String
methodName
,
long
timeout
)
{
return
injectErrorHandler
(
executeMethodCall
(
methodName
,
null
,
timeout
));
}
protected
final
Task
<
String
>
call
(
String
methodName
,
long
timeout
,
ParamBuilder
paramBuilder
)
{
try
{
final
JSONArray
params
=
paramBuilder
.
buildParam
();
return
injectErrorHandler
(
executeMethodCall
(
methodName
,
params
!=
null
?
params
.
toString
()
:
null
,
timeout
));
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
/**
* Register RealmUser.
*/
public
Task
<
String
>
registerUser
(
final
String
name
,
final
String
email
,
final
String
password
,
final
String
confirmPassword
)
{
return
call
(
"registerUser"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"name"
,
name
)
.
put
(
"email"
,
email
)
.
put
(
"pass"
,
password
)
.
put
(
"confirm-pass"
,
confirmPassword
)));
// nothing to do.
}
private
Task
<
Void
>
saveToken
(
Task
<
String
>
task
)
{
return
realmHelper
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
RealmSession
.
class
,
new
JSONObject
()
.
put
(
"sessionId"
,
RealmSession
.
DEFAULT_ID
)
.
put
(
"token"
,
task
.
getResult
())
.
put
(
"tokenVerified"
,
true
)
.
put
(
"error"
,
JSONObject
.
NULL
)
));
}
/**
* set current user's name.
*/
public
Task
<
String
>
setUsername
(
final
String
username
)
{
return
call
(
"setUsername"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
username
));
}
public
Task
<
Void
>
joinDefaultChannels
()
{
return
call
(
"joinDefaultChannels"
,
TIMEOUT_MS
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
joinRoom
(
String
roomId
)
{
return
call
(
"joinRoom"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
/**
* Login with username/email and password.
*/
public
Task
<
Void
>
loginWithEmail
(
final
String
usernameOrEmail
,
final
String
password
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
param
=
new
JSONObject
();
if
(
Patterns
.
EMAIL_ADDRESS
.
matcher
(
usernameOrEmail
).
matches
())
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"email"
,
usernameOrEmail
));
}
else
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"username"
,
usernameOrEmail
));
}
param
.
put
(
"password"
,
new
JSONObject
()
.
put
(
"digest"
,
CheckSum
.
sha256
(
password
))
.
put
(
"algorithm"
,
"sha-256"
));
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
public
Task
<
Void
>
loginWithLdap
(
final
String
username
,
final
String
password
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
param
=
new
JSONObject
();
param
.
put
(
"ldap"
,
true
);
param
.
put
(
"username"
,
username
);
param
.
put
(
"ldapPass"
,
password
);
param
.
put
(
"ldapOptions"
,
new
JSONObject
());
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Login with OAuth.
*/
public
Task
<
Void
>
loginWithOAuth
(
final
String
credentialToken
,
final
String
credentialSecret
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"oauth"
,
new
JSONObject
()
.
put
(
"credentialToken"
,
credentialToken
)
.
put
(
"credentialSecret"
,
credentialSecret
))
)).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Login with token.
*/
public
Task
<
Void
>
loginWithToken
(
final
String
token
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"resume"
,
token
)
)).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
)
.
continueWithTask
(
task
->
{
if
(
task
.
isFaulted
())
{
RealmSession
.
logError
(
realmHelper
,
task
.
getError
());
}
return
task
;
});
}
public
Task
<
Void
>
twoStepCodeLogin
(
final
String
usernameOrEmail
,
final
String
password
,
final
String
twoStepCode
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
loginParam
=
new
JSONObject
();
if
(
Patterns
.
EMAIL_ADDRESS
.
matcher
(
usernameOrEmail
).
matches
())
{
loginParam
.
put
(
"user"
,
new
JSONObject
().
put
(
"email"
,
usernameOrEmail
));
}
else
{
loginParam
.
put
(
"user"
,
new
JSONObject
().
put
(
"username"
,
usernameOrEmail
));
}
loginParam
.
put
(
"password"
,
new
JSONObject
()
.
put
(
"digest"
,
CheckSum
.
sha256
(
password
))
.
put
(
"algorithm"
,
"sha-256"
));
JSONObject
twoStepParam
=
new
JSONObject
();
twoStepParam
.
put
(
"login"
,
loginParam
);
twoStepParam
.
put
(
"code"
,
twoStepCode
);
JSONObject
param
=
new
JSONObject
();
param
.
put
(
"totp"
,
twoStepParam
);
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Logout.
*/
public
Task
<
Void
>
logout
()
{
return
call
(
"logout"
,
TIMEOUT_MS
).
onSuccessTask
(
task
->
{
if
(
task
.
isFaulted
())
{
return
Task
.
forError
(
task
.
getError
());
}
return
null
;
});
}
/**
* request "subscriptions/get".
*/
public
Task
<
Void
>
getRoomSubscriptions
()
{
return
call
(
"subscriptions/get"
,
TIMEOUT_MS
).
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
result
=
task
.
getResult
();
try
{
for
(
int
i
=
0
;
i
<
result
.
length
();
i
++)
{
RealmRoom
.
customizeJson
(
result
.
getJSONObject
(
i
));
}
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmRoom
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmRoom
.
class
,
result
);
return
null
;
});
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
});
}
/**
* Load messages for room.
*/
public
Task
<
JSONArray
>
loadHistory
(
final
String
roomId
,
final
long
timestamp
,
final
int
count
,
final
long
lastSeen
)
{
return
call
(
"loadHistory"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
roomId
)
.
put
(
timestamp
>
0
?
new
JSONObject
().
put
(
"$date"
,
timestamp
)
:
JSONObject
.
NULL
)
.
put
(
count
)
.
put
(
lastSeen
>
0
?
new
JSONObject
().
put
(
"$date"
,
lastSeen
)
:
JSONObject
.
NULL
)
).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
JSONObject
result
=
task
.
getResult
();
final
JSONArray
messages
=
result
.
getJSONArray
(
"messages"
);
for
(
int
i
=
0
;
i
<
messages
.
length
();
i
++)
{
RealmMessage
.
customizeJson
(
messages
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
if
(
timestamp
==
0
)
{
realm
.
where
(
RealmMessage
.
class
)
.
equalTo
(
"rid"
,
roomId
)
.
equalTo
(
"syncstate"
,
SyncState
.
SYNCED
)
.
findAll
().
deleteAllFromRealm
();
}
if
(
messages
.
length
()
>
0
)
{
realm
.
createOrUpdateAllFromJson
(
RealmMessage
.
class
,
messages
);
private
Task
<
String
>
injectErrorHandler
(
Task
<
String
>
task
)
{
return
task
.
continueWithTask
(
_task
->
{
if
(
_task
.
isFaulted
())
{
Exception
exception
=
_task
.
getError
();
if
(
exception
instanceof
MethodCall
.
Error
||
exception
instanceof
DDPClientCallback
.
RPC
.
Error
)
{
String
errMessageJson
;
if
(
exception
instanceof
DDPClientCallback
.
RPC
.
Error
)
{
errMessageJson
=
((
DDPClientCallback
.
RPC
.
Error
)
exception
).
error
.
toString
();
}
else
{
errMessageJson
=
exception
.
getMessage
();
}
if
(
TextUtils
.
isEmpty
(
errMessageJson
))
{
return
Task
.
forError
(
exception
);
}
String
errType
=
new
JSONObject
(
errMessageJson
).
optString
(
"error"
);
String
errMessage
=
new
JSONObject
(
errMessageJson
).
getString
(
"message"
);
if
(
TwoStepAuthException
.
TYPE
.
equals
(
errType
))
{
return
Task
.
forError
(
new
TwoStepAuthException
(
errMessage
));
}
return
Task
.
forError
(
new
Exception
(
errMessage
));
}
else
if
(
exception
instanceof
DDPClientCallback
.
RPC
.
Timeout
)
{
return
Task
.
forError
(
new
MethodCall
.
Timeout
());
}
else
if
(
exception
instanceof
DDPClientCallback
.
Closed
)
{
return
Task
.
forError
(
new
Exception
(
exception
.
getMessage
()));
}
else
{
return
Task
.
forError
(
exception
);
}
}
else
{
return
_task
;
}
return
null
;
}).
onSuccessTask
(
_task
->
Task
.
forResult
(
messages
));
});
}
/**
* update user's status.
*/
public
Task
<
Void
>
setUserStatus
(
final
String
status
)
{
return
call
(
"UserPresence:setDefaultStatus"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
status
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
setUserPresence
(
final
String
status
)
{
return
call
(
"UserPresence:"
+
status
,
TIMEOUT_MS
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
JSONObject
>
getUsersOfRoom
(
final
String
roomId
,
final
boolean
showAll
)
{
return
call
(
"getUsersOfRoom"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
).
put
(
showAll
))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
);
}
public
Task
<
Void
>
createChannel
(
final
String
name
,
final
boolean
readOnly
)
{
return
call
(
"createChannel"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
name
)
.
put
(
new
JSONArray
())
.
put
(
readOnly
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
createPrivateGroup
(
final
String
name
,
final
boolean
readOnly
)
{
return
call
(
"createPrivateGroup"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
name
)
.
put
(
new
JSONArray
())
.
put
(
readOnly
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
String
>
createDirectMessage
(
final
String
username
)
{
return
call
(
"createDirectMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
username
))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"rid"
)));
}
/**
* send message.
*/
public
Task
<
Void
>
sendMessage
(
String
messageId
,
String
roomId
,
String
msg
,
long
editedAt
)
{
try
{
JSONObject
messageJson
=
new
JSONObject
()
.
put
(
"_id"
,
messageId
)
.
put
(
"rid"
,
roomId
)
.
put
(
"msg"
,
msg
);
if
(
editedAt
==
0
)
{
return
sendMessage
(
messageJson
);
}
else
{
return
updateMessage
(
messageJson
);
}
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
public
Task
<
Void
>
deleteMessage
(
String
messageID
)
{
try
{
JSONObject
messageJson
=
new
JSONObject
()
.
put
(
"_id"
,
messageID
);
return
deleteMessage
(
messageJson
);
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
/**
* Send message object.
*/
private
Task
<
Void
>
sendMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"sendMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
private
Task
<
Void
>
updateMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"updateMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
private
Task
<
Void
>
deleteMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"deleteMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
/**
* mark all messages are read in the room.
*/
public
Task
<
Void
>
readMessages
(
final
String
roomId
)
{
return
call
(
"readMessages"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
getPublicSettings
(
String
currentHostname
)
{
return
call
(
"public-settings/get"
,
TIMEOUT_MS
)
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
settings
=
task
.
getResult
();
String
siteUrl
=
null
;
String
siteName
=
null
;
for
(
int
i
=
0
;
i
<
settings
.
length
();
i
++)
{
JSONObject
jsonObject
=
settings
.
getJSONObject
(
i
);
RealmPublicSetting
.
customizeJson
(
jsonObject
);
if
(
isPublicSetting
(
jsonObject
,
PublicSettingsConstants
.
General
.
SITE_URL
))
{
siteUrl
=
jsonObject
.
getString
(
RealmPublicSetting
.
VALUE
);
}
else
if
(
isPublicSetting
(
jsonObject
,
PublicSettingsConstants
.
General
.
SITE_NAME
))
{
siteName
=
jsonObject
.
getString
(
RealmPublicSetting
.
VALUE
);
}
protected
final
Task
<
String
>
call
(
String
methodName
,
long
timeout
)
{
return
injectErrorHandler
(
executeMethodCall
(
methodName
,
null
,
timeout
));
}
protected
final
Task
<
String
>
call
(
String
methodName
,
long
timeout
,
ParamBuilder
paramBuilder
)
{
try
{
final
JSONArray
params
=
paramBuilder
.
buildParam
();
return
injectErrorHandler
(
executeMethodCall
(
methodName
,
params
!=
null
?
params
.
toString
()
:
null
,
timeout
));
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
/**
* Register RealmUser.
*/
public
Task
<
String
>
registerUser
(
final
String
name
,
final
String
email
,
final
String
password
,
final
String
confirmPassword
)
{
return
call
(
"registerUser"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"name"
,
name
)
.
put
(
"email"
,
email
)
.
put
(
"pass"
,
password
)
.
put
(
"confirm-pass"
,
confirmPassword
)));
// nothing to do.
}
private
Task
<
Void
>
saveToken
(
Task
<
String
>
task
)
{
return
realmHelper
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
RealmSession
.
class
,
new
JSONObject
()
.
put
(
"sessionId"
,
RealmSession
.
DEFAULT_ID
)
.
put
(
"token"
,
task
.
getResult
())
.
put
(
"tokenVerified"
,
true
)
.
put
(
"error"
,
JSONObject
.
NULL
)
));
}
/**
* set current user's name.
*/
public
Task
<
String
>
setUsername
(
final
String
username
)
{
return
call
(
"setUsername"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
username
));
}
public
Task
<
Void
>
joinDefaultChannels
()
{
return
call
(
"joinDefaultChannels"
,
TIMEOUT_MS
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
joinRoom
(
String
roomId
)
{
return
call
(
"joinRoom"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
/**
* Login with username/email and password.
*/
public
Task
<
Void
>
loginWithEmail
(
final
String
usernameOrEmail
,
final
String
password
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
param
=
new
JSONObject
();
if
(
Patterns
.
EMAIL_ADDRESS
.
matcher
(
usernameOrEmail
).
matches
())
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"email"
,
usernameOrEmail
));
}
else
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"username"
,
usernameOrEmail
));
}
}
if
(
siteName
!=
null
&&
siteUrl
!=
null
)
{
HttpUrl
httpSiteUrl
=
HttpUrl
.
parse
(
siteUrl
);
if
(
httpSiteUrl
!=
null
)
{
String
host
=
httpSiteUrl
.
host
();
RocketChatCache
rocketChatCache
=
new
RocketChatCache
(
context
);
rocketChatCache
.
addHostnameSiteUrl
(
host
,
currentHostname
);
rocketChatCache
.
addHostSiteName
(
currentHostname
,
siteName
);
param
.
put
(
"password"
,
new
JSONObject
()
.
put
(
"digest"
,
CheckSum
.
sha256
(
password
))
.
put
(
"algorithm"
,
"sha-256"
));
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
public
Task
<
Void
>
loginWithLdap
(
final
String
username
,
final
String
password
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
param
=
new
JSONObject
();
param
.
put
(
"ldap"
,
true
);
param
.
put
(
"username"
,
username
);
param
.
put
(
"ldapPass"
,
password
);
param
.
put
(
"ldapOptions"
,
new
JSONObject
());
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Login with OAuth.
*/
public
Task
<
Void
>
loginWithOAuth
(
final
String
credentialToken
,
final
String
credentialSecret
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"oauth"
,
new
JSONObject
()
.
put
(
"credentialToken"
,
credentialToken
)
.
put
(
"credentialSecret"
,
credentialSecret
))
)).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Login with token.
*/
public
Task
<
Void
>
loginWithToken
(
final
String
token
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
new
JSONObject
()
.
put
(
"resume"
,
token
)
)).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
)
.
continueWithTask
(
task
->
{
if
(
task
.
isFaulted
())
{
RealmSession
.
logError
(
realmHelper
,
task
.
getError
());
}
return
task
;
});
}
public
Task
<
Void
>
twoStepCodeLogin
(
final
String
usernameOrEmail
,
final
String
password
,
final
String
twoStepCode
)
{
return
call
(
"login"
,
TIMEOUT_MS
,
()
->
{
JSONObject
loginParam
=
new
JSONObject
();
if
(
Patterns
.
EMAIL_ADDRESS
.
matcher
(
usernameOrEmail
).
matches
())
{
loginParam
.
put
(
"user"
,
new
JSONObject
().
put
(
"email"
,
usernameOrEmail
));
}
else
{
loginParam
.
put
(
"user"
,
new
JSONObject
().
put
(
"username"
,
usernameOrEmail
));
}
}
loginParam
.
put
(
"password"
,
new
JSONObject
()
.
put
(
"digest"
,
CheckSum
.
sha256
(
password
))
.
put
(
"algorithm"
,
"sha-256"
));
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmPublicSetting
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmPublicSetting
.
class
,
settings
);
return
null
;
});
});
}
private
boolean
isPublicSetting
(
JSONObject
jsonObject
,
String
id
)
{
return
jsonObject
.
optString
(
RealmPublicSetting
.
ID
).
equalsIgnoreCase
(
id
);
}
public
Task
<
Void
>
getPermissions
()
{
return
call
(
"permissions/get"
,
TIMEOUT_MS
)
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
permissions
=
task
.
getResult
();
for
(
int
i
=
0
;
i
<
permissions
.
length
();
i
++)
{
RealmPermission
.
customizeJson
(
permissions
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmPermission
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmPermission
.
class
,
permissions
);
return
null
;
});
});
}
public
Task
<
Void
>
getRoomRoles
(
final
String
roomId
)
{
return
call
(
"getRoomRoles"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
roomRoles
=
task
.
getResult
();
for
(
int
i
=
0
;
i
<
roomRoles
.
length
();
i
++)
{
RealmRoomRole
.
customizeJson
(
roomRoles
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmRoomRole
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmRoomRole
.
class
,
roomRoles
);
JSONObject
twoStepParam
=
new
JSONObject
();
twoStepParam
.
put
(
"login"
,
loginParam
);
twoStepParam
.
put
(
"code"
,
twoStepCode
);
JSONObject
param
=
new
JSONObject
();
param
.
put
(
"totp"
,
twoStepParam
);
return
new
JSONArray
().
put
(
param
);
}).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"token"
)))
.
onSuccessTask
(
this
::
saveToken
);
}
/**
* Logout.
*/
public
Task
<
Void
>
logout
()
{
return
call
(
"logout"
,
TIMEOUT_MS
).
onSuccessTask
(
task
->
{
if
(
task
.
isFaulted
())
{
return
Task
.
forError
(
task
.
getError
());
}
return
null
;
});
});
}
public
Task
<
Void
>
searchSpotlightUsers
(
String
term
)
{
return
searchSpotlight
(
RealmSpotlightUser
.
class
,
"users"
,
term
);
}
public
Task
<
Void
>
searchSpotlightRooms
(
String
term
)
{
return
searchSpotlight
(
RealmSpotlightRoom
.
class
,
"rooms"
,
term
);
}
public
Task
<
Void
>
searchSpotlight
(
String
term
)
{
return
call
(
"spotlight"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
term
)
.
put
(
JSONObject
.
NULL
)
.
put
(
new
JSONObject
().
put
(
"rooms"
,
true
).
put
(
"users"
,
true
))
).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
String
jsonString
=
null
;
final
JSONObject
result
=
task
.
getResult
();
JSONArray
roomJsonArray
=
(
JSONArray
)
result
.
get
(
"rooms"
);
int
roomTotal
=
roomJsonArray
.
length
();
if
(
roomTotal
>
0
)
{
for
(
int
i
=
0
;
i
<
roomTotal
;
++
i
)
{
RealmSpotlight
.
Companion
.
customizeRoomJSONObject
(
roomJsonArray
.
getJSONObject
(
i
));
}
jsonString
=
roomJsonArray
.
toString
();
}
JSONArray
userJsonArray
=
(
JSONArray
)
result
.
get
(
"users"
);
int
usersTotal
=
userJsonArray
.
length
();
if
(
usersTotal
>
0
)
{
for
(
int
i
=
0
;
i
<
usersTotal
;
++
i
)
{
RealmSpotlight
.
Companion
.
customizeUserJSONObject
(
userJsonArray
.
getJSONObject
(
i
));
}
}
/**
* request "subscriptions/get".
*/
public
Task
<
Void
>
getRoomSubscriptions
()
{
return
call
(
"subscriptions/get"
,
TIMEOUT_MS
).
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
result
=
task
.
getResult
();
try
{
for
(
int
i
=
0
;
i
<
result
.
length
();
i
++)
{
RealmRoom
.
customizeJson
(
result
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmRoom
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmRoom
.
class
,
result
);
JSONObject
openedRooms
=
RocketChatCache
.
INSTANCE
.
getOpenedRooms
();
RealmQuery
<
RealmRoom
>
query
=
realm
.
where
(
RealmRoom
.
class
);
Iterator
<
String
>
keys
=
openedRooms
.
keys
();
while
(
keys
.
hasNext
())
{
String
rid
=
keys
.
next
();
RealmRoom
realmRoom
=
query
.
equalTo
(
RealmRoom
.
ID
,
rid
).
findFirst
();
if
(
realmRoom
==
null
)
{
RocketChatCache
.
INSTANCE
.
removeOpenedRoom
(
rid
);
}
else
{
loadMissedMessages
(
rid
,
realmRoom
.
getLastSeen
())
.
continueWithTask
(
task1
->
{
if
(
task1
.
isFaulted
())
{
Exception
error
=
task1
.
getError
();
RCLog
.
e
(
error
);
}
return
null
;
});
}
}
return
null
;
});
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
});
}
public
Task
<
JSONArray
>
loadMissedMessages
(
final
String
roomId
,
final
long
timestamp
)
{
return
call
(
"loadMissedMessages"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
roomId
)
.
put
(
timestamp
>
0
?
new
JSONObject
().
put
(
"$date"
,
timestamp
)
:
JSONObject
.
NULL
)
).
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
JSONArray
result
=
task
.
getResult
();
for
(
int
i
=
0
;
i
<
result
.
length
();
i
++)
{
RealmMessage
.
customizeJson
(
result
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
if
(
timestamp
==
0
)
{
realm
.
where
(
RealmMessage
.
class
)
.
equalTo
(
"rid"
,
roomId
)
.
equalTo
(
"syncstate"
,
SyncState
.
SYNCED
)
.
findAll
().
deleteAllFromRealm
();
}
if
(
result
.
length
()
>
0
)
{
realm
.
createOrUpdateAllFromJson
(
RealmMessage
.
class
,
result
);
}
return
null
;
}).
onSuccessTask
(
_task
->
Task
.
forResult
(
result
));
});
}
/**
* Load messages for room.
*/
public
Task
<
JSONArray
>
loadHistory
(
final
String
roomId
,
final
long
timestamp
,
final
int
count
,
final
long
lastSeen
)
{
return
call
(
"loadHistory"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
roomId
)
.
put
(
timestamp
>
0
?
new
JSONObject
().
put
(
"$date"
,
timestamp
)
:
JSONObject
.
NULL
)
.
put
(
count
)
.
put
(
lastSeen
>
0
?
new
JSONObject
().
put
(
"$date"
,
lastSeen
)
:
JSONObject
.
NULL
)
).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
JSONObject
result
=
task
.
getResult
();
final
JSONArray
messages
=
result
.
getJSONArray
(
"messages"
);
for
(
int
i
=
0
;
i
<
messages
.
length
();
i
++)
{
RealmMessage
.
customizeJson
(
messages
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
if
(
timestamp
==
0
)
{
realm
.
where
(
RealmMessage
.
class
)
.
equalTo
(
"rid"
,
roomId
)
.
equalTo
(
"syncstate"
,
SyncState
.
SYNCED
)
.
findAll
().
deleteAllFromRealm
();
}
if
(
messages
.
length
()
>
0
)
{
realm
.
createOrUpdateAllFromJson
(
RealmMessage
.
class
,
messages
);
}
return
null
;
}).
onSuccessTask
(
_task
->
Task
.
forResult
(
messages
));
});
}
/**
* update user's status.
*/
public
Task
<
Void
>
setUserStatus
(
final
String
status
)
{
return
call
(
"UserPresence:setDefaultStatus"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
status
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
setUserPresence
(
final
String
status
)
{
return
call
(
"UserPresence:"
+
status
,
TIMEOUT_MS
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
JSONObject
>
getUsersOfRoom
(
final
String
roomId
,
final
boolean
showAll
)
{
return
call
(
"getUsersOfRoom"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
).
put
(
showAll
))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
);
}
if
(
jsonString
==
null
)
{
jsonString
=
userJsonArray
.
toString
();
public
Task
<
Void
>
createChannel
(
final
String
name
,
final
boolean
readOnly
)
{
return
call
(
"createChannel"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
name
)
.
put
(
new
JSONArray
())
.
put
(
readOnly
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
createPrivateGroup
(
final
String
name
,
final
boolean
readOnly
)
{
return
call
(
"createPrivateGroup"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
name
)
.
put
(
new
JSONArray
())
.
put
(
readOnly
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
String
>
createDirectMessage
(
final
String
username
)
{
return
call
(
"createDirectMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
username
))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
Task
.
forResult
(
task
.
getResult
().
getString
(
"rid"
)));
}
/**
* send message.
*/
public
Task
<
Void
>
sendMessage
(
String
messageId
,
String
roomId
,
String
msg
,
long
editedAt
)
{
try
{
JSONObject
messageJson
=
new
JSONObject
()
.
put
(
"_id"
,
messageId
)
.
put
(
"rid"
,
roomId
)
.
put
(
"msg"
,
msg
);
if
(
editedAt
==
0
)
{
return
sendMessage
(
messageJson
);
}
else
{
jsonString
=
jsonString
.
replace
(
"]"
,
""
)
+
","
+
userJsonArray
.
toString
().
replace
(
"["
,
""
);
return
updateMessage
(
messageJson
);
}
}
if
(
jsonString
!=
null
)
{
String
jsonStringResults
=
jsonString
;
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmSpotlight
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmSpotlight
.
class
,
jsonStringResults
);
return
null
;
});
}
return
null
;
});
}
private
Task
<
Void
>
searchSpotlight
(
Class
clazz
,
String
key
,
String
term
)
{
return
call
(
"spotlight"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
term
)
.
put
(
JSONObject
.
NULL
)
.
put
(
new
JSONObject
().
put
(
key
,
true
)))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
final
JSONObject
result
=
task
.
getResult
();
if
(!
result
.
has
(
key
))
{
return
null
;
}
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
Object
items
=
result
.
get
(
key
);
if
(!(
items
instanceof
JSONArray
))
{
return
null
;
}
public
Task
<
Void
>
deleteMessage
(
String
messageID
)
{
try
{
JSONObject
messageJson
=
new
JSONObject
()
.
put
(
"_id"
,
messageID
);
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
clazz
);
realm
.
createOrUpdateAllFromJson
(
clazz
,
(
JSONArray
)
items
);
return
null
;
});
});
}
return
deleteMessage
(
messageJson
);
}
catch
(
JSONException
exception
)
{
return
Task
.
forError
(
exception
);
}
}
/**
* Send message object.
*/
private
Task
<
Void
>
sendMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"sendMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
private
Task
<
Void
>
updateMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"updateMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
private
Task
<
Void
>
deleteMessage
(
final
JSONObject
messageJson
)
{
return
call
(
"deleteMessage"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
messageJson
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
/**
* mark all messages are read in the room.
*/
public
Task
<
Void
>
readMessages
(
final
String
roomId
)
{
return
call
(
"readMessages"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
task
->
Task
.
forResult
(
null
));
}
public
Task
<
Void
>
getPublicSettings
(
String
currentHostname
)
{
return
call
(
"public-settings/get"
,
TIMEOUT_MS
)
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
settings
=
task
.
getResult
();
String
siteUrl
=
null
;
String
siteName
=
null
;
for
(
int
i
=
0
;
i
<
settings
.
length
();
i
++)
{
JSONObject
jsonObject
=
settings
.
getJSONObject
(
i
);
RealmPublicSetting
.
customizeJson
(
jsonObject
);
if
(
isPublicSetting
(
jsonObject
,
PublicSettingsConstants
.
General
.
SITE_URL
))
{
siteUrl
=
jsonObject
.
getString
(
RealmPublicSetting
.
VALUE
);
}
else
if
(
isPublicSetting
(
jsonObject
,
PublicSettingsConstants
.
General
.
SITE_NAME
))
{
siteName
=
jsonObject
.
getString
(
RealmPublicSetting
.
VALUE
);
}
}
if
(
siteName
!=
null
&&
siteUrl
!=
null
)
{
HttpUrl
httpSiteUrl
=
HttpUrl
.
parse
(
siteUrl
);
if
(
httpSiteUrl
!=
null
)
{
String
host
=
httpSiteUrl
.
host
();
RocketChatCache
.
INSTANCE
.
addSiteUrl
(
host
,
currentHostname
);
RocketChatCache
.
INSTANCE
.
addSiteName
(
currentHostname
,
siteName
);
}
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmPublicSetting
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmPublicSetting
.
class
,
settings
);
return
null
;
});
});
}
private
boolean
isPublicSetting
(
JSONObject
jsonObject
,
String
id
)
{
return
jsonObject
.
optString
(
RealmPublicSetting
.
ID
).
equalsIgnoreCase
(
id
);
}
public
Task
<
Void
>
getPermissions
()
{
return
call
(
"permissions/get"
,
TIMEOUT_MS
)
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
permissions
=
task
.
getResult
();
for
(
int
i
=
0
;
i
<
permissions
.
length
();
i
++)
{
RealmPermission
.
customizeJson
(
permissions
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmPermission
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmPermission
.
class
,
permissions
);
return
null
;
});
});
}
public
Task
<
Void
>
getRoomRoles
(
final
String
roomId
)
{
return
call
(
"getRoomRoles"
,
TIMEOUT_MS
,
()
->
new
JSONArray
().
put
(
roomId
))
.
onSuccessTask
(
CONVERT_TO_JSON_ARRAY
)
.
onSuccessTask
(
task
->
{
final
JSONArray
roomRoles
=
task
.
getResult
();
for
(
int
i
=
0
;
i
<
roomRoles
.
length
();
i
++)
{
RealmRoomRole
.
customizeJson
(
roomRoles
.
getJSONObject
(
i
));
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmRoomRole
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmRoomRole
.
class
,
roomRoles
);
return
null
;
});
});
}
public
Task
<
Void
>
searchSpotlightUsers
(
String
term
)
{
return
searchSpotlight
(
RealmSpotlightUser
.
class
,
"users"
,
term
);
}
protected
interface
ParamBuilder
{
JSONArray
buildParam
()
throws
JSONException
;
}
public
Task
<
Void
>
searchSpotlightRooms
(
String
term
)
{
return
searchSpotlight
(
RealmSpotlightRoom
.
class
,
"rooms"
,
term
);
}
public
Task
<
Void
>
searchSpotlight
(
String
term
)
{
return
call
(
"spotlight"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
term
)
.
put
(
JSONObject
.
NULL
)
.
put
(
new
JSONObject
().
put
(
"rooms"
,
true
).
put
(
"users"
,
true
))
).
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
String
jsonString
=
null
;
final
JSONObject
result
=
task
.
getResult
();
JSONArray
roomJsonArray
=
(
JSONArray
)
result
.
get
(
"rooms"
);
int
roomTotal
=
roomJsonArray
.
length
();
if
(
roomTotal
>
0
)
{
for
(
int
i
=
0
;
i
<
roomTotal
;
++
i
)
{
RealmSpotlight
.
Companion
.
customizeRoomJSONObject
(
roomJsonArray
.
getJSONObject
(
i
));
}
jsonString
=
roomJsonArray
.
toString
();
}
JSONArray
userJsonArray
=
(
JSONArray
)
result
.
get
(
"users"
);
int
usersTotal
=
userJsonArray
.
length
();
if
(
usersTotal
>
0
)
{
for
(
int
i
=
0
;
i
<
usersTotal
;
++
i
)
{
RealmSpotlight
.
Companion
.
customizeUserJSONObject
(
userJsonArray
.
getJSONObject
(
i
));
}
if
(
jsonString
==
null
)
{
jsonString
=
userJsonArray
.
toString
();
}
else
{
jsonString
=
jsonString
.
replace
(
"]"
,
""
)
+
","
+
userJsonArray
.
toString
().
replace
(
"["
,
""
);
}
}
if
(
jsonString
!=
null
)
{
String
jsonStringResults
=
jsonString
;
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
RealmSpotlight
.
class
);
realm
.
createOrUpdateAllFromJson
(
RealmSpotlight
.
class
,
jsonStringResults
);
return
null
;
});
}
return
null
;
});
}
private
Task
<
Void
>
searchSpotlight
(
Class
clazz
,
String
key
,
String
term
)
{
return
call
(
"spotlight"
,
TIMEOUT_MS
,
()
->
new
JSONArray
()
.
put
(
term
)
.
put
(
JSONObject
.
NULL
)
.
put
(
new
JSONObject
().
put
(
key
,
true
)))
.
onSuccessTask
(
CONVERT_TO_JSON_OBJECT
)
.
onSuccessTask
(
task
->
{
final
JSONObject
result
=
task
.
getResult
();
if
(!
result
.
has
(
key
))
{
return
null
;
}
Object
items
=
result
.
get
(
key
);
if
(!(
items
instanceof
JSONArray
))
{
return
null
;
}
return
realmHelper
.
executeTransaction
(
realm
->
{
realm
.
delete
(
clazz
);
realm
.
createOrUpdateAllFromJson
(
clazz
,
(
JSONArray
)
items
);
return
null
;
});
});
}
protected
interface
ParamBuilder
{
JSONArray
buildParam
()
throws
JSONException
;
}
}
app/src/main/java/chat/rocket/android/api/rest/DefaultCookieProvider.java
View file @
cc23374c
...
...
@@ -8,42 +8,37 @@ import chat.rocket.persistence.realm.models.internal.RealmSession;
public
class
DefaultCookieProvider
implements
CookieProvider
{
private
RocketChatCache
rocketChatCache
;
public
DefaultCookieProvider
(
RocketChatCache
rocketChatCache
)
{
this
.
rocketChatCache
=
rocketChatCache
;
}
@Override
public
String
getHostname
()
{
return
getHostnameFromCache
();
}
@Override
public
String
getCookie
()
{
final
String
hostname
=
getHostnameFromCache
();
if
(
hostname
==
null
)
{
return
""
;
}
final
RealmHelper
realmHelper
=
RealmStore
.
get
(
getHostnameFromCache
());
if
(
realmHelper
==
null
)
{
return
""
;
@Override
public
String
getHostname
(
)
{
return
getHostnameFromCache
()
;
}
final
RealmUser
user
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmUser
.
queryCurrentUser
(
realm
).
findFirst
());
final
RealmSession
session
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmSession
.
queryDefaultSession
(
realm
).
findFirst
());
@Override
public
String
getCookie
()
{
final
String
hostname
=
getHostnameFromCache
();
if
(
hostname
==
null
)
{
return
""
;
}
if
(
user
==
null
||
session
==
null
)
{
return
""
;
}
final
RealmHelper
realmHelper
=
RealmStore
.
get
(
getHostnameFromCache
());
if
(
realmHelper
==
null
)
{
return
""
;
}
final
RealmUser
user
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmUser
.
queryCurrentUser
(
realm
).
findFirst
());
final
RealmSession
session
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmSession
.
queryDefaultSession
(
realm
).
findFirst
());
return
"rc_uid="
+
user
.
getId
()
+
";rc_token="
+
session
.
getToken
();
}
if
(
user
==
null
||
session
==
null
)
{
return
""
;
}
private
String
getHostnameFromCache
()
{
return
rocketChatCache
.
getSelectedServerHostname
();
}
return
"rc_uid="
+
user
.
getId
()
+
";rc_token="
+
session
.
getToken
();
}
private
String
getHostnameFromCache
()
{
return
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
}
}
app/src/main/java/chat/rocket/android/extensions/HelperExtensions.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.extensions
import
chat.rocket.android.BuildConfig
fun
Throwable
.
printStackTraceOnDebug
()
{
if
(
BuildConfig
.
DEBUG
)
{
this
.
printStackTrace
()
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/fragment/add_server/InputHostnameFragment.java
View file @
cc23374c
...
...
@@ -12,7 +12,6 @@ import android.widget.TextView;
import
chat.rocket.android.BuildConfig
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.R
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.fragment.AbstractFragment
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.service.ConnectivityManager
;
...
...
@@ -34,7 +33,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
super
.
onCreate
(
savedInstanceState
);
Context
appContext
=
getContext
().
getApplicationContext
();
presenter
=
new
InputHostnamePresenter
(
new
RocketChatCache
(
appContext
),
ConnectivityManager
.
getInstance
(
appContext
));
presenter
=
new
InputHostnamePresenter
(
ConnectivityManager
.
getInstance
(
appContext
));
}
@Override
...
...
@@ -52,7 +51,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
}
private
void
setupVersionInfo
()
{
TextView
versionInfoView
=
(
TextView
)
rootView
.
findViewById
(
R
.
id
.
version_info
);
TextView
versionInfoView
=
rootView
.
findViewById
(
R
.
id
.
version_info
);
versionInfoView
.
setText
(
getString
(
R
.
string
.
version_info_text
,
BuildConfig
.
VERSION_NAME
));
}
...
...
app/src/main/java/chat/rocket/android/fragment/add_server/InputHostnamePresenter.java
View file @
cc23374c
...
...
@@ -14,11 +14,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import
io.reactivex.disposables.Disposable
;
public
class
InputHostnamePresenter
extends
BasePresenter
<
InputHostnameContract
.
View
>
implements
InputHostnameContract
.
Presenter
{
private
final
RocketChatCache
rocketChatCache
;
private
final
ConnectivityManagerApi
connectivityManager
;
public
InputHostnamePresenter
(
RocketChatCache
rocketChatCache
,
ConnectivityManagerApi
connectivityManager
)
{
this
.
rocketChatCache
=
rocketChatCache
;
public
InputHostnamePresenter
(
ConnectivityManagerApi
connectivityManager
)
{
this
.
connectivityManager
=
connectivityManager
;
}
...
...
@@ -47,14 +45,14 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.
}
},
throwable
->
{
Logger
.
report
(
throwable
);
Logger
.
INSTANCE
.
report
(
throwable
);
view
.
showConnectionError
();
});
addSubscription
(
subscription
);
}
private
void
onServerValid
(
String
hostname
,
boolean
usesSecureConnection
)
{
rocketChatCache
.
setSelectedServerHostname
(
hostname
);
RocketChatCache
.
INSTANCE
.
setSelectedServerHostname
(
hostname
);
String
server
=
hostname
.
replace
(
"/"
,
"."
);
connectivityManager
.
addOrUpdateServer
(
server
,
server
,
!
usesSecureConnection
);
...
...
app/src/main/java/chat/rocket/android/fragment/chatroom/AbstractChatRoomFragment.java
View file @
cc23374c
...
...
@@ -11,7 +11,7 @@ import chat.rocket.android.fragment.AbstractFragment;
import
chat.rocket.android.widget.RoomToolbar
;
import
chat.rocket.core.models.User
;
abstract
class
AbstractChatRoomFragment
extends
AbstractFragment
{
public
abstract
class
AbstractChatRoomFragment
extends
AbstractFragment
{
private
RoomToolbar
roomToolbar
;
@Nullable
...
...
app/src/main/java/chat/rocket/android/fragment/chatroom/HomeFragment.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
.
fragment
.
chatroom
;
import
chat.rocket.android.R
;
public
class
HomeFragment
extends
AbstractChatRoomFragment
{
public
HomeFragment
()
{}
@Override
protected
int
getLayout
()
{
return
R
.
layout
.
fragment_home
;
}
@Override
protected
void
onSetupView
()
{
setToolbarTitle
(
getText
(
R
.
string
.
home_fragment_title
));
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/fragment/chatroom/HomeFragment.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.fragment.chatroom
import
chat.rocket.android.R
class
HomeFragment
:
AbstractChatRoomFragment
()
{
override
fun
getLayout
():
Int
{
return
R
.
layout
.
fragment_home
}
override
fun
onSetupView
()
{
setToolbarTitle
(
getText
(
R
.
string
.
home_fragment_title
))
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/fragment/chatroom/RoomContract.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
.
fragment
.
chatroom
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
java.util.List
;
import
chat.rocket.android.shared.BaseContract
;
import
chat.rocket.android.widget.AbsoluteUrl
;
import
chat.rocket.core.models.Message
;
import
chat.rocket.core.models.Room
;
import
chat.rocket.core.models.User
;
public
interface
RoomContract
{
interface
View
extends
BaseContract
.
View
{
void
setupWith
(
RocketChatAbsoluteUrl
rocketChatAbsoluteUrl
);
void
render
(
Room
room
);
void
showUserStatus
(
User
user
);
void
updateHistoryState
(
boolean
hasNext
,
boolean
isLoaded
);
void
onMessageSendSuccessfully
();
void
disableMessageInput
();
void
enableMessageInput
();
void
showUnreadCount
(
int
count
);
void
showMessages
(
List
<
Message
>
messages
);
void
showMessageSendFailure
(
Message
message
);
void
showMessageDeleteFailure
(
Message
message
);
void
autoloadImages
();
void
manualLoadImages
();
void
onReply
(
AbsoluteUrl
absoluteUrl
,
String
markdown
,
Message
message
);
void
onCopy
(
String
message
);
void
showMessageActions
(
Message
message
);
}
interface
Presenter
extends
BaseContract
.
Presenter
<
View
>
{
void
loadMessages
();
void
loadMoreMessages
();
void
onMessageSelected
(
@Nullable
Message
message
);
void
onMessageTap
(
@Nullable
Message
message
);
void
sendMessage
(
String
messageText
);
void
resendMessage
(
@NonNull
Message
message
);
void
updateMessage
(
@NonNull
Message
message
,
String
content
);
void
deleteMessage
(
@NonNull
Message
message
);
void
onUnreadCount
();
void
onMarkAsRead
();
void
refreshRoom
();
void
replyMessage
(
@NonNull
Message
message
,
boolean
justQuote
);
void
acceptMessageDeleteFailure
(
Message
message
);
}
}
app/src/main/java/chat/rocket/android/fragment/chatroom/RoomContract.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.fragment.chatroom
import
chat.rocket.android.shared.BaseContract
import
chat.rocket.android.widget.AbsoluteUrl
import
chat.rocket.core.models.Message
import
chat.rocket.core.models.Room
import
chat.rocket.core.models.User
interface
RoomContract
{
interface
View
:
BaseContract
.
View
{
fun
setupWith
(
rocketChatAbsoluteUrl
:
RocketChatAbsoluteUrl
)
fun
render
(
room
:
Room
)
fun
showUserStatus
(
user
:
User
)
fun
updateHistoryState
(
hasNext
:
Boolean
,
isLoaded
:
Boolean
)
fun
onMessageSendSuccessfully
()
fun
disableMessageInput
()
fun
enableMessageInput
()
fun
showUnreadCount
(
count
:
Int
)
fun
showMessages
(
messages
:
List
<
Message
>)
fun
showMessageSendFailure
(
message
:
Message
)
fun
showMessageDeleteFailure
(
message
:
Message
)
fun
autoloadImages
()
fun
manualLoadImages
()
fun
onReply
(
absoluteUrl
:
AbsoluteUrl
,
markdown
:
String
,
message
:
Message
)
fun
onCopy
(
message
:
String
)
fun
showMessageActions
(
message
:
Message
)
}
interface
Presenter
:
BaseContract
.
Presenter
<
View
>
{
fun
loadMessages
()
fun
loadMoreMessages
()
fun
onMessageSelected
(
message
:
Message
?)
fun
onMessageTap
(
message
:
Message
?)
fun
sendMessage
(
messageText
:
String
)
fun
resendMessage
(
message
:
Message
)
fun
updateMessage
(
message
:
Message
,
content
:
String
)
fun
deleteMessage
(
message
:
Message
)
fun
onUnreadCount
()
fun
onMarkAsRead
()
fun
refreshRoom
()
fun
replyMessage
(
message
:
Message
,
justQuote
:
Boolean
)
fun
acceptMessageDeleteFailure
(
message
:
Message
)
fun
loadMissedMessages
()
}
}
app/src/main/java/chat/rocket/android/fragment/chatroom/RoomFragment.java
View file @
cc23374c
...
...
@@ -131,7 +131,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
private
MethodCallHelper
methodCallHelper
;
private
AbsoluteUrlHelper
absoluteUrlHelper
;
private
Message
edit
t
ingMessage
=
null
;
private
Message
editingMessage
=
null
;
private
RoomToolbar
toolbar
;
...
...
@@ -344,7 +344,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
optionalPane
.
ifPresent
(
pane
->
pane
.
setPanelSlideListener
(
new
SlidingPaneLayout
.
PanelSlideListener
()
{
@Override
public
void
onPanelSlide
(
View
view
,
float
v
)
{
public
void
onPanelSlide
(
@NonNull
View
view
,
float
v
)
{
messageFormManager
.
enableComposingText
(
false
);
sidebarFragment
.
clearSearchViewFocus
();
//Ref: ActionBarDrawerToggle#setProgress
...
...
@@ -352,12 +352,12 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
onPanelOpened
(
View
view
)
{
public
void
onPanelOpened
(
@NonNull
View
view
)
{
toolbar
.
setNavigationIconVerticalMirror
(
true
);
}
@Override
public
void
onPanelClosed
(
View
view
)
{
public
void
onPanelClosed
(
@NonNull
View
view
)
{
messageFormManager
.
enableComposingText
(
true
);
toolbar
.
setNavigationIconVerticalMirror
(
false
);
subPane
.
closePane
();
...
...
@@ -487,8 +487,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
@Override
public
boolean
onBackPressed
()
{
if
(
edit
t
ingMessage
!=
null
)
{
edit
t
ingMessage
=
null
;
if
(
editingMessage
!=
null
)
{
editingMessage
=
null
;
messageFormManager
.
clearComposingText
();
}
return
false
;
...
...
@@ -540,22 +540,22 @@ public class RoomFragment extends AbstractChatRoomFragment implements
inputContentInfo
.
releasePermission
();
}
catch
(
Exception
e
)
{
RCLog
.
e
(
e
);
Logger
.
report
(
e
);
Logger
.
INSTANCE
.
report
(
e
);
}
return
true
;
}
private
void
sendMessage
(
String
messageText
)
{
if
(
edit
t
ingMessage
==
null
)
{
if
(
editingMessage
==
null
)
{
presenter
.
sendMessage
(
messageText
);
}
else
{
presenter
.
updateMessage
(
edit
t
ingMessage
,
messageText
);
presenter
.
updateMessage
(
editingMessage
,
messageText
);
}
}
@Override
public
void
setupWith
(
RocketChatAbsoluteUrl
rocketChatAbsoluteUrl
)
{
public
void
setupWith
(
@NonNull
RocketChatAbsoluteUrl
rocketChatAbsoluteUrl
)
{
if
(
rocketChatAbsoluteUrl
!=
null
)
{
token
=
rocketChatAbsoluteUrl
.
getToken
();
userId
=
rocketChatAbsoluteUrl
.
getUserId
();
...
...
@@ -564,7 +564,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
render
(
Room
room
)
{
public
void
render
(
@NonNull
Room
room
)
{
roomType
=
room
.
getType
();
setToolbarTitle
(
room
.
getName
());
...
...
@@ -589,7 +589,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
showUserStatus
(
User
user
)
{
public
void
showUserStatus
(
@NonNull
User
user
)
{
showToolbarUserStatuslIcon
(
user
.
getStatus
());
}
...
...
@@ -610,7 +610,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
public
void
onMessageSendSuccessfully
()
{
scrollToLatestMessage
();
messageFormManager
.
onMessageSend
();
edit
t
ingMessage
=
null
;
editingMessage
=
null
;
}
@Override
...
...
@@ -629,15 +629,16 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
showMessages
(
List
<
Message
>
messages
)
{
public
void
showMessages
(
@NonNull
List
<?
extends
Message
>
messages
)
{
if
(
messageListAdapter
==
null
)
{
return
;
}
messageListAdapter
.
updateData
(
messages
);
messageListAdapter
.
updateData
((
List
<
Message
>)
messages
);
}
@Override
public
void
showMessageSendFailure
(
Message
message
)
{
public
void
showMessageSendFailure
(
@NonNull
Message
message
)
{
new
AlertDialog
.
Builder
(
getContext
())
.
setPositiveButton
(
R
.
string
.
resend
,
(
dialog
,
which
)
->
presenter
.
resendMessage
(
message
))
...
...
@@ -648,7 +649,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
showMessageDeleteFailure
(
Message
message
)
{
public
void
showMessageDeleteFailure
(
@NonNull
Message
message
)
{
new
AlertDialog
.
Builder
(
getContext
())
.
setTitle
(
getContext
().
getString
(
R
.
string
.
failed_to_delete
))
.
setMessage
(
getContext
().
getString
(
R
.
string
.
failed_to_delete_message
))
...
...
@@ -667,12 +668,12 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
onReply
(
AbsoluteUrl
absoluteUrl
,
String
markdown
,
Message
message
)
{
public
void
onReply
(
@NonNull
AbsoluteUrl
absoluteUrl
,
@NonNull
String
markdown
,
@NonNull
Message
message
)
{
messageFormManager
.
setReply
(
absoluteUrl
,
markdown
,
message
);
}
@Override
public
void
onCopy
(
String
message
)
{
public
void
onCopy
(
@NonNull
String
message
)
{
RocketChatApplication
context
=
RocketChatApplication
.
getInstance
();
ClipboardManager
clipboardManager
=
(
ClipboardManager
)
context
.
getSystemService
(
Context
.
CLIPBOARD_SERVICE
);
...
...
@@ -680,7 +681,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
@Override
public
void
showMessageActions
(
Message
message
)
{
public
void
showMessageActions
(
@NonNull
Message
message
)
{
Activity
context
=
getActivity
();
if
(
context
!=
null
&&
context
instanceof
MainActivity
)
{
MessagePopup
.
take
(
message
)
...
...
@@ -694,7 +695,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
private
void
onEditMessage
(
Message
message
)
{
edit
t
ingMessage
=
message
;
editingMessage
=
message
;
messageFormManager
.
setEditMessage
(
message
.
getMessage
());
}
...
...
@@ -716,7 +717,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
}
public
void
loadMessages
()
{
presenter
.
loadMessages
();
public
void
loadM
issedM
essages
()
{
presenter
.
loadM
issedM
essages
();
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/fragment/chatroom/RoomPresenter.java
View file @
cc23374c
...
...
@@ -6,11 +6,17 @@ import android.support.v4.util.Pair;
import
com.hadisatrio.optional.Optional
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
chat.rocket.android.BackgroundLooper
;
import
chat.rocket.android.RocketChatApplication
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.helper.AbsoluteUrlHelper
;
import
chat.rocket.android.helper.LogIfError
;
import
chat.rocket.android.helper.Logger
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.android.service.ConnectivityManagerApi
;
import
chat.rocket.android.shared.BasePresenter
;
import
chat.rocket.core.SyncState
;
...
...
@@ -27,391 +33,414 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import
io.reactivex.disposables.Disposable
;
public
class
RoomPresenter
extends
BasePresenter
<
RoomContract
.
View
>
implements
RoomContract
.
Presenter
{
private
final
String
roomId
;
private
final
MessageInteractor
messageInteractor
;
private
final
UserRepository
userRepository
;
private
final
RoomRepository
roomRepository
;
private
final
AbsoluteUrlHelper
absoluteUrlHelper
;
private
final
MethodCallHelper
methodCallHelper
;
private
final
ConnectivityManagerApi
connectivityManagerApi
;
private
Room
currentRoom
;
public
RoomPresenter
(
String
roomId
,
UserRepository
userRepository
,
MessageInteractor
messageInteractor
,
RoomRepository
roomRepository
,
AbsoluteUrlHelper
absoluteUrlHelper
,
MethodCallHelper
methodCallHelper
,
ConnectivityManagerApi
connectivityManagerApi
)
{
this
.
roomId
=
roomId
;
this
.
userRepository
=
userRepository
;
this
.
messageInteractor
=
messageInteractor
;
this
.
roomRepository
=
roomRepository
;
this
.
absoluteUrlHelper
=
absoluteUrlHelper
;
this
.
methodCallHelper
=
methodCallHelper
;
this
.
connectivityManagerApi
=
connectivityManagerApi
;
}
@Override
public
void
bindView
(
@NonNull
RoomContract
.
View
view
)
{
super
.
bindView
(
view
);
refreshRoom
();
}
@Override
public
void
refreshRoom
()
{
getRoomRoles
();
getRoomInfo
();
getRoomHistoryStateInfo
();
getMessages
();
getUserPreferences
();
}
@Override
public
void
loadMessages
()
{
final
Disposable
subscription
=
getSingleRoom
()
.
flatMap
(
messageInteractor:
:
loadMessages
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(!
success
)
{
connectivityManagerApi
.
keepAliveServer
();
}
},
Logger:
:
report
);
implements
RoomContract
.
Presenter
{
private
final
String
roomId
;
private
final
MessageInteractor
messageInteractor
;
private
final
UserRepository
userRepository
;
private
final
RoomRepository
roomRepository
;
private
final
AbsoluteUrlHelper
absoluteUrlHelper
;
private
final
MethodCallHelper
methodCallHelper
;
private
final
ConnectivityManagerApi
connectivityManagerApi
;
private
Room
currentRoom
;
/* package */
RoomPresenter
(
String
roomId
,
UserRepository
userRepository
,
MessageInteractor
messageInteractor
,
RoomRepository
roomRepository
,
AbsoluteUrlHelper
absoluteUrlHelper
,
MethodCallHelper
methodCallHelper
,
ConnectivityManagerApi
connectivityManagerApi
)
{
this
.
roomId
=
roomId
;
this
.
userRepository
=
userRepository
;
this
.
messageInteractor
=
messageInteractor
;
this
.
roomRepository
=
roomRepository
;
this
.
absoluteUrlHelper
=
absoluteUrlHelper
;
this
.
methodCallHelper
=
methodCallHelper
;
this
.
connectivityManagerApi
=
connectivityManagerApi
;
}
addSubscription
(
subscription
);
}
@Override
public
void
loadMoreMessages
()
{
final
Disposable
subscription
=
getSingleRoom
()
.
flatMap
(
messageInteractor:
:
loadMoreMessages
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(!
success
)
{
connectivityManagerApi
.
keepAliveServer
();
}
},
Logger:
:
report
);
@Override
public
void
bindView
(
@NonNull
RoomContract
.
View
view
)
{
super
.
bindView
(
view
);
refreshRoom
();
}
addSubscription
(
subscription
);
}
@Override
public
void
refreshRoom
()
{
getRoomRoles
();
getRoomInfo
();
getRoomHistoryStateInfo
();
getMessages
();
getUserPreferences
();
}
@Override
public
void
onMessageSelected
(
@Nullable
Message
message
)
{
if
(
message
==
null
)
{
return
;
@Override
public
void
loadMessages
()
{
final
Disposable
subscription
=
getSingleRoom
()
.
flatMap
(
messageInteractor:
:
loadMessages
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(!
success
)
{
connectivityManagerApi
.
keepAliveServer
();
}
},
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
if
(
message
.
getSyncState
()
==
SyncState
.
DELETE_FAILED
)
{
view
.
showMessageDeleteFailure
(
message
);
}
else
if
(
message
.
getSyncState
()
==
SyncState
.
FAILED
)
{
view
.
showMessageSendFailure
(
message
);
}
else
if
(
message
.
getType
()
==
null
&&
message
.
getSyncState
()
==
SyncState
.
SYNCED
)
{
// If message is not a system message show applicable actions.
view
.
showMessageActions
(
message
);
@Override
public
void
loadMoreMessages
()
{
final
Disposable
subscription
=
getSingleRoom
()
.
flatMap
(
messageInteractor:
:
loadMoreMessages
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(!
success
)
{
connectivityManagerApi
.
keepAliveServer
();
}
},
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
}
@Override
public
void
onMessageTap
(
@Nullable
Message
message
)
{
if
(
message
==
null
)
{
return
;
@Override
public
void
onMessageSelected
(
@Nullable
Message
message
)
{
if
(
message
==
null
)
{
return
;
}
if
(
message
.
getSyncState
()
==
SyncState
.
DELETE_FAILED
)
{
view
.
showMessageDeleteFailure
(
message
);
}
else
if
(
message
.
getSyncState
()
==
SyncState
.
FAILED
)
{
view
.
showMessageSendFailure
(
message
);
}
else
if
(
message
.
getType
()
==
null
&&
message
.
getSyncState
()
==
SyncState
.
SYNCED
)
{
// If message is not a system message show applicable actions.
view
.
showMessageActions
(
message
);
}
}
if
(
message
.
getSyncState
()
==
SyncState
.
FAILED
)
{
view
.
showMessageSendFailure
(
message
);
@Override
public
void
onMessageTap
(
@Nullable
Message
message
)
{
if
(
message
==
null
)
{
return
;
}
if
(
message
.
getSyncState
()
==
SyncState
.
FAILED
)
{
view
.
showMessageSendFailure
(
message
);
}
}
}
@Override
public
void
replyMessage
(
@NonNull
Message
message
,
boolean
justQuote
)
{
final
Disposable
subscription
=
this
.
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
()
.
cache
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
serverUrl
->
{
if
(
serverUrl
.
isPresent
())
{
RocketChatAbsoluteUrl
absoluteUrl
=
serverUrl
.
get
();
String
baseUrl
=
absoluteUrl
.
getBaseUrl
();
view
.
onReply
(
absoluteUrl
,
buildReplyOrQuoteMarkdown
(
baseUrl
,
message
,
justQuote
),
message
);
}
},
Logger:
:
report
);
}
public
void
acceptMessageDeleteFailure
(
Message
message
)
{
final
Disposable
subscription
=
messageInteractor
.
acceptDeleteFailure
(
message
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
private
String
buildReplyOrQuoteMarkdown
(
String
baseUrl
,
Message
message
,
boolean
justQuote
)
{
if
(
currentRoom
==
null
||
message
.
getUser
()
==
null
)
{
return
""
;
@Override
public
void
replyMessage
(
@NonNull
Message
message
,
boolean
justQuote
)
{
final
Disposable
subscription
=
this
.
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
()
.
cache
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
serverUrl
->
{
if
(
serverUrl
.
isPresent
())
{
RocketChatAbsoluteUrl
absoluteUrl
=
serverUrl
.
get
();
String
baseUrl
=
absoluteUrl
.
getBaseUrl
();
view
.
onReply
(
absoluteUrl
,
buildReplyOrQuoteMarkdown
(
baseUrl
,
message
,
justQuote
),
message
);
}
},
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
if
(
currentRoom
.
isDirectMessage
())
{
return
String
.
format
(
"[ ](%s/direct/%s?msg=%s) "
,
baseUrl
,
message
.
getUser
().
getUsername
(),
message
.
getId
());
}
else
{
return
String
.
format
(
"[ ](%s/channel/%s?msg=%s) %s"
,
baseUrl
,
currentRoom
.
getName
(),
message
.
getId
(),
justQuote
?
""
:
"@"
+
message
.
getUser
().
getUsername
()
+
" "
);
public
void
acceptMessageDeleteFailure
(
Message
message
)
{
final
Disposable
subscription
=
messageInteractor
.
acceptDeleteFailure
(
message
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
}
@Override
public
void
sendMessage
(
String
messageText
)
{
view
.
disableMessageInput
();
final
Disposable
subscription
=
getRoomUserPair
()
.
flatMap
(
pair
->
messageInteractor
.
send
(
pair
.
first
,
pair
.
second
,
messageText
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(
success
)
{
view
.
onMessageSendSuccessfully
();
}
view
.
enableMessageInput
();
},
throwable
->
{
view
.
enableMessageInput
();
Logger
.
report
(
throwable
);
}
);
addSubscription
(
subscription
);
}
@Override
public
void
resendMessage
(
@NonNull
Message
message
)
{
final
Disposable
subscription
=
getCurrentUser
()
.
flatMap
(
user
->
messageInteractor
.
resend
(
message
,
user
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
@Override
public
void
updateMessage
(
@NonNull
Message
message
,
String
content
)
{
view
.
disableMessageInput
();
final
Disposable
subscription
=
getCurrentUser
()
.
flatMap
(
user
->
messageInteractor
.
update
(
message
,
user
,
content
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(
success
)
{
view
.
onMessageSendSuccessfully
();
}
view
.
enableMessageInput
();
},
throwable
->
{
view
.
enableMessageInput
();
Logger
.
report
(
throwable
);
@Override
public
void
loadMissedMessages
()
{
RocketChatApplication
appContext
=
RocketChatApplication
.
getInstance
();
JSONObject
openedRooms
=
RocketChatCache
.
INSTANCE
.
getOpenedRooms
();
if
(
openedRooms
.
has
(
roomId
))
{
try
{
JSONObject
room
=
openedRooms
.
getJSONObject
(
roomId
);
String
rid
=
room
.
optString
(
"rid"
);
long
ls
=
room
.
optLong
(
"ls"
);
methodCallHelper
.
loadMissedMessages
(
rid
,
ls
)
.
continueWith
(
new
LogIfError
());
}
catch
(
JSONException
e
)
{
RCLog
.
e
(
e
);
}
);
}
}
addSubscription
(
subscription
);
}
@Override
public
void
deleteMessage
(
@NonNull
Message
message
)
{
final
Disposable
subscription
=
messageInteractor
.
delete
(
message
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
@Override
public
void
onUnreadCount
()
{
final
Disposable
subscription
=
getRoomUserPair
()
.
flatMap
(
roomUserPair
->
messageInteractor
.
unreadCountFor
(
roomUserPair
.
first
,
roomUserPair
.
second
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
count
->
view
.
showUnreadCount
(
count
),
Logger:
:
report
);
private
String
buildReplyOrQuoteMarkdown
(
String
baseUrl
,
Message
message
,
boolean
justQuote
)
{
if
(
currentRoom
==
null
||
message
.
getUser
()
==
null
)
{
return
""
;
}
if
(
currentRoom
.
isDirectMessage
())
{
return
String
.
format
(
"[ ](%s/direct/%s?msg=%s) "
,
baseUrl
,
message
.
getUser
().
getUsername
(),
message
.
getId
());
}
else
{
return
String
.
format
(
"[ ](%s/channel/%s?msg=%s) %s"
,
baseUrl
,
currentRoom
.
getName
(),
message
.
getId
(),
justQuote
?
""
:
"@"
+
message
.
getUser
().
getUsername
()
+
" "
);
}
}
addSubscription
(
subscription
);
}
@Override
public
void
onMarkAsRead
()
{
final
Disposable
subscription
=
roomRepository
.
getById
(
roomId
)
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
filter
(
Room:
:
isAlert
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
room
->
methodCallHelper
.
readMessages
(
room
.
getRoomId
())
.
continueWith
(
new
LogIfError
()),
Logger:
:
report
);
@Override
public
void
sendMessage
(
String
messageText
)
{
view
.
disableMessageInput
();
final
Disposable
subscription
=
getRoomUserPair
()
.
flatMap
(
pair
->
messageInteractor
.
send
(
pair
.
first
,
pair
.
second
,
messageText
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(
success
)
{
view
.
onMessageSendSuccessfully
();
}
view
.
enableMessageInput
();
},
throwable
->
{
view
.
enableMessageInput
();
Logger
.
INSTANCE
.
report
(
throwable
);
}
);
addSubscription
(
subscription
);
}
@Override
public
void
resendMessage
(
@NonNull
Message
message
)
{
final
Disposable
subscription
=
getCurrentUser
()
.
flatMap
(
user
->
messageInteractor
.
resend
(
message
,
user
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
private
void
getRoomRoles
()
{
methodCallHelper
.
getRoomRoles
(
roomId
);
}
private
void
getRoomInfo
()
{
final
Disposable
subscription
=
roomRepository
.
getById
(
roomId
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
processRoom
,
Logger:
:
report
);
addSubscription
(
subscription
);
}
private
void
processRoom
(
Room
room
)
{
this
.
currentRoom
=
room
;
view
.
render
(
room
);
if
(
room
.
isDirectMessage
())
{
getUserByUsername
(
room
.
getName
());
addSubscription
(
subscription
);
}
}
private
void
getUserByUsername
(
String
username
)
{
final
Disposable
disposable
=
userRepository
.
getByUsername
(
username
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
view:
:
showUserStatus
,
Logger:
:
report
);
addSubscription
(
disposable
);
}
private
void
getRoomHistoryStateInfo
()
{
final
Disposable
subscription
=
roomRepository
.
getHistoryStateByRoomId
(
roomId
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
roomHistoryState
->
{
int
syncState
=
roomHistoryState
.
getSyncState
();
view
.
updateHistoryState
(
!
roomHistoryState
.
isComplete
(),
syncState
==
SyncState
.
SYNCED
||
syncState
==
SyncState
.
FAILED
);
},
Logger:
:
report
);
addSubscription
(
subscription
);
}
private
void
getMessages
()
{
final
Disposable
subscription
=
Flowable
.
zip
(
roomRepository
.
getById
(
roomId
),
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
().
toFlowable
().
cache
(),
Pair:
:
new
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
map
(
pair
->
{
view
.
setupWith
(
pair
.
second
.
orNull
());
return
pair
.
first
;
})
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
flatMap
(
messageInteractor:
:
getAllFrom
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
view:
:
showMessages
,
Logger:
:
report
);
@Override
public
void
updateMessage
(
@NonNull
Message
message
,
String
content
)
{
view
.
disableMessageInput
();
final
Disposable
subscription
=
getCurrentUser
()
.
flatMap
(
user
->
messageInteractor
.
update
(
message
,
user
,
content
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
success
->
{
if
(
success
)
{
view
.
onMessageSendSuccessfully
();
}
view
.
enableMessageInput
();
},
throwable
->
{
view
.
enableMessageInput
();
Logger
.
INSTANCE
.
report
(
throwable
);
}
);
addSubscription
(
subscription
);
}
addSubscription
(
subscription
);
}
private
void
getUserPreferences
()
{
final
Disposable
subscription
=
userRepository
.
getCurrent
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
filter
(
user
->
user
.
getSettings
()
!=
null
)
.
map
(
User:
:
getSettings
)
.
filter
(
settings
->
settings
.
getPreferences
()
!=
null
)
.
map
(
Settings:
:
getPreferences
)
.
distinctUntilChanged
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
preferences
->
{
if
(
preferences
.
isAutoImageLoad
())
{
view
.
autoloadImages
();
}
else
{
view
.
manualLoadImages
();
}
},
Logger:
:
report
);
@Override
public
void
deleteMessage
(
@NonNull
Message
message
)
{
final
Disposable
subscription
=
messageInteractor
.
delete
(
message
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
();
addSubscription
(
subscription
);
}
addSubscription
(
subscription
);
}
@Override
public
void
onUnreadCount
()
{
final
Disposable
subscription
=
getRoomUserPair
()
.
flatMap
(
roomUserPair
->
messageInteractor
.
unreadCountFor
(
roomUserPair
.
first
,
roomUserPair
.
second
))
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
count
->
view
.
showUnreadCount
(
count
),
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
getAbsoluteUrl
()
{
final
Disposable
subscription
=
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
it
->
view
.
setupWith
(
it
.
orNull
()),
Logger:
:
report
@Override
public
void
onMarkAsRead
()
{
final
Disposable
subscription
=
roomRepository
.
getById
(
roomId
)
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
filter
(
Room:
:
isAlert
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
room
->
methodCallHelper
.
readMessages
(
room
.
getRoomId
())
.
continueWith
(
new
LogIfError
()),
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
getRoomRoles
()
{
methodCallHelper
.
getRoomRoles
(
roomId
);
}
private
void
getRoomInfo
()
{
final
Disposable
subscription
=
roomRepository
.
getById
(
roomId
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
processRoom
,
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
processRoom
(
Room
room
)
{
this
.
currentRoom
=
room
;
view
.
render
(
room
);
if
(
room
.
isDirectMessage
())
{
getUserByUsername
(
room
.
getName
());
}
}
private
void
getUserByUsername
(
String
username
)
{
final
Disposable
disposable
=
userRepository
.
getByUsername
(
username
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
view:
:
showUserStatus
,
Logger
.
INSTANCE
::
report
);
addSubscription
(
disposable
);
}
private
void
getRoomHistoryStateInfo
()
{
final
Disposable
subscription
=
roomRepository
.
getHistoryStateByRoomId
(
roomId
)
.
distinctUntilChanged
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
roomHistoryState
->
{
int
syncState
=
roomHistoryState
.
getSyncState
();
view
.
updateHistoryState
(
!
roomHistoryState
.
isComplete
(),
syncState
==
SyncState
.
SYNCED
||
syncState
==
SyncState
.
FAILED
);
},
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
getMessages
()
{
final
Disposable
subscription
=
Flowable
.
zip
(
roomRepository
.
getById
(
roomId
),
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
().
toFlowable
().
cache
(),
Pair:
:
new
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
map
(
pair
->
{
view
.
setupWith
(
pair
.
second
.
orNull
());
return
pair
.
first
;
})
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
map
(
room
->
{
RocketChatCache
.
INSTANCE
.
addOpenedRoom
(
room
.
getRoomId
(),
room
.
getLastSeen
());
return
room
;
})
.
flatMap
(
messageInteractor:
:
getAllFrom
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
view:
:
showMessages
,
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
getUserPreferences
()
{
final
Disposable
subscription
=
userRepository
.
getCurrent
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
filter
(
user
->
user
.
getSettings
()
!=
null
)
.
map
(
User:
:
getSettings
)
.
filter
(
settings
->
settings
.
getPreferences
()
!=
null
)
.
map
(
Settings:
:
getPreferences
)
.
distinctUntilChanged
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
preferences
->
{
if
(
preferences
.
isAutoImageLoad
())
{
view
.
autoloadImages
();
}
else
{
view
.
manualLoadImages
();
}
},
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
void
getAbsoluteUrl
()
{
final
Disposable
subscription
=
absoluteUrlHelper
.
getRocketChatAbsoluteUrl
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
it
->
view
.
setupWith
(
it
.
orNull
()),
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
private
Single
<
Pair
<
Room
,
User
>>
getRoomUserPair
()
{
return
Single
.
zip
(
getSingleRoom
(),
getCurrentUser
(),
Pair:
:
new
);
}
addSubscription
(
subscription
);
}
private
Single
<
Pair
<
Room
,
User
>>
getRoomUserPair
()
{
return
Single
.
zip
(
getSingleRoom
(),
getCurrentUser
(),
Pair:
:
new
);
}
private
Single
<
Room
>
getSingleRoom
()
{
return
roomRepository
.
getById
(
roomId
)
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
toSingle
();
}
private
Single
<
User
>
getCurrentUser
()
{
return
userRepository
.
getCurrent
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
toSingle
();
}
private
Single
<
Room
>
getSingleRoom
()
{
return
roomRepository
.
getById
(
roomId
)
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
toSingle
();
}
private
Single
<
User
>
getCurrentUser
()
{
return
userRepository
.
getCurrent
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
.
firstElement
()
.
toSingle
();
}
}
app/src/main/java/chat/rocket/android/fragment/chatroom/dialog/MessageOptionsDialogFragment.java
View file @
cc23374c
...
...
@@ -36,134 +36,132 @@ import io.reactivex.disposables.Disposable;
public
class
MessageOptionsDialogFragment
extends
BottomSheetDialogFragment
{
public
final
static
String
ARG_MESSAGE_ID
=
"messageId"
;
public
final
static
String
ARG_MESSAGE_ID
=
"messageId"
;
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
private
OnMessageOptionSelectedListener
internalListener
=
new
OnMessageOptionSelectedListener
()
{
@Override
public
void
onEdit
(
Message
message
)
{
if
(
externalListener
!=
null
)
{
externalListener
.
onEdit
(
message
);
}
}
};
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
private
OnMessageOptionSelectedListener
internalListener
=
new
OnMessageOptionSelectedListener
()
{
@Override
public
void
onEdit
(
Message
message
)
{
if
(
externalListener
!=
null
)
{
externalListener
.
onEdit
(
message
);
}
}
};
private
OnMessageOptionSelectedListener
externalListener
=
null
;
private
OnMessageOptionSelectedListener
externalListener
=
null
;
public
static
MessageOptionsDialogFragment
create
(
@NonNull
Message
message
)
{
Bundle
bundle
=
new
Bundle
();
bundle
.
putString
(
ARG_MESSAGE_ID
,
message
.
getId
());
public
static
MessageOptionsDialogFragment
create
(
@NonNull
Message
message
)
{
Bundle
bundle
=
new
Bundle
();
bundle
.
putString
(
ARG_MESSAGE_ID
,
message
.
getId
());
MessageOptionsDialogFragment
messageOptionsDialogFragment
=
new
MessageOptionsDialogFragment
();
messageOptionsDialogFragment
.
setArguments
(
bundle
);
MessageOptionsDialogFragment
messageOptionsDialogFragment
=
new
MessageOptionsDialogFragment
()
;
messageOptionsDialogFragment
.
setArguments
(
bundle
);
return
messageOptionsDialogFragment
;
}
return
messageOptionsDialogFragment
;
}
public
void
setOnMessageOptionSelectedListener
(
OnMessageOptionSelectedListener
onMessageOptionSelectedListener
)
{
externalListener
=
onMessageOptionSelectedListener
;
}
public
void
setOnMessageOptionSelectedListener
(
OnMessageOptionSelectedListener
onMessageOptionSelectedListener
)
{
externalListener
=
onMessageOptionSelectedListener
;
}
@NonNull
@Override
public
Dialog
onCreateDialog
(
Bundle
savedInstanceState
)
{
BottomSheetDialog
bottomSheetDialog
=
new
BottomSheetDialog
(
getContext
());
@NonNull
@Override
public
Dialog
onCreateDialog
(
Bundle
savedInstanceState
)
{
BottomSheetDialog
bottomSheetDialog
=
new
BottomSheetDialog
(
getContext
());
bottomSheetDialog
.
setContentView
(
R
.
layout
.
dialog_message_options
);
bottomSheetDialog
.
setContentView
(
R
.
layout
.
dialog_message_options
);
TextView
info
=
(
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
);
TextView
info
=
(
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
);
Bundle
args
=
getArguments
();
if
(
args
==
null
||
!
args
.
containsKey
(
ARG_MESSAGE_ID
))
{
info
.
setText
(
R
.
string
.
message_options_no_message_info
);
}
else
{
setUpDialog
(
bottomSheetDialog
,
args
.
getString
(
ARG_MESSAGE_ID
));
}
Bundle
args
=
getArguments
();
if
(
args
==
null
||
!
args
.
containsKey
(
ARG_MESSAGE_ID
))
{
info
.
setText
(
R
.
string
.
message_options_no_message_info
);
}
else
{
setUpDialog
(
bottomSheetDialog
,
args
.
getString
(
ARG_MESSAGE_ID
));
return
bottomSheetDialog
;
}
return
bottomSheetDialog
;
}
@Override
public
void
onDismiss
(
DialogInterface
dialog
)
{
compositeDisposable
.
clear
();
super
.
onDismiss
(
dialog
);
}
private
void
setUpDialog
(
final
BottomSheetDialog
bottomSheetDialog
,
String
messageId
)
{
RocketChatCache
cache
=
new
RocketChatCache
(
bottomSheetDialog
.
getContext
());
String
hostname
=
cache
.
getSelectedServerHostname
();
EditMessageInteractor
editMessageInteractor
=
getEditMessageInteractor
(
hostname
);
MessageRepository
messageRepository
=
new
RealmMessageRepository
(
hostname
);
Disposable
disposable
=
messageRepository
.
getById
(
messageId
)
.
flatMap
(
it
->
{
if
(!
it
.
isPresent
())
{
return
Single
.
just
(
Pair
.<
Message
,
Boolean
>
create
(
null
,
false
));
}
Message
message
=
it
.
get
();
return
Single
.
zip
(
Single
.
just
(
message
),
editMessageInteractor
.
isAllowed
(
message
),
Pair:
:
create
);
})
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
pair
->
{
if
(
pair
.
second
)
{
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
)
.
setVisibility
(
View
.
GONE
);
View
editView
=
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_edit_action
);
editView
.
setVisibility
(
View
.
VISIBLE
);
editView
.
setOnClickListener
(
view
->
internalListener
.
onEdit
(
pair
.
first
));
}
else
{
((
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
))
.
setText
(
R
.
string
.
message_options_no_permissions_info
);
}
},
throwable
->
{
((
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
))
.
setText
(
R
.
string
.
message_options_no_message_info
);
Logger
.
report
(
throwable
);
}
@Override
public
void
onDismiss
(
DialogInterface
dialog
)
{
compositeDisposable
.
clear
();
super
.
onDismiss
(
dialog
);
}
private
void
setUpDialog
(
final
BottomSheetDialog
bottomSheetDialog
,
String
messageId
)
{
String
hostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
EditMessageInteractor
editMessageInteractor
=
getEditMessageInteractor
(
hostname
);
MessageRepository
messageRepository
=
new
RealmMessageRepository
(
hostname
);
Disposable
disposable
=
messageRepository
.
getById
(
messageId
)
.
flatMap
(
it
->
{
if
(!
it
.
isPresent
())
{
return
Single
.
just
(
Pair
.<
Message
,
Boolean
>
create
(
null
,
false
));
}
Message
message
=
it
.
get
();
return
Single
.
zip
(
Single
.
just
(
message
),
editMessageInteractor
.
isAllowed
(
message
),
Pair:
:
create
);
})
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
pair
->
{
if
(
pair
.
second
)
{
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
)
.
setVisibility
(
View
.
GONE
);
View
editView
=
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_edit_action
);
editView
.
setVisibility
(
View
.
VISIBLE
);
editView
.
setOnClickListener
(
view
->
internalListener
.
onEdit
(
pair
.
first
));
}
else
{
((
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
))
.
setText
(
R
.
string
.
message_options_no_permissions_info
);
}
},
throwable
->
{
((
TextView
)
bottomSheetDialog
.
findViewById
(
R
.
id
.
message_options_info
))
.
setText
(
R
.
string
.
message_options_no_message_info
);
Logger
.
INSTANCE
.
report
(
throwable
);
}
);
compositeDisposable
.
add
(
disposable
);
}
private
EditMessageInteractor
getEditMessageInteractor
(
String
hostname
)
{
UserRepository
userRepository
=
new
RealmUserRepository
(
hostname
);
RoomRoleRepository
roomRoleRepository
=
new
RealmRoomRoleRepository
(
hostname
);
PermissionRepository
permissionRepository
=
new
RealmPermissionRepository
(
hostname
);
PermissionInteractor
permissionInteractor
=
new
PermissionInteractor
(
userRepository
,
roomRoleRepository
,
permissionRepository
);
compositeDisposable
.
add
(
disposable
);
}
private
EditMessageInteractor
getEditMessageInteractor
(
String
hostname
)
{
UserRepository
userRepository
=
new
RealmUserRepository
(
hostname
);
RoomRoleRepository
roomRoleRepository
=
new
RealmRoomRoleRepository
(
hostname
);
PermissionRepository
permissionRepository
=
new
RealmPermissionRepository
(
hostname
);
PermissionInteractor
permissionInteractor
=
new
PermissionInteractor
(
userRepository
,
roomRoleRepository
,
permissionRepository
);
MessageRepository
messageRepository
=
new
RealmMessageRepository
(
hostname
);
RoomRepository
roomRepository
=
new
RealmRoomRepository
(
hostname
);
PublicSettingRepository
publicSettingRepository
=
new
RealmPublicSettingRepository
(
hostname
);
return
new
EditMessageInteractor
(
permissionInteractor
,
userRepository
,
messageRepository
,
roomRepository
,
publicSettingRepository
);
}
public
interface
OnMessageOptionSelectedListener
{
void
onEdit
(
Message
message
);
}
MessageRepository
messageRepository
=
new
RealmMessageRepository
(
hostname
);
RoomRepository
roomRepository
=
new
RealmRoomRepository
(
hostname
);
PublicSettingRepository
publicSettingRepository
=
new
RealmPublicSettingRepository
(
hostname
);
return
new
EditMessageInteractor
(
permissionInteractor
,
userRepository
,
messageRepository
,
roomRepository
,
publicSettingRepository
);
}
public
interface
OnMessageOptionSelectedListener
{
void
onEdit
(
Message
message
);
}
}
app/src/main/java/chat/rocket/android/fragment/oauth/OAuthPresenter.java
View file @
cc23374c
...
...
@@ -37,7 +37,7 @@ public class OAuthPresenter extends BasePresenter<OAuthContract.View>
view
.
close
();
}
},
Logger:
:
report
Logger
.
INSTANCE
::
report
)
);
}
...
...
app/src/main/java/chat/rocket/android/fragment/server_config/AbstractServerConfigFragment.java
View file @
cc23374c
...
...
@@ -8,7 +8,7 @@ import chat.rocket.android.R;
import
chat.rocket.android.fragment.AbstractFragment
;
import
chat.rocket.android.helper.TextUtils
;
abstract
class
AbstractServerConfigFragment
extends
AbstractFragment
{
public
abstract
class
AbstractServerConfigFragment
extends
AbstractFragment
{
public
static
final
String
KEY_HOSTNAME
=
"hostname"
;
protected
String
hostname
;
...
...
app/src/main/java/chat/rocket/android/fragment/server_config/LoginContract.java
View file @
cc23374c
...
...
@@ -7,21 +7,25 @@ import chat.rocket.core.models.LoginServiceConfiguration;
public
interface
LoginContract
{
interface
View
extends
BaseContract
.
View
{
interface
View
extends
BaseContract
.
View
{
void
showLoader
();
void
showLoader
();
void
hideLoader
();
void
hideLoader
();
void
showError
(
String
message
);
void
showError
(
String
message
);
void
showLoginServices
(
List
<
LoginServiceConfiguration
>
loginServiceList
);
void
showLoginServices
(
List
<
LoginServiceConfiguration
>
loginServiceList
);
void
showTwoStepAuth
();
}
void
showTwoStepAuth
();
interface
Presenter
extends
BaseContract
.
Presenter
<
View
>
{
void
goBack
();
}
void
login
(
String
username
,
String
password
);
}
interface
Presenter
extends
BaseContract
.
Presenter
<
View
>
{
void
login
(
String
username
,
String
password
);
void
goBack
();
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/LoginFragment.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
import
android.os.Bundle
;
import
android.support.annotation.Nullable
;
import
android.support.constraint.ConstraintLayout
;
import
android.support.design.widget.Snackbar
;
import
android.support.v4.app.Fragment
;
import
android.view.View
;
import
android.widget.Button
;
import
android.widget.TextView
;
import
java.util.HashMap
;
import
java.util.List
;
import
chat.rocket.android.R
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.layouthelper.oauth.OAuthProviderInfo
;
import
chat.rocket.android.log.RCLog
;
import
chat.rocket.core.models.LoginServiceConfiguration
;
import
chat.rocket.persistence.realm.repositories.RealmLoginServiceConfigurationRepository
;
import
chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository
;
/**
* Login screen.
*/
public
class
LoginFragment
extends
AbstractServerConfigFragment
implements
LoginContract
.
View
{
private
LoginContract
.
Presenter
presenter
;
private
ConstraintLayout
container
;
private
View
waitingView
;
private
TextView
txtUsername
;
private
TextView
txtPasswd
;
@Override
protected
int
getLayout
()
{
return
R
.
layout
.
fragment_login
;
}
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
presenter
=
new
LoginPresenter
(
new
RealmLoginServiceConfigurationRepository
(
hostname
),
new
RealmPublicSettingRepository
(
hostname
),
new
MethodCallHelper
(
getContext
(),
hostname
)
);
}
@Override
protected
void
onSetupView
()
{
container
=
rootView
.
findViewById
(
R
.
id
.
container
);
Button
btnEmail
=
rootView
.
findViewById
(
R
.
id
.
btn_login_with_email
);
Button
btnUserRegistration
=
rootView
.
findViewById
(
R
.
id
.
btn_user_registration
);
txtUsername
=
rootView
.
findViewById
(
R
.
id
.
editor_username
);
txtPasswd
=
rootView
.
findViewById
(
R
.
id
.
editor_passwd
);
waitingView
=
rootView
.
findViewById
(
R
.
id
.
waiting
);
btnEmail
.
setOnClickListener
(
view
->
presenter
.
login
(
txtUsername
.
getText
().
toString
(),
txtPasswd
.
getText
().
toString
()));
btnUserRegistration
.
setOnClickListener
(
view
->
UserRegistrationDialogFragment
.
create
(
hostname
,
txtUsername
.
getText
().
toString
(),
txtPasswd
.
getText
().
toString
())
.
show
(
getFragmentManager
(),
"UserRegistrationDialogFragment"
));
}
@Override
public
void
showLoader
()
{
container
.
setVisibility
(
View
.
GONE
);
waitingView
.
setVisibility
(
View
.
VISIBLE
);
}
@Override
public
void
hideLoader
()
{
waitingView
.
setVisibility
(
View
.
GONE
);
container
.
setVisibility
(
View
.
VISIBLE
);
}
@Override
public
void
showError
(
String
message
)
{
Snackbar
.
make
(
rootView
,
message
,
Snackbar
.
LENGTH_SHORT
).
show
();
}
@Override
public
void
showLoginServices
(
List
<
LoginServiceConfiguration
>
loginServiceList
)
{
HashMap
<
String
,
View
>
viewMap
=
new
HashMap
<>();
HashMap
<
String
,
Boolean
>
supportedMap
=
new
HashMap
<>();
for
(
OAuthProviderInfo
info
:
OAuthProviderInfo
.
LIST
)
{
viewMap
.
put
(
info
.
serviceName
,
rootView
.
findViewById
(
info
.
buttonId
));
supportedMap
.
put
(
info
.
serviceName
,
false
);
}
for
(
LoginServiceConfiguration
authProvider
:
loginServiceList
)
{
for
(
OAuthProviderInfo
info
:
OAuthProviderInfo
.
LIST
)
{
if
(!
supportedMap
.
get
(
info
.
serviceName
)
&&
info
.
serviceName
.
equals
(
authProvider
.
getService
()))
{
supportedMap
.
put
(
info
.
serviceName
,
true
);
viewMap
.
get
(
info
.
serviceName
).
setOnClickListener
(
view
->
{
Fragment
fragment
=
null
;
try
{
fragment
=
info
.
fragmentClass
.
newInstance
();
}
catch
(
Exception
exception
)
{
RCLog
.
w
(
exception
,
"failed to build new Fragment"
);
}
if
(
fragment
!=
null
)
{
Bundle
args
=
new
Bundle
();
args
.
putString
(
"hostname"
,
hostname
);
fragment
.
setArguments
(
args
);
showFragmentWithBackStack
(
fragment
);
}
});
viewMap
.
get
(
info
.
serviceName
).
setVisibility
(
View
.
VISIBLE
);
}
}
}
for
(
OAuthProviderInfo
info
:
OAuthProviderInfo
.
LIST
)
{
if
(!
supportedMap
.
get
(
info
.
serviceName
))
{
viewMap
.
get
(
info
.
serviceName
).
setVisibility
(
View
.
GONE
);
}
}
}
@Override
public
void
showTwoStepAuth
()
{
showFragmentWithBackStack
(
TwoStepAuthFragment
.
create
(
hostname
,
txtUsername
.
getText
().
toString
(),
txtPasswd
.
getText
().
toString
()
));
}
@Override
public
void
onResume
()
{
super
.
onResume
();
presenter
.
bindView
(
this
);
}
@Override
public
void
onPause
()
{
presenter
.
release
();
super
.
onPause
();
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/LoginFragment.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.fragment.server_config
import
android.os.Bundle
import
android.support.constraint.ConstraintLayout
import
android.support.design.widget.Snackbar
import
android.support.v4.app.Fragment
import
android.view.View
import
android.widget.Button
import
android.widget.TextView
import
chat.rocket.android.R
import
chat.rocket.android.api.MethodCallHelper
import
chat.rocket.android.layouthelper.oauth.OAuthProviderInfo
import
chat.rocket.android.log.RCLog
import
chat.rocket.core.models.LoginServiceConfiguration
import
chat.rocket.persistence.realm.repositories.RealmLoginServiceConfigurationRepository
import
chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository
import
java.util.*
/**
* Login screen.
*/
class
LoginFragment
:
AbstractServerConfigFragment
(),
LoginContract
.
View
{
private
lateinit
var
presenter
:
LoginContract
.
Presenter
private
lateinit
var
container
:
ConstraintLayout
private
lateinit
var
waitingView
:
View
private
lateinit
var
txtUsername
:
TextView
private
lateinit
var
txtPasswd
:
TextView
override
fun
getLayout
():
Int
{
return
R
.
layout
.
fragment_login
}
override
fun
onCreate
(
savedInstanceState
:
Bundle
?)
{
super
.
onCreate
(
savedInstanceState
)
presenter
=
LoginPresenter
(
RealmLoginServiceConfigurationRepository
(
hostname
),
RealmPublicSettingRepository
(
hostname
),
MethodCallHelper
(
context
,
hostname
)
)
}
override
fun
onSetupView
()
{
container
=
rootView
.
findViewById
(
R
.
id
.
container
)
val
btnEmail
=
rootView
.
findViewById
<
Button
>(
R
.
id
.
btn_login_with_email
)
val
btnUserRegistration
=
rootView
.
findViewById
<
Button
>(
R
.
id
.
btn_user_registration
)
txtUsername
=
rootView
.
findViewById
(
R
.
id
.
editor_username
)
txtPasswd
=
rootView
.
findViewById
(
R
.
id
.
editor_passwd
)
waitingView
=
rootView
.
findViewById
(
R
.
id
.
waiting
)
btnEmail
.
setOnClickListener
{
_
->
presenter
.
login
(
txtUsername
.
text
.
toString
(),
txtPasswd
.
text
.
toString
())
}
btnUserRegistration
.
setOnClickListener
{
_
->
UserRegistrationDialogFragment
.
create
(
hostname
,
txtUsername
.
text
.
toString
(),
txtPasswd
.
text
.
toString
())
.
show
(
fragmentManager
!!
,
"UserRegistrationDialogFragment"
)
}
}
override
fun
showLoader
()
{
container
.
visibility
=
View
.
GONE
waitingView
.
visibility
=
View
.
VISIBLE
}
override
fun
hideLoader
()
{
waitingView
.
visibility
=
View
.
GONE
container
.
visibility
=
View
.
VISIBLE
}
override
fun
showError
(
message
:
String
)
{
Snackbar
.
make
(
rootView
,
message
,
Snackbar
.
LENGTH_SHORT
).
show
()
}
override
fun
showLoginServices
(
loginServiceList
:
List
<
LoginServiceConfiguration
>)
{
val
viewMap
=
HashMap
<
String
,
View
>()
val
supportedMap
=
HashMap
<
String
,
Boolean
>()
for
(
info
in
OAuthProviderInfo
.
LIST
)
{
viewMap
.
put
(
info
.
serviceName
,
rootView
.
findViewById
(
info
.
buttonId
))
supportedMap
.
put
(
info
.
serviceName
,
false
)
}
for
(
authProvider
in
loginServiceList
)
{
for
(
info
in
OAuthProviderInfo
.
LIST
)
{
if
(
supportedMap
[
info
.
serviceName
]
==
false
&&
info
.
serviceName
==
authProvider
.
service
)
{
supportedMap
.
put
(
info
.
serviceName
,
true
)
viewMap
[
info
.
serviceName
]
?.
setOnClickListener
{
_
->
var
fragment
:
Fragment
?
=
null
try
{
fragment
=
info
.
fragmentClass
.
newInstance
()
}
catch
(
exception
:
Exception
)
{
RCLog
.
w
(
exception
,
"failed to build new Fragment"
)
}
fragment
?.
let
{
val
args
=
Bundle
()
args
.
putString
(
"hostname"
,
hostname
)
fragment
.
arguments
=
args
showFragmentWithBackStack
(
fragment
)
}
}
viewMap
[
info
.
serviceName
]
?.
visibility
=
View
.
VISIBLE
}
}
}
for
(
info
in
OAuthProviderInfo
.
LIST
)
{
if
(
supportedMap
[
info
.
serviceName
]
==
false
)
{
viewMap
[
info
.
serviceName
]
?.
visibility
=
View
.
GONE
}
}
}
override
fun
showTwoStepAuth
()
{
showFragmentWithBackStack
(
TwoStepAuthFragment
.
create
(
hostname
,
txtUsername
.
text
.
toString
(),
txtPasswd
.
text
.
toString
()
))
}
override
fun
onResume
()
{
super
.
onResume
()
presenter
.
bindView
(
this
)
}
override
fun
onPause
()
{
presenter
.
release
()
super
.
onPause
()
}
override
fun
goBack
()
{
presenter
.
goBack
()
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/LoginPresenter.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
import
android.support.annotation.NonNull
;
import
com.hadisatrio.optional.Optional
;
import
bolts.Task
;
import
chat.rocket.android.BackgroundLooper
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.api.TwoStepAuthException
;
import
chat.rocket.android.helper.Logger
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.shared.BasePresenter
;
import
chat.rocket.core.PublicSettingsConstants
;
import
chat.rocket.core.models.PublicSetting
;
import
chat.rocket.core.repositories.LoginServiceConfigurationRepository
;
import
chat.rocket.core.repositories.PublicSettingRepository
;
import
io.reactivex.android.schedulers.AndroidSchedulers
;
public
class
LoginPresenter
extends
BasePresenter
<
LoginContract
.
View
>
implements
LoginContract
.
Presenter
{
private
final
LoginServiceConfigurationRepository
loginServiceConfigurationRepository
;
private
final
PublicSettingRepository
publicSettingRepository
;
private
final
MethodCallHelper
methodCallHelper
;
public
LoginPresenter
(
LoginServiceConfigurationRepository
loginServiceConfigurationRepository
,
PublicSettingRepository
publicSettingRepository
,
MethodCallHelper
methodCallHelper
)
{
this
.
loginServiceConfigurationRepository
=
loginServiceConfigurationRepository
;
this
.
publicSettingRepository
=
publicSettingRepository
;
this
.
methodCallHelper
=
methodCallHelper
;
}
@Override
public
void
bindView
(
@NonNull
LoginContract
.
View
view
)
{
super
.
bindView
(
view
);
getLoginServices
();
}
@Override
public
void
login
(
String
username
,
String
password
)
{
if
(
TextUtils
.
isEmpty
(
username
)
||
TextUtils
.
isEmpty
(
password
))
{
return
;
}
view
.
showLoader
();
addSubscription
(
publicSettingRepository
.
getById
(
PublicSettingsConstants
.
LDAP
.
ENABLE
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
publicSettingOptional
->
doLogin
(
username
,
password
,
publicSettingOptional
),
Logger:
:
report
)
);
}
private
void
getLoginServices
()
{
addSubscription
(
loginServiceConfigurationRepository
.
getAll
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
loginServiceConfigurations
->
view
.
showLoginServices
(
loginServiceConfigurations
),
Logger:
:
report
)
);
}
private
void
doLogin
(
String
username
,
String
password
,
Optional
<
PublicSetting
>
optional
)
{
call
(
username
,
password
,
optional
)
.
continueWith
(
task
->
{
if
(
task
.
isFaulted
())
{
view
.
hideLoader
();
final
Exception
error
=
task
.
getError
();
if
(
error
instanceof
TwoStepAuthException
)
{
view
.
showTwoStepAuth
();
}
else
{
view
.
showError
(
error
.
getMessage
());
}
}
return
null
;
},
Task
.
UI_THREAD_EXECUTOR
);
}
private
Task
<
Void
>
call
(
String
username
,
String
password
,
Optional
<
PublicSetting
>
optional
)
{
if
(
optional
.
isPresent
()
&&
optional
.
get
().
getValueAsBoolean
())
{
return
methodCallHelper
.
loginWithLdap
(
username
,
password
);
}
return
methodCallHelper
.
loginWithEmail
(
username
,
password
);
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/LoginPresenter.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.fragment.server_config
import
bolts.Continuation
import
bolts.Task
import
chat.rocket.android.BackgroundLooper
import
chat.rocket.android.LaunchUtil
import
chat.rocket.android.RocketChatApplication
import
chat.rocket.android.RocketChatCache
import
chat.rocket.android.api.MethodCallHelper
import
chat.rocket.android.api.TwoStepAuthException
import
chat.rocket.android.helper.Logger
import
chat.rocket.android.helper.TextUtils
import
chat.rocket.android.service.ConnectivityManager
import
chat.rocket.android.shared.BasePresenter
import
chat.rocket.core.PublicSettingsConstants
import
chat.rocket.core.models.PublicSetting
import
chat.rocket.core.repositories.LoginServiceConfigurationRepository
import
chat.rocket.core.repositories.PublicSettingRepository
import
com.hadisatrio.optional.Optional
import
io.reactivex.Completable
import
io.reactivex.android.schedulers.AndroidSchedulers
import
io.reactivex.rxkotlin.subscribeBy
class
LoginPresenter
(
private
val
loginServiceConfigurationRepository
:
LoginServiceConfigurationRepository
,
private
val
publicSettingRepository
:
PublicSettingRepository
,
private
val
methodCallHelper
:
MethodCallHelper
)
:
BasePresenter
<
LoginContract
.
View
>(),
LoginContract
.
Presenter
{
override
fun
bindView
(
view
:
LoginContract
.
View
)
{
super
.
bindView
(
view
)
getLoginServices
()
}
override
fun
goBack
()
{
val
context
=
RocketChatApplication
.
getInstance
()
val
hostname
=
RocketChatCache
.
getSelectedServerHostname
()
hostname
?.
let
{
ConnectivityManager
.
getInstance
(
context
).
removeServer
(
hostname
)
RocketChatCache
.
clearSelectedHostnameReferences
()
}
LaunchUtil
.
showMainActivity
(
context
)
}
override
fun
login
(
username
:
String
,
password
:
String
)
{
if
(
TextUtils
.
isEmpty
(
username
)
||
TextUtils
.
isEmpty
(
password
))
{
return
}
view
.
showLoader
()
addSubscription
(
publicSettingRepository
.
getById
(
PublicSettingsConstants
.
LDAP
.
ENABLE
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribeBy
(
onSuccess
=
{
publicSettingOptional
->
doLogin
(
username
,
password
,
publicSettingOptional
)
},
onError
=
{
Logger
.
report
(
it
)
}
)
)
}
private
fun
getLoginServices
()
{
addSubscription
(
loginServiceConfigurationRepository
.
all
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribeBy
(
onNext
=
{
loginServiceConfigurations
->
view
.
showLoginServices
(
loginServiceConfigurations
);
},
onError
=
{
Logger
.
report
(
it
)
}
)
)
}
private
fun
doLogin
(
username
:
String
,
password
:
String
,
optional
:
Optional
<
PublicSetting
>)
{
addSubscription
(
Completable
.
create
{
call
(
username
,
password
,
optional
)
.
continueWith
(
object
:
Continuation
<
Void
,
Any
?>
{
override
fun
then
(
task
:
Task
<
Void
>?):
Any
?
{
if
(
task
!=
null
&&
task
.
isFaulted
())
{
view
.
hideLoader
()
val
error
=
task
.
getError
()
error
?.
let
{
if
(
error
is
TwoStepAuthException
)
{
view
.
showTwoStepAuth
()
}
else
{
view
.
showError
(
error
.
message
)
}
}
return
Completable
.
complete
()
}
return
null
}
},
Task
.
UI_THREAD_EXECUTOR
)
}.
subscribeBy
(
onError
=
{
view
.
showError
(
it
.
message
)
}
)
)
}
private
fun
call
(
username
:
String
,
password
:
String
,
optional
:
Optional
<
PublicSetting
>):
Task
<
Void
>
{
return
if
(
optional
.
isPresent
&&
optional
.
get
().
valueAsBoolean
)
{
methodCallHelper
.
loginWithLdap
(
username
,
password
)
}
else
methodCallHelper
.
loginWithEmail
(
username
,
password
)
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/RetryLoginPresenter.java
View file @
cc23374c
...
...
@@ -52,7 +52,7 @@ public class RetryLoginPresenter extends BasePresenter<RetryLoginContract.View>
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
onSession
,
Logger:
:
report
Logger
.
INSTANCE
::
report
)
);
}
...
...
app/src/main/java/chat/rocket/android/fragment/sidebar/SidebarMainFragment.java
View file @
cc23374c
...
...
@@ -24,7 +24,6 @@ import java.util.List;
import
chat.rocket.android.BuildConfig
;
import
chat.rocket.android.R
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.activity.MainActivity
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.fragment.AbstractFragment
;
...
...
@@ -94,13 +93,11 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
new
SessionInteractor
(
new
RealmSessionRepository
(
hostname
))
);
RocketChatCache
rocketChatCache
=
new
RocketChatCache
(
getContext
().
getApplicationContext
());
presenter
=
new
SidebarMainPresenter
(
hostname
,
new
RoomInteractor
(
new
RealmRoomRepository
(
hostname
)),
userRepository
,
rocketChatCache
,
absoluteUrlHelper
,
new
MethodCallHelper
(
getContext
(),
hostname
),
new
RealmSpotlightRepository
(
hostname
)
...
...
@@ -239,7 +236,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
.
compose
(
bindToLifecycle
())
.
subscribe
(
this
::
showUserActionContainer
,
Logger:
:
report
Logger
.
INSTANCE
::
report
);
}
...
...
app/src/main/java/chat/rocket/android/fragment/sidebar/SidebarMainPresenter.java
View file @
cc23374c
...
...
@@ -38,7 +38,6 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
private
final
String
hostname
;
private
final
RoomInteractor
roomInteractor
;
private
final
UserRepository
userRepository
;
private
final
RocketChatCache
rocketChatCache
;
private
final
AbsoluteUrlHelper
absoluteUrlHelper
;
private
final
MethodCallHelper
methodCallHelper
;
private
SpotlightRepository
realmSpotlightRepository
;
...
...
@@ -47,14 +46,12 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
public
SidebarMainPresenter
(
String
hostname
,
RoomInteractor
roomInteractor
,
UserRepository
userRepository
,
RocketChatCache
rocketChatCache
,
AbsoluteUrlHelper
absoluteUrlHelper
,
MethodCallHelper
methodCallHelper
,
RealmSpotlightRepository
realmSpotlightRepository
)
{
this
.
hostname
=
hostname
;
this
.
roomInteractor
=
roomInteractor
;
this
.
userRepository
=
userRepository
;
this
.
rocketChatCache
=
rocketChatCache
;
this
.
absoluteUrlHelper
=
absoluteUrlHelper
;
this
.
methodCallHelper
=
methodCallHelper
;
this
.
realmSpotlightRepository
=
realmSpotlightRepository
;
...
...
@@ -80,14 +77,14 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
)
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
pair
->
view
.
show
(
pair
.
first
.
orNull
()),
Logger:
:
report
);
.
subscribe
(
pair
->
view
.
show
(
pair
.
first
.
orNull
()),
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
@Override
public
void
onRoomSelected
(
RoomSidebar
roomSidebar
)
{
rocketChatCache
.
setSelectedRoomId
(
roomSidebar
.
getRoomId
());
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
roomSidebar
.
getRoomId
());
}
@Override
...
...
@@ -103,7 +100,7 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
methodCallHelper
.
createDirectMessage
(
username
)
.
continueWithTask
(
task
->
{
if
(
task
.
isCompleted
())
{
rocketChatCache
.
setSelectedRoomId
(
task
.
getResult
());
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
task
.
getResult
());
}
return
null
;
});
...
...
@@ -111,7 +108,7 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
methodCallHelper
.
joinRoom
(
spotlight
.
getId
())
.
continueWithTask
(
task
->
{
if
(
task
.
isCompleted
())
{
rocketChatCache
.
setSelectedRoomId
(
spotlight
.
getId
());
RocketChatCache
.
INSTANCE
.
setSelectedRoomId
(
spotlight
.
getId
());
}
return
null
;
});
...
...
@@ -142,7 +139,7 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
public
void
onLogout
(
Continuation
<
Void
,
Object
>
continuation
)
{
methodCallHelper
.
logout
().
continueWith
(
task
->
{
if
(
task
.
isFaulted
())
{
Logger
.
report
(
task
.
getError
());
Logger
.
INSTANCE
.
report
(
task
.
getError
());
return
Task
.
forError
(
task
.
getError
());
}
return
task
.
onSuccess
(
continuation
);
...
...
@@ -157,12 +154,12 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
}
clearSubscriptions
();
String
currentHostname
=
rocketChatCache
.
getSelectedServerHostname
();
String
currentHostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
RealmHelper
realmHelper
=
RealmStore
.
getOrCreate
(
currentHostname
);
return
realmHelper
.
executeTransaction
(
realm
->
{
rocketChatCache
.
removeHostname
(
currentHostname
);
rocketChatCache
.
removeSelectedRoomId
(
currentHostname
);
rocketChatCache
.
setSelectedServerHostname
(
rocketChatCache
.
getFirstLoggedHostnameIfAny
());
RocketChatCache
.
INSTANCE
.
removeHostname
(
currentHostname
);
RocketChatCache
.
INSTANCE
.
removeSelectedRoomId
(
currentHostname
);
RocketChatCache
.
INSTANCE
.
setSelectedServerHostname
(
RocketChatCache
.
INSTANCE
.
getFirstLoggedHostnameIfAny
());
realm
.
executeTransactionAsync
(
Realm:
:
deleteAll
);
view
.
onPreparedToLogOut
();
ConnectivityManager
.
getInstance
(
RocketChatApplication
.
getInstance
())
...
...
@@ -183,7 +180,7 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
.
distinctUntilChanged
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
processRooms
,
Logger:
:
report
);
.
subscribe
(
this
::
processRooms
,
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
...
...
@@ -227,7 +224,7 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
.
distinctUntilChanged
()
.
subscribeOn
(
AndroidSchedulers
.
from
(
BackgroundLooper
.
get
()))
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
processUsers
,
Logger:
:
report
);
.
subscribe
(
this
::
processUsers
,
Logger
.
INSTANCE
::
report
);
addSubscription
(
subscription
);
}
...
...
app/src/main/java/chat/rocket/android/fragment/sidebar/dialog/AddChannelDialogFragment.java
View file @
cc23374c
...
...
@@ -47,7 +47,7 @@ public class AddChannelDialogFragment extends AbstractAddRoomDialogFragment {
.
compose
(
bindToLifecycle
())
.
subscribe
(
buttonAddChannel:
:
setEnabled
,
Logger:
:
report
Logger
.
INSTANCE
::
report
);
buttonAddChannel
.
setOnClickListener
(
view
->
createRoom
());
...
...
app/src/main/java/chat/rocket/android/fragment/sidebar/dialog/AddDirectMessageDialogFragment.java
View file @
cc23374c
...
...
@@ -68,7 +68,7 @@ public class AddDirectMessageDialogFragment extends AbstractAddRoomDialogFragmen
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
setupView
,
Logger:
:
report
Logger
.
INSTANCE
::
report
)
);
...
...
@@ -77,7 +77,7 @@ public class AddDirectMessageDialogFragment extends AbstractAddRoomDialogFragmen
.
compose
(
bindToLifecycle
())
.
subscribe
(
buttonAddDirectMessage:
:
setEnabled
,
Logger:
:
report
Logger
.
INSTANCE
::
report
);
buttonAddDirectMessage
.
setOnClickListener
(
view
->
createRoom
());
...
...
app/src/main/java/chat/rocket/android/helper/Logger.java
deleted
100644 → 0
View file @
8407c568
package
chat
.
rocket
.
android
.
helper
;
import
com.crashlytics.android.Crashlytics
;
import
com.google.firebase.crash.FirebaseCrash
;
public
class
Logger
{
public
static
void
report
(
Throwable
throwable
)
{
FirebaseCrash
.
report
(
throwable
);
Crashlytics
.
logException
(
throwable
);
}
}
app/src/main/java/chat/rocket/android/helper/Logger.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.helper
import
chat.rocket.android.BuildConfig
import
com.crashlytics.android.Crashlytics
import
com.google.firebase.crash.FirebaseCrash
object
Logger
{
fun
report
(
throwable
:
Throwable
)
{
if
(
BuildConfig
.
DEBUG
)
{
throwable
.
printStackTrace
()
}
FirebaseCrash
.
report
(
throwable
)
Crashlytics
.
logException
(
throwable
)
}
}
app/src/main/java/chat/rocket/android/layouthelper/chatroom/MessagePopup.java
View file @
cc23374c
...
...
@@ -48,9 +48,7 @@ public class MessagePopup {
}
private
void
showAvailableActionsOnly
(
Context
context
)
{
RocketChatCache
cache
=
new
RocketChatCache
(
context
.
getApplicationContext
());
String
hostname
=
cache
.
getSelectedServerHostname
();
String
hostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
EditMessageInteractor
editMessageInteractor
=
getEditMessageInteractor
(
hostname
);
...
...
@@ -102,7 +100,7 @@ public class MessagePopup {
.
create
()
.
show
();
},
Logger:
:
report
Logger
.
INSTANCE
::
report
);
compositeDisposable
.
add
(
disposable
);
}
...
...
@@ -167,7 +165,7 @@ public class MessagePopup {
}
public
MessagePopup
setDeleteAction
(
ActionListener
actionListener
)
{
DELETE_ACTION_INFO
.
actionListener
=
actionListener
;
DELETE_ACTION_INFO
.
actionListener
=
actionListener
;
return
singleton
;
}
...
...
app/src/main/java/chat/rocket/android/layouthelper/chatroom/roomlist/RoomListItemViewHolder.java
View file @
cc23374c
...
...
@@ -101,7 +101,7 @@ public class RoomListItemViewHolder extends RecyclerView.ViewHolder {
break
;
default
:
{
itemView
.
showPrivateChannelIcon
();
Logger
.
report
(
new
AssertionError
(
"Room type doesn't satisfies the method documentation. Room type is:"
+
roomType
));
Logger
.
INSTANCE
.
report
(
new
AssertionError
(
"Room type doesn't satisfies the method documentation. Room type is:"
+
roomType
));
}
}
}
...
...
app/src/main/java/chat/rocket/android/push/PushManager.kt
View file @
cc23374c
package
chat.rocket.android.push
import
android.annotation.SuppressLint
import
android.app.Notification
import
android.app.NotificationChannel
import
android.app.NotificationManager
...
...
@@ -137,6 +138,7 @@ object PushManager {
return
group
}
@SuppressLint
(
"NewApi"
)
internal
fun
showNotification
(
context
:
Context
,
lastPushMessage
:
PushMessage
)
{
if
(
lastPushMessage
.
host
==
null
||
lastPushMessage
.
message
==
null
||
lastPushMessage
.
title
==
null
)
{
return
...
...
@@ -202,7 +204,7 @@ object PushManager {
.
setDeleteIntent
(
deleteIntent
)
.
setMessageNotification
()
val
subText
=
RocketChatCache
(
context
)
.
getHostSiteName
(
host
)
val
subText
=
RocketChatCache
.
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
...
...
@@ -257,6 +259,7 @@ object PushManager {
}
}
@SuppressLint
(
"NewApi"
)
@RequiresApi
(
Build
.
VERSION_CODES
.
N
)
internal
fun
createGroupNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
with
(
lastPushMessage
)
{
...
...
@@ -289,7 +292,7 @@ object PushManager {
manager
.
createNotificationChannel
(
groupChannel
)
}
val
subText
=
RocketChatCache
(
context
)
.
getHostSiteName
(
host
)
val
subText
=
RocketChatCache
.
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
...
...
@@ -344,7 +347,7 @@ object PushManager {
.
setContentIntent
(
contentIntent
)
.
setMessageNotification
()
val
subText
=
RocketChatCache
(
context
)
.
getHostSiteName
(
host
)
val
subText
=
RocketChatCache
.
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
...
...
@@ -370,6 +373,7 @@ object PushManager {
}
}
@SuppressLint
(
"NewApi"
)
@RequiresApi
(
Build
.
VERSION_CODES
.
N
)
internal
fun
createSingleNotificationForNougatAndAbove
(
context
:
Context
,
lastPushMessage
:
PushMessage
):
Notification
?
{
val
manager
:
NotificationManager
=
...
...
@@ -404,7 +408,7 @@ object PushManager {
manager
.
createNotificationChannel
(
channel
)
}
val
subText
=
RocketChatCache
(
context
)
.
getHostSiteName
(
host
)
val
subText
=
RocketChatCache
.
getHostSiteName
(
host
)
if
(
subText
.
isNotEmpty
())
{
builder
.
setSubText
(
subText
)
}
...
...
@@ -503,6 +507,7 @@ object PushManager {
setAutoCancel
(
true
)
setShowWhen
(
true
)
setColor
(
res
.
getColor
(
R
.
color
.
colorRed400
,
ctx
.
theme
))
setDefaults
(
Notification
.
DEFAULT_ALL
)
setSmallIcon
(
smallIcon
)
})
return
this
...
...
@@ -646,7 +651,7 @@ object PushManager {
}
val
httpUrl
=
HttpUrl
.
parse
(
pushMessage
.
host
)
httpUrl
?.
let
{
val
siteUrl
=
RocketChatCache
(
context
)
.
getSiteUrlFor
(
httpUrl
.
host
())
val
siteUrl
=
RocketChatCache
.
getSiteUrlFor
(
httpUrl
.
host
())
if
(
siteUrl
!=
null
)
{
sendMessage
(
siteUrl
,
message
,
pushMessage
.
rid
)
}
...
...
@@ -693,7 +698,6 @@ object PushManager {
.
subscribe
({
_
->
// Empty
},
{
throwable
->
throwable
.
printStackTrace
()
Logger
.
report
(
throwable
)
})
}
...
...
app/src/main/java/chat/rocket/android/service/ConnectivityManagerApi.java
View file @
cc23374c
...
...
@@ -28,4 +28,8 @@ public interface ConnectivityManagerApi {
int
getConnectivityState
(
@NonNull
String
hostname
);
void
resetConnectivityStateList
();
void
notifySessionEstablished
(
String
hostname
);
void
notifyConnecting
(
String
hostname
);
}
app/src/main/java/chat/rocket/android/service/KeepAliveJob.kt
0 → 100644
View file @
cc23374c
package
chat.rocket.android.service
import
chat.rocket.android.ConnectionStatusManager
import
chat.rocket.android.RocketChatApplication
import
com.evernote.android.job.Job
import
com.evernote.android.job.JobManager
import
com.evernote.android.job.JobRequest
import
java.util.concurrent.TimeUnit
class
KeepAliveJob
:
Job
()
{
private
val
connectivityManager
:
ConnectivityManagerApi
companion
object
{
val
TAG
=
"chat.rocket.android.service.KeepAliveJob"
fun
schedule
()
{
JobRequest
.
Builder
(
TAG
)
.
setExecutionWindow
(
TimeUnit
.
SECONDS
.
toMillis
(
3L
),
TimeUnit
.
SECONDS
.
toMillis
(
10L
))
.
setBackoffCriteria
(
10L
,
JobRequest
.
BackoffPolicy
.
EXPONENTIAL
)
.
setUpdateCurrent
(
true
)
.
setRequiredNetworkType
(
JobRequest
.
NetworkType
.
CONNECTED
)
.
setRequiresCharging
(
false
)
.
setRequirementsEnforced
(
true
)
.
build
()
.
schedule
()
}
fun
cancelPendingJobRequests
()
{
val
allJobRequests
=
JobManager
.
instance
().
getAllJobRequestsForTag
(
TAG
)
allJobRequests
.
forEach
{
jobRequest
->
jobRequest
.
cancelAndEdit
()
}
}
}
init
{
val
context
=
RocketChatApplication
.
getInstance
()
connectivityManager
=
ConnectivityManager
.
getInstance
(
context
)
}
override
fun
onRunJob
(
params
:
Params
):
Result
{
if
(
ConnectionStatusManager
.
transitionCount
()
==
0L
)
{
return
Result
.
SUCCESS
}
when
(
ConnectionStatusManager
.
currentState
())
{
ConnectionStatusManager
.
State
.
CONNECTING
,
ConnectionStatusManager
.
State
.
ONLINE
->
{
cancel
()
}
else
->
connectivityManager
.
keepAliveServer
()
}
return
Result
.
SUCCESS
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/service/RealmBasedConnectivityManager.java
View file @
cc23374c
...
...
@@ -14,6 +14,7 @@ import java.util.Map;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.TimeUnit
;
import
chat.rocket.android.ConnectionStatusManager
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.helper.RxHelper
;
import
chat.rocket.android.log.RCLog
;
...
...
@@ -74,7 +75,7 @@ import io.reactivex.subjects.BehaviorSubject;
@DebugLog
@Override
public
void
ensureConnections
()
{
String
hostname
=
new
RocketChatCache
(
appContext
)
.
getSelectedServerHostname
();
String
hostname
=
RocketChatCache
.
INSTANCE
.
getSelectedServerHostname
();
if
(
hostname
==
null
)
{
return
;
}
...
...
@@ -107,7 +108,7 @@ import io.reactivex.subjects.BehaviorSubject;
public
void
removeServer
(
String
hostname
)
{
RealmBasedServerInfo
.
remove
(
hostname
);
if
(
serverConnectivityList
.
containsKey
(
hostname
))
{
disconnectFromServerIfNeeded
(
hostname
)
disconnectFromServerIfNeeded
(
hostname
,
DDPClient
.
REASON_CLOSED_BY_USER
)
.
subscribe
(
_val
->
{
},
RCLog:
:
e
);
}
...
...
@@ -136,6 +137,13 @@ import io.reactivex.subjects.BehaviorSubject;
return
list
;
}
@Override
public
void
notifySessionEstablished
(
String
hostname
)
{
serverConnectivityList
.
put
(
hostname
,
ServerConnectivity
.
STATE_SESSION_ESTABLISHED
);
connectivitySubject
.
onNext
(
new
ServerConnectivity
(
hostname
,
ServerConnectivity
.
STATE_SESSION_ESTABLISHED
));
}
@DebugLog
@Override
public
void
notifyConnectionEstablished
(
String
hostname
,
String
session
)
{
...
...
@@ -200,7 +208,7 @@ import io.reactivex.subjects.BehaviorSubject;
});
}
private
Single
<
Boolean
>
disconnectFromServerIfNeeded
(
String
hostname
)
{
private
Single
<
Boolean
>
disconnectFromServerIfNeeded
(
String
hostname
,
int
reason
)
{
return
Single
.
defer
(()
->
{
final
int
connectivity
=
serverConnectivityList
.
get
(
hostname
);
if
(
connectivity
==
ServerConnectivity
.
STATE_DISCONNECTED
)
{
...
...
@@ -209,8 +217,8 @@ import io.reactivex.subjects.BehaviorSubject;
if
(
connectivity
==
ServerConnectivity
.
STATE_CONNECTING
)
{
return
waitForConnected
(
hostname
)
.
doOnError
(
err
->
notifyConnectionLost
(
hostname
,
DDPClient
.
REASON_NETWORK_ERRO
R
))
.
flatMap
(
_val
->
disconnectFromServerIfNeeded
(
hostname
));
// .doOnError(err -> notifyConnectionLost(hostname, DDPClient.REASON_CLOSED_BY_USE
R))
.
flatMap
(
_val
->
disconnectFromServerIfNeeded
(
hostname
,
DDPClient
.
REASON_CLOSED_BY_USER
));
}
if
(
connectivity
==
ServerConnectivity
.
STATE_DISCONNECTING
)
{
...
...
@@ -253,6 +261,7 @@ import io.reactivex.subjects.BehaviorSubject;
private
Single
<
Boolean
>
connectToServer
(
String
hostname
)
{
return
Single
.
defer
(()
->
{
if
(!
serverConnectivityList
.
containsKey
(
hostname
))
{
ConnectionStatusManager
.
INSTANCE
.
setConnectionError
();
return
Single
.
error
(
new
IllegalArgumentException
(
"hostname not found"
));
}
...
...
@@ -263,8 +272,10 @@ import io.reactivex.subjects.BehaviorSubject;
}
if
(
serviceInterface
!=
null
)
{
ConnectionStatusManager
.
INSTANCE
.
setConnecting
();
return
serviceInterface
.
ensureConnectionToServer
(
hostname
);
}
else
{
ConnectionStatusManager
.
INSTANCE
.
setConnectionError
();
return
Single
.
error
(
new
ThreadLooperNotPreparedException
(
"not prepared"
));
}
});
...
...
@@ -279,7 +290,7 @@ import io.reactivex.subjects.BehaviorSubject;
if
(
serviceInterface
!=
null
)
{
return
serviceInterface
.
disconnectFromServer
(
hostname
)
//after disconnection from server, remove HOSTNAME key from HashMap
//after disconnection from server, remove HOSTNAME key from HashMap
.
doAfterTerminate
(()
->
{
serverConnectivityList
.
remove
(
hostname
);
serverConnectivityList
.
put
(
hostname
,
ServerConnectivity
.
STATE_DISCONNECTED
);
...
...
app/src/main/java/chat/rocket/android/service/RocketChatService.java
View file @
cc23374c
...
...
@@ -38,7 +38,8 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
/**
* ensure RocketChatService alive.
*/
/*package*/
static
void
keepAlive
(
Context
context
)
{
/*package*/
static
void
keepAlive
(
Context
context
)
{
context
.
startService
(
new
Intent
(
context
,
RocketChatService
.
class
));
}
...
...
@@ -80,7 +81,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
}
if
(
currentWebSocketThread
!=
null
)
{
return
currentWebSocketThread
.
terminate
()
return
currentWebSocketThread
.
terminate
(
false
)
// after disconnection from server
.
doAfterTerminate
(()
->
{
currentWebSocketThread
=
null
;
...
...
@@ -99,14 +100,15 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
return
Single
.
defer
(()
->
{
webSocketThreadLock
.
acquire
();
int
connectivityState
=
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getConnectivityState
(
hostname
);
boolean
isDisconnected
=
connectivityState
!=
ServerConnectivity
.
STATE_CONNECTED
;
boolean
isDisconnected
=
connectivityState
<
ServerConnectivity
.
STATE_CONNECTED
;
if
(
currentWebSocketThread
!=
null
&&
existsThreadForHostname
(
hostname
)
&&
!
isDisconnected
)
{
webSocketThreadLock
.
release
();
return
Single
.
just
(
currentWebSocketThread
);
}
if
(
currentWebSocketThread
!=
null
)
{
return
currentWebSocketThread
.
terminate
()
boolean
hasFailed
=
existsThreadForHostname
(
hostname
);
return
currentWebSocketThread
.
terminate
(
hasFailed
)
.
doAfterTerminate
(()
->
currentWebSocketThread
=
null
)
.
flatMap
(
terminated
->
RocketChatWebSocketThread
.
getStarted
(
getApplicationContext
(),
hostname
)
...
...
@@ -117,7 +119,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
.
doOnError
(
throwable
->
{
currentWebSocketThread
=
null
;
RCLog
.
e
(
throwable
);
Logger
.
report
(
throwable
);
Logger
.
INSTANCE
.
report
(
throwable
);
webSocketThreadLock
.
release
();
})
);
...
...
@@ -131,7 +133,7 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
.
doOnError
(
throwable
->
{
currentWebSocketThread
=
null
;
RCLog
.
e
(
throwable
);
Logger
.
report
(
throwable
);
Logger
.
INSTANCE
.
report
(
throwable
);
webSocketThreadLock
.
release
();
});
});
...
...
app/src/main/java/chat/rocket/android/service/RocketChatWebSocketThread.java
View file @
cc23374c
...
...
@@ -14,6 +14,7 @@ import java.util.concurrent.TimeUnit;
import
java.util.concurrent.TimeoutException
;
import
bolts.Task
;
import
chat.rocket.android.ConnectionStatusManager
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.helper.LogIfError
;
import
chat.rocket.android.helper.RxHelper
;
...
...
@@ -29,7 +30,6 @@ import chat.rocket.android.service.observer.FileUploadingWithUfsObserver;
import
chat.rocket.android.service.observer.GcmPushRegistrationObserver
;
import
chat.rocket.android.service.observer.GetUsersOfRoomsProcedureObserver
;
import
chat.rocket.android.service.observer.LoadMessageProcedureObserver
;
import
chat.rocket.android.service.observer.MethodCallObserver
;
import
chat.rocket.android.service.observer.NewMessageObserver
;
import
chat.rocket.android.service.observer.PushSettingsObserver
;
import
chat.rocket.android.service.observer.SessionObserver
;
...
...
@@ -54,8 +54,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
LoginServiceConfigurationSubscriber
.
class
,
ActiveUsersSubscriber
.
class
,
UserDataSubscriber
.
class
,
MethodCallObserver
.
class
,
SessionObserver
.
class
,
// MethodCallObserver.class,
LoadMessageProcedureObserver
.
class
,
GetUsersOfRoomsProcedureObserver
.
class
,
NewMessageObserver
.
class
,
...
...
@@ -125,21 +124,26 @@ public class RocketChatWebSocketThread extends HandlerThread {
}
/**
* terminate WebSocket thread.
* Terminate WebSocket thread. If {@code hasFailed} is {@code true} it means that a connection was
* in progress but failed and got offline. If it's {@code false} means that the user explicitly
* disconnected from server either by logging out or by means of changing servers.
*
* @param hasFailed {@code true} if the termination is due to a network error, otherwise
* return false
*/
@DebugLog
/* package */
Single
<
Boolean
>
terminate
()
{
/* package */
Single
<
Boolean
>
terminate
(
boolean
hasFailed
)
{
if
(
isAlive
())
{
return
Single
.
create
(
emitter
->
{
new
Handler
(
getLooper
()).
post
(()
->
{
RCLog
.
d
(
"thread %s: terminated()"
,
Thread
.
currentThread
().
getId
());
unregisterListenersAndClose
()
;
connectivityManager
.
notifyConnectionLost
(
hostname
,
DDPClient
.
REASON_CLOSED_BY_USER
);
RocketChatWebSocketThread
.
super
.
quit
();
emitter
.
onSuccess
(
true
);
}
);
});
return
Single
.
create
(
emitter
->
new
Handler
(
getLooper
()).
post
(()
->
{
RCLog
.
d
(
"thread %s: terminated()"
,
Thread
.
currentThread
().
getId
());
int
reason
=
(
hasFailed
)
?
DDPClient
.
REASON_NETWORK_ERROR
:
DDPClient
.
REASON_CLOSED_BY_USER
;
unregisterListenersAndClose
(
reason
);
connectivityManager
.
notifyConnectionLost
(
hostname
,
reason
);
RocketChatWebSocketThread
.
super
.
quit
();
ConnectionStatusManager
.
INSTANCE
.
setOffline
(
);
emitter
.
onSuccess
(
true
);
})
)
;
}
else
{
connectivityManager
.
notifyConnectionLost
(
hostname
,
DDPClient
.
REASON_NETWORK_ERROR
);
...
...
@@ -163,7 +167,7 @@ public class RocketChatWebSocketThread extends HandlerThread {
@DebugLog
/* package */
Single
<
Boolean
>
keepAlive
()
{
return
checkIfConnectionAlive
()
.
flatMap
(
alive
->
alive
?
Single
.
just
(
true
)
:
connectWithExponentialBackoff
());
.
flatMap
(
alive
->
connectWithExponentialBackoff
());
}
@DebugLog
...
...
@@ -224,13 +228,11 @@ public class RocketChatWebSocketThread extends HandlerThread {
.
onSuccessTask
(
task
->
{
final
String
newSession
=
task
.
getResult
().
session
;
connectivityManager
.
notifyConnectionEstablished
(
hostname
,
newSession
);
// handling WebSocket#onClose() callback.
task
.
getResult
().
client
.
getOnCloseCallback
().
onSuccess
(
_task
->
{
RxWebSocketCallback
.
Close
result
=
_task
.
getResult
();
if
(
result
.
code
==
DDPClient
.
REASON_NETWORK_ERROR
)
{
reconnect
();
}
else
{
unregisterListenersAndClose
();
}
return
null
;
});
...
...
@@ -382,6 +384,10 @@ public class RocketChatWebSocketThread extends HandlerThread {
RCLog
.
w
(
exception
,
"Failed to register listeners!!"
);
}
}
// Register SessionObserver late.
SessionObserver
sessionObserver
=
new
SessionObserver
(
appContext
,
hostname
,
realmHelper
);
sessionObserver
.
register
();
listeners
.
add
(
sessionObserver
);
listenersRegistered
=
true
;
startHeartBeat
();
}
...
...
@@ -410,9 +416,9 @@ public class RocketChatWebSocketThread extends HandlerThread {
}
@DebugLog
private
void
unregisterListenersAndClose
()
{
private
void
unregisterListenersAndClose
(
int
reason
)
{
unregisterListeners
();
DDPClient
.
get
().
close
();
DDPClient
.
get
().
close
(
reason
);
}
@DebugLog
...
...
app/src/main/java/chat/rocket/android/service/ServerConnectivity.java
View file @
cc23374c
...
...
@@ -5,50 +5,50 @@ package chat.rocket.android.service;
*/
public
class
ServerConnectivity
{
public
static
final
int
STATE_CONNECTED
=
1
;
public
static
final
int
STATE_DISCONNECTED
=
2
;
public
static
final
int
STATE_CONNECTING
=
3
;
/*package*/
static
final
int
STATE_DISCONNECTING
=
4
;
public
static
final
ServerConnectivity
CONNECTED
=
new
ServerConnectivity
(
null
,
STATE_CONNECTED
);
public
final
String
hostname
;
public
final
int
state
;
public
final
int
code
;
ServerConnectivity
(
String
hostname
,
int
state
)
{
this
.
hostname
=
hostname
;
this
.
state
=
state
;
this
.
code
=
-
1
;
}
ServerConnectivity
(
String
hostname
,
int
state
,
int
code
)
{
this
.
hostname
=
hostname
;
this
.
state
=
state
;
this
.
code
=
code
;
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
return
true
;
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
return
false
;
ServerConnectivity
that
=
(
ServerConnectivity
)
o
;
return
state
==
that
.
state
;
}
@Override
public
int
hashCode
()
{
return
state
;
}
/**
* This exception should be thrown when connection is lost during waiting for CONNECTED.
*/
public
static
class
DisconnectedException
extends
Exception
{
public
DisconnectedException
()
{
super
(
"Disconnected"
);
public
static
final
int
STATE_DISCONNECTED
=
0
;
/* package */
static
final
int
STATE_DISCONNECTING
=
1
;
/* package */
static
final
int
STATE_CONNECTING
=
2
;
public
static
final
int
STATE_CONNECTED
=
3
;
public
static
final
int
STATE_SESSION_ESTABLISHED
=
4
;
/* package */
static
final
ServerConnectivity
CONNECTED
=
new
ServerConnectivity
(
null
,
STATE_CONNECTED
);
public
final
String
hostname
;
public
final
int
state
;
public
final
int
code
;
ServerConnectivity
(
String
hostname
,
int
state
)
{
this
.
hostname
=
hostname
;
this
.
state
=
state
;
this
.
code
=
-
1
;
}
ServerConnectivity
(
String
hostname
,
int
state
,
int
code
)
{
this
.
hostname
=
hostname
;
this
.
state
=
state
;
this
.
code
=
code
;
}
@Override
public
boolean
equals
(
Object
o
)
{
if
(
this
==
o
)
return
true
;
if
(
o
==
null
||
getClass
()
!=
o
.
getClass
())
return
false
;
ServerConnectivity
that
=
(
ServerConnectivity
)
o
;
return
state
==
that
.
state
;
}
@Override
public
int
hashCode
()
{
return
state
;
}
/**
* This exception should be thrown when connection is lost during waiting for CONNECTED.
*/
/* package */
static
class
DisconnectedException
extends
Exception
{
/* package */
DisconnectedException
()
{
super
(
"Disconnected"
);
}
}
}
}
app/src/main/java/chat/rocket/android/service/internal/AbstractRocketChatCacheObserver.java
View file @
cc23374c
package
chat
.
rocket
.
android
.
service
.
internal
;
import
android.content.Context
;
import
com.hadisatrio.optional.Optional
;
import
chat.rocket.android.RocketChatCache
;
...
...
@@ -13,13 +11,11 @@ import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import
io.reactivex.disposables.CompositeDisposable
;
public
abstract
class
AbstractRocketChatCacheObserver
implements
Registrable
{
private
final
Context
context
;
private
final
RealmHelper
realmHelper
;
private
String
roomId
;
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
protected
AbstractRocketChatCacheObserver
(
Context
context
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
;
protected
AbstractRocketChatCacheObserver
(
RealmHelper
realmHelper
)
{
this
.
realmHelper
=
realmHelper
;
}
...
...
@@ -47,7 +43,7 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
@Override
public
final
void
register
()
{
compositeDisposable
.
add
(
new
RocketChatCache
(
context
)
RocketChatCache
.
INSTANCE
.
getSelectedRoomIdPublisher
()
.
filter
(
Optional:
:
isPresent
)
.
map
(
Optional:
:
get
)
...
...
app/src/main/java/chat/rocket/android/service/internal/StreamRoomMessageManager.java
View file @
cc23374c
...
...
@@ -13,60 +13,58 @@ import chat.rocket.persistence.realm.RealmHelper;
* wrapper for managing stream-notify-message depending on RocketChatCache.
*/
public
class
StreamRoomMessageManager
implements
Registrable
{
private
final
Context
context
;
private
final
String
hostname
;
private
final
RealmHelper
realmHelper
;
private
final
AbstractRocketChatCacheObserver
cacheObserver
;
private
final
Handler
handler
;
private
final
RocketChatCache
rocketChatCache
;
private
StreamRoomMessage
streamRoomMessage
;
private
final
Context
context
;
private
final
String
hostname
;
private
final
RealmHelper
realmHelper
;
private
final
AbstractRocketChatCacheObserver
cacheObserver
;
private
final
Handler
handler
;
private
StreamRoomMessage
streamRoomMessage
;
public
StreamRoomMessageManager
(
Context
context
,
String
hostname
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
;
this
.
hostname
=
hostname
;
this
.
realmHelper
=
realmHelper
;
this
.
rocketChatCache
=
new
RocketChatCache
(
context
);
public
StreamRoomMessageManager
(
Context
context
,
String
hostname
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
;
this
.
hostname
=
hostname
;
this
.
realmHelper
=
realmHelper
;
cacheObserver
=
new
AbstractRocketChatCacheObserver
(
context
,
realmHelper
)
{
@Override
protected
void
onRoomIdUpdated
(
String
roomId
)
{
unregisterStreamNotifyMessageIfNeeded
();
registerStreamNotifyMessage
(
roomId
);
}
};
handler
=
new
Handler
(
Looper
.
myLooper
());
}
cacheObserver
=
new
AbstractRocketChatCacheObserver
(
realmHelper
)
{
@Override
protected
void
onRoomIdUpdated
(
String
roomId
)
{
unregisterStreamNotifyMessageIfNeeded
();
registerStreamNotifyMessage
(
roomId
);
}
};
handler
=
new
Handler
(
Looper
.
myLooper
());
}
private
void
registerStreamNotifyMessage
(
String
roomId
)
{
handler
.
post
(()
->
{
streamRoomMessage
=
new
StreamRoomMessage
(
context
,
hostname
,
realmHelper
,
roomId
);
streamRoomMessage
.
register
();
});
}
private
void
registerStreamNotifyMessage
(
String
roomId
)
{
handler
.
post
(()
->
{
streamRoomMessage
=
new
StreamRoomMessage
(
context
,
hostname
,
realmHelper
,
roomId
);
streamRoomMessage
.
register
();
});
}
private
void
unregisterStreamNotifyMessageIfNeeded
()
{
handler
.
post
(()
->
{
if
(
streamRoomMessage
!=
null
)
{
streamRoomMessage
.
unregister
();
streamRoomMessage
=
null
;
}
});
}
private
void
unregisterStreamNotifyMessageIfNeeded
()
{
handler
.
post
(()
->
{
if
(
streamRoomMessage
!=
null
)
{
streamRoomMessage
.
unregister
();
streamRoomMessage
=
null
;
}
});
}
@Override
public
void
register
()
{
cacheObserver
.
register
();
String
selectedRoomId
=
rocketChatCache
.
getSelectedRoomId
();
if
(
selectedRoomId
==
null
)
{
return
;
@Override
public
void
register
()
{
cacheObserver
.
register
();
String
selectedRoomId
=
RocketChatCache
.
INSTANCE
.
getSelectedRoomId
();
if
(
selectedRoomId
==
null
)
{
return
;
}
registerStreamNotifyMessage
(
selectedRoomId
);
}
registerStreamNotifyMessage
(
selectedRoomId
);
}
@Override
public
void
unregister
()
{
unregisterStreamNotifyMessageIfNeeded
();
cacheObserver
.
unregister
();
}
@Override
public
void
unregister
()
{
unregisterStreamNotifyMessageIfNeeded
();
cacheObserver
.
unregister
();
}
}
app/src/main/java/chat/rocket/android/service/observer/GcmPushRegistrationObserver.java
View file @
cc23374c
...
...
@@ -68,7 +68,7 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
final
RealmUser
currentUser
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmUser
.
queryCurrentUser
(
realm
).
findFirst
());
final
String
userId
=
currentUser
!=
null
?
currentUser
.
getId
()
:
null
;
final
String
pushId
=
new
RocketChatCache
(
context
)
.
getOrCreatePushId
();
final
String
pushId
=
RocketChatCache
.
INSTANCE
.
getOrCreatePushId
();
return
new
RaixPushHelper
(
realmHelper
)
.
pushUpdate
(
pushId
,
gcmToken
,
userId
);
...
...
app/src/main/java/chat/rocket/android/service/observer/SessionObserver.java
View file @
cc23374c
...
...
@@ -7,6 +7,7 @@ import java.util.List;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.api.RaixPushHelper
;
import
chat.rocket.android.helper.LogIfError
;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android.service.internal.StreamRoomMessageManager
;
import
chat.rocket.persistence.realm.RealmHelper
;
import
chat.rocket.persistence.realm.models.internal.GetUsersOfRoomsProcedure
;
...
...
@@ -73,8 +74,10 @@ public class SessionObserver extends AbstractModelObserver<RealmSession> {
// update push info
pushHelper
.
pushSetUser
(
new
RocketChatCache
(
context
)
.
getOrCreatePushId
())
.
pushSetUser
(
RocketChatCache
.
INSTANCE
.
getOrCreatePushId
())
.
continueWith
(
new
LogIfError
());
ConnectivityManager
.
getInstance
(
context
).
notifySessionEstablished
(
hostname
);
}
@DebugLog
...
...
app/src/main/res/anim/rotation.xml
0 → 100644
View file @
cc23374c
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<objectAnimator
android:duration=
"1000"
android:propertyName=
"rotation"
android:repeatCount=
"-1"
android:valueFrom=
"0"
android:valueTo=
"360"
/>
</set>
\ No newline at end of file
app/src/main/res/drawable/community.xml
0 → 100644
View file @
cc23374c
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"426.88dp"
android:height=
"343.23dp"
android:viewportWidth=
"426.88"
android:viewportHeight=
"343.23"
>
<path
android:fillColor=
"#e1e5e8"
android:pathData=
"M210.44,32.71 A134.94,134.94,0,0,0,103.67,85.13 L107.49,85.13
A131.91,131.91,0,0,1,210.44,35.71 C283.28,35.71,342.54,94.97,342.54,167.81
S283.28,299.91,210.44,299.91 S78.34,240.65,78.34,167.81
Q78.34,166.28,78.34,164.75 C77.34,164,76.52,162.83,75.42,162.26
Q75.31,165.02,75.31,167.8 C75.31,242.29,135.91,302.9,210.41,302.9
S345.51,242.3,345.51,167.8 S284.93,32.71,210.44,32.71 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M163.17,223.3 C192.933,223.3,217.06,247.427,217.06,277.19
C217.06,306.953,192.933,331.08,163.17,331.08
C133.407,331.08,109.28,306.953,109.28,277.19
C109.28,247.427,133.407,223.3,163.17,223.3 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M50.16,153.04 L39.79,153.04"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M44.98,147.86 L44.98,158.22"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M103.73,53.06 C106.293,53.06,108.37,55.1374,108.37,57.7
C108.37,60.2626,106.293,62.34,103.73,62.34
C101.167,62.34,99.09,60.2626,99.09,57.7
C99.09,55.1374,101.167,53.06,103.73,53.06 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M35.47,57.7 L11.88,57.7"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M86.97,57.7 L44.98,57.7"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M385.19,57.71 C387.753,57.71,389.83,59.7874,389.83,62.35
C389.83,64.9126,387.753,66.99,385.19,66.99
C382.627,66.99,380.55,64.9126,380.55,62.35
C380.55,59.7874,382.627,57.71,385.19,57.71 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M287.28,143.64 C289.843,143.64,291.92,145.717,291.92,148.28
C291.92,150.843,289.843,152.92,287.28,152.92
C284.717,152.92,282.64,150.843,282.64,148.28
C282.64,145.717,284.717,143.64,287.28,143.64 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M173.56,113.07 C176.123,113.07,178.2,115.147,178.2,117.71
C178.2,120.273,176.123,122.35,173.56,122.35
C170.997,122.35,168.92,120.273,168.92,117.71
C168.92,115.147,170.997,113.07,173.56,113.07 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M111.61,223.8 C114.173,223.8,116.25,225.877,116.25,228.44
C116.25,231.003,114.173,233.08,111.61,233.08
C109.047,233.08,106.97,231.003,106.97,228.44
C106.97,225.877,109.047,223.8,111.61,223.8 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M370.48,62.35 L325.7,62.35"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M214.13,19.55 L203.77,19.55"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M208.95,14.37 L208.95,24.73"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M247.92,19.55 L218.56,19.55"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M415.22,152.57 L404.86,152.57"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M410.04,147.39 L410.04,157.75"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M395.53,152.57 L349.72,152.57"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M334.39,170.87 C354.372,170.87,370.57,187.068,370.57,207.05
C370.57,227.032,354.372,243.23,334.39,243.23
C314.408,243.23,298.21,227.032,298.21,207.05
C298.21,187.068,314.408,170.87,334.39,170.87 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M222.52,146.58 C239.503,146.58,253.27,160.347,253.27,177.33
C253.27,194.313,239.503,208.08,222.52,208.08
C205.537,208.08,191.77,194.313,191.77,177.33
C191.77,160.347,205.537,146.58,222.52,146.58 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M287.28,230.33 C293.316,230.33,298.21,235.224,298.21,241.26
C298.21,247.296,293.316,252.19,287.28,252.19
C281.244,252.19,276.35,247.296,276.35,241.26
C276.35,235.224,281.244,230.33,287.28,230.33 Z"
/>
<path
android:fillColor=
"#f5455c"
android:pathData=
"M284.28,232.31 C290.316,232.31,295.21,237.204,295.21,243.24
C295.21,249.276,290.316,254.17,284.28,254.17
C278.244,254.17,273.35,249.276,273.35,243.24
C273.35,237.204,278.244,232.31,284.28,232.31 Z"
/>
<path
android:fillColor=
"#f5455c"
android:pathData=
"M331.39,174.17 C351.206,174.17,367.27,190.234,367.27,210.05
C367.27,229.866,351.206,245.93,331.39,245.93
C311.574,245.93,295.51,229.866,295.51,210.05
C295.51,190.234,311.574,174.17,331.39,174.17 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M311.94,250.72 C315.613,250.72,318.59,253.697,318.59,257.37
C318.59,261.043,315.613,264.02,311.94,264.02
C308.267,264.02,305.29,261.043,305.29,257.37
C305.29,253.697,308.267,250.72,311.94,250.72 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M229.74,181.04 C234.125,181.04,237.68,184.595,237.68,188.98
C237.68,193.365,234.125,196.92,229.74,196.92
C225.355,196.92,221.8,193.365,221.8,188.98
C221.8,184.595,225.355,181.04,229.74,181.04 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M244.19,175.09 C246.896,175.09,249.09,177.284,249.09,179.99
C249.09,182.696,246.896,184.89,244.19,184.89
C241.484,184.89,239.29,182.696,239.29,179.99
C239.29,177.284,241.484,175.09,244.19,175.09 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M218.74,154.14 C220.673,154.14,222.24,155.707,222.24,157.64
C222.24,159.573,220.673,161.14,218.74,161.14
C216.807,161.14,215.24,159.573,215.24,157.64
C215.24,155.707,216.807,154.14,218.74,154.14 Z"
/>
<path
android:pathData=
"M201.57,63.28 C203,57.68,213,55.49,226.86,55.78 A49.61,49.61,0,0,1,231.62,48.78
C228.17,48.57,224.83,48.45,221.62,48.45 Q218.53,48.45,215.62,48.59
C199.24,49.35,188.93,53.39,187.33,59.68 S192.85,74.44,206.87,82.94
A149.7,149.7,0,0,0,221.63,90.71 A49.64,49.64,0,0,1,220.55,82.16
C208,75.72,200.13,68.93,201.57,63.28 Z"
/>
<path
android:pathData=
"M225.84,57.71 L224.74,57.71 C212.23,57.71,204.48,59.96,203.51,63.78
S208.71,73.71,220.51,79.93 A50,50,0,0,1,225.85,57.72 Z"
/>
<path
android:pathData=
"M314.74,103.46 C326.88,103.4,334.74,101.19,335.66,97.46
S330.94,88.08,320.46,82.3 A50,50,0,0,1,314.74,103.51 Z"
/>
<path
android:pathData=
"M337.6,97.91 C336.02,104.13,324.32,105.47,314.55,105.47 L313.63,105.47
A49.64,49.64,0,0,1,308.47,112.71 C333.15,114.31,351.17,110.6,353.38,101.93
S341.78,81.61,319.69,71.27 A49.59,49.59,0,0,1,320.5,80
C329.2,84.67,339.21,91.57,337.6,97.91 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M206.86,82.92 C192.86,74.42,185.72,65.92,187.32,59.66 S199.23,49.34,215.61,48.57
Q218.53,48.43,221.61,48.43 C224.82,48.43,228.16,48.54,231.61,48.76
Q232.4,47.76,233.23,46.86 A153.06,153.06,0,0,0,215.51,46.57
C197.94,47.39,187.24,51.86,185.38,59.16 S190.78,75.5,205.83,84.62
A154.6,154.6,0,0,0,222.23,93.15 Q221.9,91.93,221.63,90.68
A149.7,149.7,0,0,1,206.86,82.92 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M319.15,68.69 Q319.45,69.97,319.69,71.27 C341.78,81.6,355.56,93.34,353.38,101.93
S333.15,114.31,308.47,112.71 Q307.59,113.71,306.66,114.71
C311.03,115.04,315.21,115.21,319.14,115.21
C339.36,115.21,353.14,110.86,355.32,102.43
C357.85,92.45,343.31,79.69,319.15,68.69 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M264.08,105.42 A249.58,249.58,0,0,1,221.63,90.69 Q221.9,91.93,222.23,93.16
A254,254,0,0,0,263.59,107.36 A255.47,255.47,0,0,0,306.66,114.7
Q307.59,113.7,308.47,112.7 A248.15,248.15,0,0,1,264.08,105.42 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M320.46,82.25 C330.94,88.03,336.62,93.67,335.66,97.41
S326.88,103.41,314.74,103.41 Q314.21,104.41,313.63,105.41 L314.55,105.41
C324.32,105.41,336.02,104.07,337.6,97.85 S329.2,84.67,320.51,80
C320.51,80.75,320.49,81.5,320.46,82.25 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M201.57,63.28 C200.13,68.93,208.03,75.71,220.57,82.13
C220.57,81.4,220.57,80.66,220.57,79.92 C208.78,73.7,202.57,67.74,203.57,63.77
S212.29,57.7,224.8,57.7 L225.9,57.7 C226.23,57.05,226.56,56.4,226.9,55.77
C213,55.49,203,57.68,201.57,63.28 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M314,103.47 C300.61,103.4,283.42,100.86,265.59,96.32
C246.72,91.52,231.29,85.61,220.51,79.93 C220.51,80.67,220.51,81.41,220.51,82.14
C232.51,88.29,248.7,94.14,265.05,98.26 C282.88,102.8,300.05,105.35,313.59,105.46
Q314.17,104.46,314.7,103.46 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M270.53,131.18 A51,51,0,0,1,221.26,93.41 L220.71,91.41 L222.64,92.28
A254.16,254.16,0,0,0,263.83,106.43 A256.15,256.15,0,0,0,306.73,113.74
L308.84,113.9 L307.38,115.43 A51.26,51.26,0,0,1,270.53,131.22 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M222.23,93.15 A254,254,0,0,0,263.59,107.35 A255.47,255.47,0,0,0,306.66,114.69
A50,50,0,0,1,222.23,93.14 M219.23,89.57 L220.33,93.66 A52,52,0,0,0,308.14,116.07
L311,113 L306.78,112.68 A255.19,255.19,0,0,1,264.05,105.39
A253.16,253.16,0,0,1,223.05,91.3 L219.19,89.56 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M314,104.47 C300.53,104.4,283.25,101.85,265.34,97.29
C247.81,92.83,231.72,87,220,80.81 L219.46,80.53 L219.46,79.92
A51,51,0,0,1,225,57.26 C225.33,56.59,225.68,55.94,226,55.26
A50.81,50.81,0,0,1,230.85,48.1 Q231.65,47.1,232.5,46.16
A51,51,0,0,1,320.14,68.43 C320.34,69.28,320.53,70.17,320.69,71.06
A51,51,0,0,1,321.52,79.96 C321.52,80.73,321.52,81.49,321.47,82.26
A51.07,51.07,0,0,1,315.64,103.89 L315.36,104.42 L314,104.42 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M270.48,30.18 A50,50,0,0,1,319.16,68.7 Q319.46,69.98,319.7,71.28
A49.59,49.59,0,0,1,320.51,80.01 C320.51,80.76,320.51,81.51,320.51,82.27
A50,50,0,0,1,314.79,103.48 L314,103.48 C300.61,103.41,283.42,100.87,265.59,96.33
C246.72,91.53,231.29,85.62,220.51,79.94 A50,50,0,0,1,225.85,57.73
C226.18,57.08,226.51,56.43,226.85,55.8 A49.6,49.6,0,0,1,231.61,48.8
Q232.4,47.8,233.23,46.9 A50.1,50.1,0,0,1,270.47,30.21 M270.47,28.21
A52.08,52.08,0,0,0,231.74,45.56 C231.17,46.2,230.61,46.86,230.06,47.56
A51.78,51.78,0,0,0,225.11,54.87 C224.77,55.48,224.42,56.14,224.05,56.87
A52,52,0,0,0,218.5,79.97 L218.5,81.18 L219.57,81.75
C231.32,87.95,247.48,93.83,265.09,98.31
C283.09,102.89,300.44,105.45,313.98,105.52 L315.98,105.52 L316.54,104.46
A52,52,0,0,0,322.48,82.41 C322.48,81.55,322.53,80.78,322.53,80.06
A51.77,51.77,0,0,0,321.68,70.98 C321.51,70.07,321.33,69.17,321.12,68.3
A52,52,0,0,0,270.5,28.25 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M95.95,197.11 L107.17,197.11 Q112.06,197.11,112.06,202 L112.06,202.01
Q112.06,206.9,107.17,206.9 L95.95,206.9 Q91.06,206.9,91.06,202.01 L91.06,202
Q91.06,197.11,95.95,197.11 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M52.07,176.33 L63.29,176.33 Q68.18,176.33,68.18,181.22 L68.18,181.23
Q68.18,186.12,63.29,186.12 L52.07,186.12 Q47.18,186.12,47.18,181.23
L47.18,181.22 Q47.18,176.33,52.07,176.33 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M93.17,198.71 L104.39,198.71 Q109.28,198.71,109.28,203.6 L109.28,203.61
Q109.28,208.5,104.39,208.5 L93.17,208.5 Q88.28,208.5,88.28,203.61 L88.28,203.6
Q88.28,198.71,93.17,198.71 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M49.29,177.93 L60.51,177.93 Q65.4,177.93,65.4,182.82 L65.4,182.83
Q65.4,187.72,60.51,187.72 L49.29,187.72 Q44.4,187.72,44.4,182.83 L44.4,182.82
Q44.4,177.93,49.29,177.93 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M67.17,185.71 L72.99,188.47"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M86.08,194.67 L91.9,197.43"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M229,147.52 C228.62,147.45,228.25,147.39,227.87,147.33
A8.28,8.28,0,0,0,227.1,149.49 A7.66,7.66,0,0,0,232.98,158.69
A7.81,7.81,0,0,0,241.69,153.51 A28.09,28.09,0,0,0,229,147.52 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M228.58,151 C228.2,150.93,227.83,150.87,227.45,150.81
A8.28,8.28,0,0,0,226.68,152.97 A7.66,7.66,0,0,0,232.56,162.17
A7.81,7.81,0,0,0,241.27,156.99 A28.09,28.09,0,0,0,228.58,151 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M106.62,85.7 C130.97,85.7,150.71,105.44,150.71,129.79
C150.71,154.14,130.97,173.88,106.62,173.88
C82.2698,173.88,62.53,154.14,62.53,129.79 C62.53,105.44,82.2698,85.7,106.62,85.7
Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M217.34,95 A50,50,0,0,0,301.77,116.55 A255.47,255.47,0,0,1,258.7,109.21
A254,254,0,0,1,217.34,95 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M278,33.55 A50,50,0,0,0,228.4,48.68 Q227.57,49.61,226.78,50.58
A49.61,49.61,0,0,0,222.02,57.58 C221.67,58.21,221.33,58.86,221.02,59.51
A50.14,50.14,0,0,0,217.23,69.64 A33.54,33.54,0,0,0,215.75,79.47
A207.67,207.67,0,0,0,260.75,96.47 C278.58,101.01,295.75,105.2,309.14,105.27
L309.89,105.27 A50,50,0,0,0,315.61,84.06 C315.61,83.31,315.66,82.55,315.61,81.8
A49.59,49.59,0,0,0,314.8,73.07 Q314.56,71.77,314.26,70.49 A50,50,0,0,0,278,33.55
Z"
/>
<path
android:fillColor=
"#f5455c"
android:pathData=
"M309.68,252.29 C313.353,252.29,316.33,255.267,316.33,258.94
C316.33,262.613,313.353,265.59,309.68,265.59
C306.007,265.59,303.03,262.613,303.03,258.94
C303.03,255.267,306.007,252.29,309.68,252.29 Z"
/>
<path
android:fillColor=
"#f5455c"
android:pathData=
"M340.53,228.63 C343.584,228.63,346.06,231.106,346.06,234.16
C346.06,237.214,343.584,239.69,340.53,239.69
C337.476,239.69,335,237.214,335,234.16 C335,231.106,337.476,228.63,340.53,228.63
Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M219.34,148.56 C236.323,148.56,250.09,162.327,250.09,179.31
C250.09,196.293,236.323,210.06,219.34,210.06
C202.357,210.06,188.59,196.293,188.59,179.31
C188.59,162.327,202.357,148.56,219.34,148.56 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M217.34,155.92 C219.411,155.92,221.09,157.599,221.09,159.67
C221.09,161.741,219.411,163.42,217.34,163.42
C215.269,163.42,213.59,161.741,213.59,159.67
C213.59,157.599,215.269,155.92,217.34,155.92 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M243.02,176.34 C245.726,176.34,247.92,178.534,247.92,181.24
C247.92,183.946,245.726,186.14,243.02,186.14
C240.314,186.14,238.12,183.946,238.12,181.24
C238.12,178.534,240.314,176.34,243.02,176.34 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M228.93,183.33 C233.508,183.33,237.22,187.042,237.22,191.62
C237.22,196.198,233.508,199.91,228.93,199.91
C224.352,199.91,220.64,196.198,220.64,191.62
C220.64,187.042,224.352,183.33,228.93,183.33 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M235.28,69.97 L227.96,77.29"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M227.96,69.97 L235.28,77.29"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M342.41,227.67 C344.973,227.67,347.05,229.747,347.05,232.31
C347.05,234.873,344.973,236.95,342.41,236.95
C339.847,236.95,337.77,234.873,337.77,232.31
C337.77,229.747,339.847,227.67,342.41,227.67 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M146.28,56.68 S124.16,42.33,131.84,15.31 C131.84,15.31,151.65,22.06,152.95,52.66
C152.94,52.66,147.26,54.32,146.28,56.68 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M140.09,32.98 C141.868,32.98,143.31,34.4216,143.31,36.2
C143.31,37.9784,141.868,39.42,140.09,39.42
C138.312,39.42,136.87,37.9784,136.87,36.2
C136.87,34.4216,138.312,32.98,140.09,32.98 Z"
/>
<path
android:fillColor=
"#f5455c"
android:pathData=
"M147.68,56.07 S145.11,61.93,155.48,69.35 A15,15,0,0,0,152,53.52 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M134.87,44 A18.27,18.27,0,0,0,138.62,64.87 S140.94,58.36,146.28,56.71
C146.28,56.68,139.77,53.89,134.87,44 Z"
/>
<path
android:fillColor=
"#fff"
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M150,36 A18.27,18.27,0,0,1,163.14,52.65 S156.73,50.07,151.95,52.97
C151.94,53,154.16,46.27,150,36 Z"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M130.68,32.18 L135.91,28.95"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M152.19,59.98 L144.66,45.18"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M302.42,223.3 L321.45,223.3"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M300.73,217.56 L311.94,217.56"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M126.06,175.93 S159.22,186.63,145.35,221.13"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M320.51,265.59 S352.51,273.25,351.51,245.94"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M256.67,167.81 S285.94,155.64,298.2,184.38"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M192.77,155.55 S180.14,114,218.56,108.3"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M301.78,126.48 S332.53,137.99,325.26,167.81"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M222.52,213.45 S219.24,250.71,270.52,247.67"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M187.1,223.3 S180.1,205.38,192.78,202.01"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M220.51,280 S271.23,284.17,273.35,254.13"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M185.16,170.86 S181.39,145.33,152.67,148.56"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M150.71,108.3 S169.3,77.08,210.44,92.17"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M102.6,115.51 C98.73,116.22,95.33,117.77,92.92,121.04
C87.51,128.36,90.84,136.68,99.83,138.33
C102.83,138.88,107.54,136.68,108.48,139.82
C109.71,143.92,111.98,148.17,110.92,152.82
C110.32,155.45,111.67,157.71,113.05,159.68 S116.15,163.94,119.25,161.17
C123.16,157.69,127.47,154.77,127.56,148.67
C127.63,143.26,132.04,139.87,134.75,135.03 A16.08,16.08,0,0,1,126.31,124.5
C130.19,124.15,134.41,128.42,137.11,123.74
C138.41,121.49,135.58,120.17,135.11,118.24"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M103.29,115.51 C108.62,115.51,112.9,119.79,118.52,119.08
C120.52,118.83,122.13,118.51,123.52,116.66
C122.52,113.24,119.82,112.1,116.45,112.06
C112.3,112.06,108.15,111.9,104.01,112.11
C102.01,112.22,101.41,113.36,103.32,114.81"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M63.47,125.89 C67.29,126.68,66.14,129.72,66.21,132.11
C66.31,135.57,66.1,138.58,70.29,140.61 A7.26,7.26,0,0,1,73.78,150.75
C72.67,153.1,73.52,155.4,73.15,157.69"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M120.57,88.54 C119.38,90.75,118.13,93,120,95
C118.49,98.36,115.44,95.82,113.54,97.48 C112.54,98.37,109.62,98.48,109.64,98.93
C109.91,106.4,101.03,105.93,99.08,111.34
C99.82,112.24,100.55,113.08,101.86,112.77"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M82.32,93.06 C84.54,98.61,81.32,101.39,76.75,102.61 A15,15,0,0,0,67.1,109.61"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M111.61,173 C105.89,170,99.88,170.79,93.86,171.63"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M93,94.36 C98.24,94.58,101.39,91.21,104.46,87.69"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M94.1,87.6 C93.83,94.33,88.3,94.99,83.64,96.6"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M148.26,116.93 L148.26,125.76 C143.07,122.52,141.48,115.86,135.26,118.98"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M103.06,84.63 C127.41,84.63,147.15,104.37,147.15,128.72
C147.15,153.07,127.41,172.81,103.06,172.81
C78.7098,172.81,58.97,153.07,58.97,128.72
C58.97,104.37,78.7098,84.63,103.06,84.63 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M71,102.06 L71,102.06 A8.77,8.77,0,0,1,73.93,100.28 L77.1,99.13
A4.12,4.12,0,0,0,78.39,98.37 L80.92,96.18 A2.44,2.44,0,0,0,81.71,94.83
L81.81,94.36 A1.77,1.77,0,0,1,83.28,93 L84.67,92.83 A5.34,5.34,0,0,0,86.05,92.47
L89.29,91.14 L90.37,90.87 A11.44,11.44,0,0,1,92.12,90.58 L93.03,90.5
A7.54,7.54,0,0,0,96.03,89.57 L99.12,87.84 A5.57,5.57,0,0,0,100.69,86.53
L102.82,83.95 L100.76,83.95 A39.41,39.41,0,0,0,84.9,89.09
C76.63,93.88,74.9,96.46,70.68,101.73 C70.43,102,71,102.06,71,102.06 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M62.46,123.26 A1.42,1.42,0,0,1,64.14,124.19
C64.71,125.46,65.03,124.76,64.71,131.19 S70.17,138.47,70.77,138.98
C70.77,138.98,74.31,141.68,72.71,146.87 C72.71,146.87,71.28,148.01,71.57,151.76
L71.57,153.15 S68.21,149.39,64.65,140.58 C62.71,135.77,62.42,124.29,62.42,124.29
Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M91.76,169.07 S104.16,165.61,109.56,170.66
C109.56,170.66,102.11,172.07,91.76,169.07 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M120.62,85.7 S129.77,90.33,134.39,94.83 S139.07,99.2,143.25,105.65
A37.79,37.79,0,0,1,146.37,112.54 A14.62,14.62,0,0,1,146.37,120.54
S146.37,121.17,145.37,119.95 S140.37,114.17,138.28,114.04
S133.99,114.14,134.04,114.76 S133.13,114.68,135.58,118.47
S131.75,122.82,131.75,122.82 S131.32,123.33,126.38,121.17
C126.38,121.17,124.14,120.62,125.54,123.41 S127.19,127.93,133.14,132.1
C133.14,132.1,133.73,132.1,131.75,134.34 A28.21,28.21,0,0,0,126.56,142.86
S126.56,148.57,124.03,151.86 A54,54,0,0,1,117.53,158.62 A3,3,0,0,1,112.95,157.87
C110.95,154.87,109.47,153.6,109.8,150.12 S110.4,145.4,109.04,141.45
S107.51,135.14,105.57,135.07 S96.57,135.86,95.36,134.16
S89.63,131.91,89.27,124.74 S97,113.58,97,113.58 S98.35,112.05,104.59,112.73
S111.33,116.57,117.73,116.12 C124.48,115.64,124.4,111.31,118.09,109.36
C112.95,107.78,104.59,109.03,101.66,109.54 A3.6,3.6,0,0,1,98.88,108.95
C98,108.29,97.56,107.21,99.94,105.65 C104.26,102.81,109.08,101.25,108.81,96.65
C108.81,96.65,108.16,95.76,111.03,95.38 S122.09,93.76,118.1,90.38
C118.13,90.34,119,84.86,120.62,85.7 Z"
/>
<path
android:strokeColor=
"#e1e5e8"
android:strokeWidth=
"3"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M129.7,104.74 S159.38,95.46,154.7,68.93"
/>
<path
android:strokeColor=
"#175cc4"
android:strokeWidth=
"2"
android:strokeLineJoin=
"round"
android:strokeMiterLimit=
"10"
android:strokeLineCap=
"round"
android:pathData=
"M79.45,184.39 C83.3933,184.39,86.59,187.587,86.59,191.53
C86.59,195.473,83.3933,198.67,79.45,198.67
C75.5067,198.67,72.31,195.473,72.31,191.53
C72.31,187.587,75.5067,184.39,79.45,184.39 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M76.86,186.08 C80.8033,186.08,84,189.277,84,193.22
C84,197.163,80.8033,200.36,76.86,200.36
C72.9167,200.36,69.72,197.163,69.72,193.22
C69.72,189.277,72.9167,186.08,76.86,186.08 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M160.59,245.94 A4.74,4.74,0,0,0,155.85,241.2 L123.69,241.2
Q122.81,242.2,121.97,243.2 L155.85,243.2 A2.74,2.74,0,0,1,155.85,248.67
L118,248.67 C117.59,249.33,117.19,250,116.81,250.67 L155.81,250.67
A4.74,4.74,0,0,0,160.59,245.94 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M198.59,258.21 A2.89,2.89,0,0,0,195.71,255.33 L114.47,255.33
C114.17,255.99,113.9,256.66,113.63,257.33 L195.7,257.33
A0.88,0.88,0,0,1,195.7,259.09 L113,259.09
C112.76,259.75,112.53,260.42,112.32,261.09 L195.7,261.09
A2.89,2.89,0,0,0,198.59,258.21 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M192.59,273.45 A4.86,4.86,0,0,0,187.73,268.59 L128.25,268.59
A4.86,4.86,0,0,0,128.25,278.3 L187.73,278.3 A4.86,4.86,0,0,0,192.59,273.45 Z
M125.4,273.45 A2.86,2.86,0,0,1,128.26,270.59 L187.74,270.59
A2.86,2.86,0,1,1,187.74,276.3 L128.25,276.3 A2.86,2.86,0,0,1,125.4,273.45 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M152.84,287.59 A1.75,1.75,0,0,1,152.84,284.09 L217.14,284.09
C217.22,283.43,217.29,282.76,217.35,282.09 L152.84,282.09
A3.75,3.75,0,0,0,152.84,289.59 L216.15,289.59
C216.31,288.93,216.44,288.26,216.57,287.59 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M178.59,312.59 A5.51,5.51,0,0,0,173.09,307.09 L118.77,307.09
Q119.45,308.09,120.16,309.09 L173.09,309.09 A3.5,3.5,0,0,1,173.09,316.09
L126.29,316.09 Q127.36,317.09,128.48,318.09 L173.09,318.09
A5.51,5.51,0,0,0,178.59,312.59 Z"
/>
<path
android:fillColor=
"#175cc4"
android:pathData=
"M157.12,323.81 A4.81,4.81,0,0,0,152.32,328.61 A4.74,4.74,0,0,0,152.56,330.03
Q153.8,330.29,155.07,330.5 A2.79,2.79,0,0,1,157.13,325.81 L187.13,325.81
C188.35,325.22,189.55,324.59,190.71,323.92 A4.79,4.79,0,0,0,189.71,323.81 Z"
/>
<path
android:fillColor=
"#2de0a5"
android:pathData=
"M160.59,226.65 C190.353,226.65,214.48,250.777,214.48,280.54
C214.48,310.303,190.353,334.43,160.59,334.43
C130.827,334.43,106.7,310.303,106.7,280.54
C106.7,250.777,130.827,226.65,160.59,226.65 Z"
/>
</vector>
\ No newline at end of file
app/src/main/res/drawable/ic_loading.xml
0 → 100644
View file @
cc23374c
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"16dp"
android:height=
"16dp"
android:viewportHeight=
"100.0"
android:viewportWidth=
"100.0"
>
<group
android:name=
"rotationGroup"
android:pivotX=
"50"
android:pivotY=
"50"
android:rotation=
"90"
>
<path
android:fillColor=
"#00000000"
android:pathData=
"M85,50A35,35 108.96,1 0,74.79 74.79"
android:strokeColor=
"#FFFFFF"
android:strokeWidth=
"12"
/>
<path
android:fillColor=
"#FFFFFF"
android:pathData=
"M105,49L70,49L85,65L100,49"
/>
</group>
</vector>
app/src/main/res/drawable/ic_loading_animated.xml
0 → 100644
View file @
cc23374c
<?xml version="1.0" encoding="utf-8"?>
<animated-vector
xmlns:tools=
"http://schemas.android.com/tools"
xmlns:android=
"http://schemas.android.com/apk/res/android"
tools:targetApi=
"lollipop"
android:drawable=
"@drawable/ic_loading"
>
<target
android:name=
"rotationGroup"
android:animation=
"@anim/rotation"
/>
</animated-vector>
\ No newline at end of file
app/src/main/res/layout/crouton_status_ticker.xml
0 → 100644
View file @
cc23374c
<?xml version="1.0" encoding="utf-8"?>
<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=
"match_parent"
android:background=
"#030000"
>
<TextView
android:id=
"@+id/text_view_status"
android:layout_width=
"wrap_content"
android:layout_height=
"32dp"
android:background=
"@color/connection_crouton_background"
android:gravity=
"center"
android:text=
"@string/server_config_activity_authenticating"
android:textAlignment=
"center"
android:textColor=
"@android:color/white"
android:textSize=
"16sp"
android:textStyle=
"bold"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
tools:text=
"Authenticating…"
/>
<ImageView
android:id=
"@+id/try_again_image"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_marginLeft=
"8dp"
android:layout_marginStart=
"8dp"
app:layout_constraintBottom_toBottomOf=
"@+id/text_view_status"
app:layout_constraintStart_toEndOf=
"@+id/text_view_status"
app:layout_constraintTop_toTopOf=
"@+id/text_view_status"
tools:src=
"@drawable/ic_loading"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
app/src/main/res/layout/fragment_login.xml
View file @
cc23374c
<?xml version="1.0" encoding="utf-8"?>
<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=
"match_parent"
android:background=
"?attr/colorPrimaryDark"
android:padding=
"@dimen/margin_8"
tools:context=
"chat.rocket.android.fragment.server_config.LoginFragment"
>
xmlns:app=
"http://schemas.android.com/apk/res-auto"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"?attr/colorPrimaryDark"
android:padding=
"@dimen/margin_8"
tools:context=
"chat.rocket.android.fragment.server_config.LoginFragment"
>
<ScrollView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:layout_constraintTop_toTop
Of=
"parent"
app:layout_constraintBottom_toBottom
Of=
"parent"
app:layout_constraintLeft_toLef
tOf=
"parent"
app:layout_constraintRight_toRight
Of=
"parent"
>
<ScrollView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
app:layout_constraintBottom_toBottom
Of=
"parent"
app:layout_constraintLeft_toLeft
Of=
"parent"
app:layout_constraintRight_toRigh
tOf=
"parent"
app:layout_constraintTop_toTop
Of=
"parent"
>
<android.support.constraint.ConstraintLayout
android:id=
"@+id/container"
android:layout_width=
"wrap_content"
android:minWidth=
"280dp
"
android:layout_height=
"wrap_content
"
android:paddingStart=
"@dimen/margin_16
"
android:paddingLeft=
"@dimen/margin_16
"
android:paddingEnd=
"@dimen/margin_16"
android:paddingRigh
t=
"@dimen/margin_16"
android:paddingTop=
"@dimen/margin_8
"
android:paddingBottom=
"@dimen/margin_8
"
android:background=
"@drawable/container_bg
"
app:layout_constraintTop_toTop
Of=
"parent"
app:layout_constraintBottom_toBottom
Of=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintLeft_toLeft
Of=
"parent"
>
<android.support.constraint.ConstraintLayout
android:id=
"@+id/container"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content
"
android:background=
"@drawable/container_bg
"
android:minWidth=
"280dp
"
android:paddingBottom=
"@dimen/margin_8
"
android:paddingEnd=
"@dimen/margin_16"
android:paddingLef
t=
"@dimen/margin_16"
android:paddingRight=
"@dimen/margin_16
"
android:paddingStart=
"@dimen/margin_16
"
android:paddingTop=
"@dimen/margin_8
"
app:layout_constraintBottom_toBottom
Of=
"parent"
app:layout_constraintLeft_toLeft
Of=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTop
Of=
"parent"
>
<ImageButton
android:id=
"@+id/btn_login_with_twitter"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitXY
"
app:srcCompat=
"@drawable/ic_button_twitter_24dp
"
app:layout_constraintTop_toTopOf=
"parent
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toLeftOf=
"parent
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_facebook
"
android:background=
"?attr/selectableItemBackgroundBorderless
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_twitter"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:background=
"?attr/selectableItemBackgroundBorderless
"
android:scaleType=
"fitXY
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toLeftOf=
"parent
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_facebook
"
app:layout_constraintTop_toTopOf=
"parent
"
app:srcCompat=
"@drawable/ic_button_twitter_24dp
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_facebook"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitXY
"
app:srcCompat=
"@drawable/ic_button_facebook_24dp
"
app:layout_constraintTop_toTopOf=
"parent
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_twitter
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_github
"
android:background=
"?attr/selectableItemBackgroundBorderless
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_facebook"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:background=
"?attr/selectableItemBackgroundBorderless
"
android:scaleType=
"fitXY
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_twitter
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_github
"
app:layout_constraintTop_toTopOf=
"parent
"
app:srcCompat=
"@drawable/ic_button_facebook_24dp
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_github"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitXY
"
app:srcCompat=
"@drawable/ic_button_github_24dp
"
app:layout_constraintTop_toTopOf=
"parent
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_facebook
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_google
"
android:background=
"?attr/selectableItemBackgroundBorderless
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_github"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:background=
"?attr/selectableItemBackgroundBorderless
"
android:scaleType=
"fitXY
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_facebook
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_google
"
app:layout_constraintTop_toTopOf=
"parent
"
app:srcCompat=
"@drawable/ic_button_github_24dp
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_google"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:scaleType=
"fitXY
"
app:srcCompat=
"@drawable/ic_button_google_24dp
"
app:layout_constraintTop_toTopOf=
"parent
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_github
"
app:layout_constraintRight_toRight
Of=
"parent"
android:background=
"?attr/selectableItemBackgroundBorderless
"
/>
<ImageButton
android:id=
"@+id/btn_login_with_google"
android:layout_width=
"48dp"
android:layout_height=
"48dp"
android:background=
"?attr/selectableItemBackgroundBorderless
"
android:scaleType=
"fitXY
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_username
"
app:layout_constraintLeft_toRightOf=
"@+id/btn_login_with_github
"
app:layout_constraintRight_toRightOf=
"parent
"
app:layout_constraintTop_toTop
Of=
"parent"
app:srcCompat=
"@drawable/ic_button_google_24dp
"
/>
<android.support.design.widget.TextInputLayout
android:id=
"@+id/text_input_username"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintTop_toBottomOf=
"@+id/btn_login_with_twitter
"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_passwd
"
app:layout_constraintLeft_toLef
tOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent
"
>
<android.support.design.widget.TextInputLayout
android:id=
"@+id/text_input_username"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintBottom_toTopOf=
"@+id/text_input_passwd
"
app:layout_constraintLeft_toLeftOf=
"parent
"
app:layout_constraintRight_toRigh
tOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/btn_login_with_twitter
"
>
<android.support.design.widget.TextInputEditText
android:id=
"@+id/editor_username"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"@string/fragment_login_username_or_email"
android:imeOptions=
"actionNext"
android:inputType=
"textWebEmailAddress"
android:maxLines=
"1"
/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputEditText
android:id=
"@+id/editor_username"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"@string/fragment_login_username_or_email"
android:imeOptions=
"actionNext"
android:inputType=
"textWebEmailAddress"
android:maxLines=
"1"
/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id=
"@+id/text_input_passwd"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_username
"
app:layout_constraintBottom_toTopOf=
"@+id/btn_user_registration
"
app:layout_constraintLeft_toLef
tOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent
"
>
<android.support.design.widget.TextInputLayout
android:id=
"@+id/text_input_passwd"
android:layout_width=
"0dp"
android:layout_height=
"wrap_content"
app:layout_constraintBottom_toTopOf=
"@+id/btn_user_registration
"
app:layout_constraintLeft_toLeftOf=
"parent
"
app:layout_constraintRight_toRigh
tOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_username
"
>
<android.support.design.widget.TextInputEditText
android:id=
"@+id/editor_passwd"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"@string/fragment_login_password"
android:imeOptions=
"actionNext"
android:inputType=
"textWebPassword"
android:maxLines=
"1"
/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputEditText
android:id=
"@+id/editor_passwd"
android:layout_width=
"match_parent"
android:layout_height=
"wrap_content"
android:hint=
"@string/fragment_login_password"
android:imeOptions=
"actionNext"
android:inputType=
"textWebPassword"
android:maxLines=
"1"
/>
</android.support.design.widget.TextInputLayout>
<Button
android:id=
"@+id/btn_user_registration"
android:layout_width=
"0dp
"
android:layout_height=
"wrap_content
"
android:layout_marginTop=
"@dimen/margin_8
"
android:text=
"@string/fragment_login_sign_up
"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_passwd
"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_email
"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"parent
"
style=
"@style/Widget.AppCompat.Button.Colore
d"
/>
<Button
android:id=
"@+id/btn_user_registration"
style=
"@style/Widget.AppCompat.Button.Colored
"
android:layout_width=
"0dp
"
android:layout_height=
"wrap_content
"
android:layout_marginTop=
"@dimen/margin_8
"
android:text=
"@string/fragment_login_sign_up
"
app:layout_constraintBottom_toBottomOf=
"parent
"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toLeftOf=
"@+id/btn_login_with_email
"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_passw
d"
/>
<Button
android:id=
"@+id/btn_login_with_email"
android:layout_width=
"0dp
"
android:layout_height=
"wrap_content
"
android:layout_marginTop=
"@dimen/margin_8
"
android:text=
"@string/fragment_login_sign_in
"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_passwd
"
app:layout_constraintRight_toRight
Of=
"parent"
app:layout_constraintLeft_toRightOf=
"@+id/btn_user_registration"
app:layout_constraintBottom_toBottom
Of=
"parent"
style=
"@style/Widget.AppCompat.Button.Colore
d"
/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
<Button
android:id=
"@+id/btn_login_with_email"
style=
"@style/Widget.AppCompat.Button.Colored
"
android:layout_width=
"0dp
"
android:layout_height=
"wrap_content
"
android:layout_marginTop=
"@dimen/margin_8
"
android:text=
"@string/fragment_login_sign_in
"
app:layout_constraintBottom_toBottom
Of=
"parent"
app:layout_constraintLeft_toRightOf=
"@+id/btn_user_registration"
app:layout_constraintRight_toRight
Of=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/text_input_passw
d"
/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
<chat.rocket.android.widget.WaitingView
android:id=
"@+id/waiting"
...
...
app/src/main/res/values/colors.xml
View file @
cc23374c
...
...
@@ -13,4 +13,5 @@
<color
name=
"colorSecondaryTextDark"
>
#8A000000
</color>
<color
name=
"colorDivider"
>
#1F000000
</color>
<color
name=
"connection_crouton_background"
>
#030000
</color>
</resources>
\ No newline at end of file
app/src/main/res/values/strings.xml
View file @
cc23374c
...
...
@@ -55,7 +55,7 @@
<string
name=
"fragment_login_sign_up"
>
SIGN UP
</string>
<string
name=
"fragment_login_sign_in"
>
SIGN IN
</string>
<string
name=
"fragment_retry_login_retry_title"
>
RETRY
</string>
<string
name=
"fragment_retry_login_error_title"
>
Connection Error
</string>
<string
name=
"fragment_retry_login_error_title"
>
Your connection seems off…
</string>
<string
name=
"server_config_activity_authenticating"
>
Authenticating…
</string>
<string
name=
"home_fragment_title"
>
Rocket.Chat - Home
</string>
<string
name=
"fragment_sidebar_main_unread_rooms_title"
>
UNREAD ROOMS
</string>
...
...
app/src/main/res/values/styles.xml
View file @
cc23374c
...
...
@@ -12,7 +12,6 @@
<item
name=
"actionModeBackground"
>
?attr/colorPrimaryDark
</item>
<item
name=
"android:statusBarColor"
tools:targetApi=
"21"
>
?attr/colorPrimaryDark
</item>
<item
name=
"android:navigationBarColor"
tools:targetApi=
"21"
>
?attr/colorPrimaryDark
</item>
<item
name=
"android:windowBackground"
>
@android:color/white
</item>
</style>
...
...
@@ -27,7 +26,6 @@
<item
name=
"actionModeBackground"
>
?attr/colorPrimaryDark
</item>
<item
name=
"android:statusBarColor"
tools:targetApi=
"21"
>
?attr/colorPrimaryDark
</item>
<item
name=
"android:navigationBarColor"
tools:targetApi=
"21"
>
?attr/colorPrimaryDark
</item>
</style>
<style
name=
"AppTheme.Dialog"
parent=
"Theme.AppCompat.Light.Dialog"
>
...
...
app/src/release/java/chat/rocket/android/helper/OkHttpHelper.kt
View file @
cc23374c
...
...
@@ -23,12 +23,12 @@ object OkHttpHelper {
return
httpClientForUploadFile
?:
throw
AssertionError
(
"httpClientForUploadFile set to null by another thread"
)
}
fun
getClientForDownloadFile
(
context
:
Context
):
OkHttpClient
{
fun
getClientForDownloadFile
():
OkHttpClient
{
if
(
httpClientForDownloadFile
==
null
)
{
httpClientForDownloadFile
=
OkHttpClient
.
Builder
()
.
followRedirects
(
true
)
.
followSslRedirects
(
true
)
.
addInterceptor
(
CookieInterceptor
(
DefaultCookieProvider
(
RocketChatCache
(
context
)
)))
.
addInterceptor
(
CookieInterceptor
(
DefaultCookieProvider
()))
.
build
()
}
return
httpClientForDownloadFile
?:
throw
AssertionError
(
"httpClientForDownloadFile set to null by another thread"
)
...
...
build.gradle
View file @
cc23374c
...
...
@@ -16,10 +16,10 @@ buildscript {
}
dependencies
{
classpath
'com.android.tools.build:gradle:3.0.1'
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:1.
1.6
0"
classpath
"org.jetbrains.kotlin:kotlin-gradle-plugin:1.
2.
0"
classpath
'io.realm:realm-gradle-plugin:4.2.0'
classpath
'com.jakewharton.hugo:hugo-plugin:1.2.1'
classpath
'com.google.gms:google-services:3.
0
.0'
classpath
'com.google.gms:google-services:3.
1
.0'
classpath
'com.github.triplet.gradle:play-publisher:1.1.5'
classpath
'io.fabric.tools:gradle:1.+'
}
...
...
dependencies.gradle
View file @
cc23374c
ext
{
preDexLibs
=
"true"
!=
System
.
getenv
(
"CI"
)
supportLibraryVersion
=
"27.0.1"
playLibVersion
=
'11.6.2'
constraintLayoutVersion
=
"1.0.2"
kotlinVersion
=
"1.
1.51
"
kotlinVersion
=
"1.
2.0
"
okHttpVersion
=
"3.9.0"
rxbindingVersion
=
'2.0.0'
rxJavaVersion
=
"2.1.0"
supportDependencies
=
[
designSupportLibrary:
"com.android.support:design:${supportLibraryVersion}"
,
...
...
@@ -15,14 +17,20 @@ ext {
multidex
:
"com.android.support:multidex:1.0.2"
,
customTabs
:
"com.android.support:customtabs:${supportLibraryVersion}"
]
extraDependencies
=
[
okHTTP
:
"com.squareup.okhttp3:okhttp:${okHttpVersion}"
,
rxJava
:
"io.reactivex.rxjava2:rxjava:2.1.0"
,
rxJava
:
"io.reactivex.rxjava2:rxjava:${rxJavaVersion}"
,
rxKotlin
:
"io.reactivex.rxjava2:rxkotlin:${rxJavaVersion}"
,
boltTask
:
"com.parse.bolts:bolts-tasks:1.4.0"
,
rxAndroid
:
"io.reactivex.rxjava2:rxandroid:2.0.1"
,
textDrawable
:
"com.github.rocketchat:textdrawable:1.0.2"
,
optional
:
"com.hadisatrio:Optional:v1.0.1"
optional
:
"com.hadisatrio:Optional:v1.0.1"
,
androidJob
:
"com.evernote:android-job:1.2.1"
,
jstate
:
"com.unquietcode.tools.jstate:jstate:2.1"
,
crouton
:
"de.keyboardsurfer.android.widget:crouton:1.8.5@aar"
]
rxbindingDependencies
=
[
rxBinding
:
"com.jakewharton.rxbinding2:rxbinding:${rxbindingVersion}"
,
rxBindingSupport
:
"com.jakewharton.rxbinding2:rxbinding-support-v4:${rxbindingVersion}"
,
...
...
@@ -38,5 +46,14 @@ subprojects {
project
.
android
.
dexOptions
.
preDexLibraries
=
rootProject
.
ext
.
preDexLibs
}
}
project
.
configurations
.
all
{
resolutionStrategy
.
eachDependency
{
details
->
if
(
details
.
requested
.
group
==
'com.android.support'
&&
!
details
.
requested
.
name
.
contains
(
'multidex'
)
)
{
details
.
useVersion
"${supportLibraryVersion}"
}
}
}
}
persistence-realm/build.gradle
View file @
cc23374c
...
...
@@ -30,17 +30,18 @@ android {
}
}
dependencies
{
compile
project
(
':log-wrapper'
)
compile
project
(
':rocket-chat-core'
)
compile
extraDependencies
.
rxJava
compile
extraDependencies
.
boltTask
compile
supportDependencies
.
annotation
compile
supportDependencies
.
designSupportLibrary
compile
extraDependencies
.
rxAndroid
provided
extraDependencies
.
optional
testCompile
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
testCompile
'org.json:json:20170516'
testCompile
'org.skyscreamer:jsonassert:1.5.0'
testCompile
'junit:junit:4.12'
api
project
(
':log-wrapper'
)
api
project
(
':rocket-chat-core'
)
implementation
extraDependencies
.
rxJava
implementation
extraDependencies
.
rxKotlin
implementation
extraDependencies
.
boltTask
implementation
supportDependencies
.
annotation
implementation
supportDependencies
.
designSupportLibrary
implementation
extraDependencies
.
rxAndroid
implementation
extraDependencies
.
optional
testImplementation
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testImplementation
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
testImplementation
'org.json:json:20170516'
testImplementation
'org.skyscreamer:jsonassert:1.5.0'
testImplementation
'junit:junit:4.12'
}
rocket-chat-android-widgets/build.gradle
View file @
cc23374c
...
...
@@ -31,25 +31,25 @@ ext {
frescoVersion
=
'1.4.0'
}
dependencies
{
compile
project
(
':rocket-chat-core'
)
compile
extraDependencies
.
okHTTP
compile
extraDependencies
.
textDrawable
compile
supportDependencies
.
annotation
compile
supportDependencies
.
cardView
compile
supportDependencies
.
designSupportLibrary
compile
supportDependencies
.
constraintLayout
compile
supportDependencies
.
supportV13
compile
supportDependencies
.
customTabs
compile
rxbindingDependencies
.
rxBinding
compile
rxbindingDependencies
.
rxBindingSupport
compile
"org.jetbrains.kotlin:kotlin-stdlib-jre7
:$rootProject.ext.kotlinVersion"
compile
'org.nibor.autolink:autolink:0.6.0'
compile
'com.github.yusukeiwaki.android-widget:widget-fontawesome:0.0.1'
compile
"com.facebook.fresco:fresco:$frescoVersion"
compile
"com.facebook.fresco:imagepipeline-okhttp3:$frescoVersion"
compile
'com.caverock:androidsvg:1.2.1'
test
Compile
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
test
Compile
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
test
Compile
'junit:junit:4.12'
test
Compile
"org.mockito:mockito-core:2.7.19
"
api
project
(
':rocket-chat-core'
)
api
extraDependencies
.
okHTTP
api
extraDependencies
.
textDrawable
api
supportDependencies
.
annotation
api
supportDependencies
.
cardView
api
supportDependencies
.
designSupportLibrary
api
supportDependencies
.
constraintLayout
api
supportDependencies
.
supportV13
api
supportDependencies
.
customTabs
api
rxbindingDependencies
.
rxBinding
api
rxbindingDependencies
.
rxBindingSupport
api
"org.jetbrains.kotlin:kotlin-stdlib-jre8
:$rootProject.ext.kotlinVersion"
implementation
'org.nibor.autolink:autolink:0.6.0'
implementation
'com.github.yusukeiwaki.android-widget:widget-fontawesome:0.0.1'
api
"com.facebook.fresco:fresco:$frescoVersion"
api
"com.facebook.fresco:imagepipeline-okhttp3:$frescoVersion"
implementation
'com.caverock:androidsvg:1.2.1'
test
Implementation
"org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
test
Implementation
"org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
test
Implementation
'junit:junit:4.12'
test
Implementation
"org.mockito:mockito-core:2.10.0
"
}
rocket-chat-core/build.gradle
View file @
cc23374c
...
...
@@ -4,14 +4,15 @@ apply plugin: 'java'
dependencies
{
compile
extraDependencies
.
rxJava
compile
extraDependencies
.
rxKotlin
compile
extraDependencies
.
optional
compile
"org.jetbrains.kotlin:kotlin-stdlib-jre
7
:$rootProject.ext.kotlinVersion"
compile
"org.jetbrains.kotlin:kotlin-stdlib-jre
8
:$rootProject.ext.kotlinVersion"
compile
'com.google.code.findbugs:jsr305:3.0.2'
compileOnly
'com.google.auto.value:auto-value:1.3'
kapt
'com.google.auto.value:auto-value:1.3'
kapt
'com.gabrielittner.auto.value:auto-value-with:1.0.0'
test
Compile
'junit:junit:4.12'
test
Compile
"org.mockito:mockito-inline:2.8.9"
test
Implementation
'junit:junit:4.12'
test
Implementation
"org.mockito:mockito-inline:2.8.9"
}
sourceCompatibility
=
"1.7"
targetCompatibility
=
"1.7"
\ No newline at end of file
sourceCompatibility
=
"1.8"
targetCompatibility
=
"1.8"
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment