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
6cb7bfdf
Commit
6cb7bfdf
authored
Feb 22, 2017
by
Tiago Cunha
Committed by
GitHub
Feb 22, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #217 from RocketChat/fix/user-presence-online-trigger
Fixes user presence online trigger
parents
3e1bb00a
7ba0adaf
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
200 additions
and
142 deletions
+200
-142
RocketChatCache.java
app/src/main/java/chat/rocket/android/RocketChatCache.java
+65
-12
AbstractAuthedActivity.java
.../chat/rocket/android/activity/AbstractAuthedActivity.java
+47
-38
MainActivity.java
.../main/java/chat/rocket/android/activity/MainActivity.java
+10
-51
MainContract.java
.../main/java/chat/rocket/android/activity/MainContract.java
+2
-0
MainPresenter.java
...main/java/chat/rocket/android/activity/MainPresenter.java
+40
-1
DefaultCookieProvider.java
...a/chat/rocket/android/api/rest/DefaultCookieProvider.java
+4
-6
InputHostnameFragment.java
...et/android/fragment/add_server/InputHostnameFragment.java
+1
-1
InputHostnamePresenter.java
...t/android/fragment/add_server/InputHostnamePresenter.java
+3
-7
SidebarMainFragment.java
.../rocket/android/fragment/sidebar/SidebarMainFragment.java
+5
-3
OkHttpHelper.java
...rc/main/java/chat/rocket/android/helper/OkHttpHelper.java
+3
-1
AbstractRocketChatCacheObserver.java
...oid/service/internal/AbstractRocketChatCacheObserver.java
+10
-13
GcmPushRegistrationObserver.java
...android/service/observer/GcmPushRegistrationObserver.java
+7
-7
SessionObserver.java
...chat/rocket/android/service/observer/SessionObserver.java
+3
-2
No files found.
app/src/main/java/chat/rocket/android/RocketChatCache.java
View file @
6cb7bfdf
...
@@ -3,30 +3,43 @@ package chat.rocket.android;
...
@@ -3,30 +3,43 @@ package chat.rocket.android;
import
android.content.Context
;
import
android.content.Context
;
import
android.content.SharedPreferences
;
import
android.content.SharedPreferences
;
import
io.reactivex.BackpressureStrategy
;
import
io.reactivex.Flowable
;
import
java.util.UUID
;
import
java.util.UUID
;
/**
/**
* sharedpreference-based cache.
* sharedpreference-based cache.
*/
*/
public
class
RocketChatCache
{
public
class
RocketChatCache
{
public
static
final
String
KEY_SELECTED_SERVER_HOSTNAME
=
"selectedServerHostname"
;
private
static
final
String
KEY_SELECTED_SERVER_HOSTNAME
=
"selectedServerHostname"
;
public
static
final
String
KEY_SELECTED_ROOM_ID
=
"selectedRoomId"
;
private
static
final
String
KEY_SELECTED_ROOM_ID
=
"selectedRoomId"
;
private
static
final
String
KEY_PUSH_ID
=
"pushId"
;
private
static
final
String
KEY_PUSH_ID
=
"pushId"
;
/**
private
Context
context
;
* get SharedPreference instance for RocketChat application cache.
*/
public
RocketChatCache
(
Context
context
)
{
public
static
SharedPreferences
get
(
Context
context
)
{
this
.
context
=
context
.
getApplicationContext
();
return
context
.
getSharedPreferences
(
"cache"
,
Context
.
MODE_PRIVATE
);
}
public
String
getSelectedServerHostname
()
{
return
getString
(
KEY_SELECTED_SERVER_HOSTNAME
,
null
);
}
public
void
setSelectedServerHostname
(
String
hostname
)
{
setString
(
KEY_SELECTED_SERVER_HOSTNAME
,
hostname
);
}
public
String
getSelectedRoomId
()
{
return
getString
(
KEY_SELECTED_ROOM_ID
,
null
);
}
}
public
static
String
getSelectedServerHostname
(
Context
context
)
{
public
void
setSelectedRoomId
(
String
roomId
)
{
return
get
(
context
).
getString
(
KEY_SELECTED_SERVER_HOSTNAME
,
null
);
setString
(
KEY_SELECTED_ROOM_ID
,
roomId
);
}
}
public
static
String
getOrCreatePushId
(
Context
context
)
{
public
String
getOrCreatePushId
(
)
{
SharedPreferences
preferences
=
get
(
context
);
SharedPreferences
preferences
=
get
SharedPreferences
(
);
if
(!
preferences
.
contains
(
KEY_PUSH_ID
))
{
if
(!
preferences
.
contains
(
KEY_PUSH_ID
))
{
// generates one and save
// generates one and save
String
newId
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
String
newId
=
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
...
@@ -37,4 +50,44 @@ public class RocketChatCache {
...
@@ -37,4 +50,44 @@ public class RocketChatCache {
}
}
return
preferences
.
getString
(
KEY_PUSH_ID
,
null
);
return
preferences
.
getString
(
KEY_PUSH_ID
,
null
);
}
}
public
Flowable
<
String
>
getSelectedServerHostnamePublisher
()
{
return
getValuePublisher
(
KEY_SELECTED_SERVER_HOSTNAME
);
}
public
Flowable
<
String
>
getSelectedRoomIdPublisher
()
{
return
getValuePublisher
(
KEY_SELECTED_ROOM_ID
);
}
private
SharedPreferences
getSharedPreferences
()
{
return
context
.
getSharedPreferences
(
"cache"
,
Context
.
MODE_PRIVATE
);
}
private
SharedPreferences
.
Editor
getEditor
()
{
return
getSharedPreferences
().
edit
();
}
private
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
<
String
>
getValuePublisher
(
final
String
key
)
{
return
Flowable
.
create
(
emitter
->
{
SharedPreferences
.
OnSharedPreferenceChangeListener
listener
=
(
sharedPreferences
,
changedKey
)
->
{
if
(
key
.
equals
(
changedKey
)
&&
!
emitter
.
isCancelled
())
{
emitter
.
onNext
(
getString
(
key
,
null
));
}
};
emitter
.
setCancellable
(()
->
getSharedPreferences
()
.
unregisterOnSharedPreferenceChangeListener
(
listener
));
getSharedPreferences
().
registerOnSharedPreferenceChangeListener
(
listener
);
},
BackpressureStrategy
.
LATEST
);
}
}
}
app/src/main/java/chat/rocket/android/activity/AbstractAuthedActivity.java
View file @
6cb7bfdf
package
chat
.
rocket
.
android
.
activity
;
package
chat
.
rocket
.
android
.
activity
;
import
android.content.Intent
;
import
android.content.Intent
;
import
android.content.SharedPreferences
;
import
android.os.Bundle
;
import
android.os.Bundle
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
io.reactivex.android.schedulers.AndroidSchedulers
;
import
io.reactivex.disposables.CompositeDisposable
;
import
io.reactivex.schedulers.Schedulers
;
import
java.util.List
;
import
java.util.List
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.RocketChatCache
;
...
@@ -19,18 +22,16 @@ import icepick.State;
...
@@ -19,18 +22,16 @@ import icepick.State;
abstract
class
AbstractAuthedActivity
extends
AbstractFragmentActivity
{
abstract
class
AbstractAuthedActivity
extends
AbstractFragmentActivity
{
@State
protected
String
hostname
;
@State
protected
String
hostname
;
@State
protected
String
roomId
;
@State
protected
String
roomId
;
SharedPreferences
.
OnSharedPreferenceChangeListener
preferenceChangeListener
=
(
sharedPreferences
,
key
)
->
{
private
RocketChatCache
rocketChatCache
;
if
(
RocketChatCache
.
KEY_SELECTED_SERVER_HOSTNAME
.
equals
(
key
))
{
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
updateHostnameIfNeeded
(
sharedPreferences
);
}
else
if
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
.
equals
(
key
))
{
updateRoomIdIfNeeded
(
sharedPreferences
);
}
};
@Override
@Override
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
protected
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
super
.
onCreate
(
savedInstanceState
);
rocketChatCache
=
new
RocketChatCache
(
this
);
if
(
savedInstanceState
==
null
)
{
if
(
savedInstanceState
==
null
)
{
handleIntent
(
getIntent
());
handleIntent
(
getIntent
());
}
}
...
@@ -48,15 +49,11 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -48,15 +49,11 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
}
}
if
(
intent
.
hasExtra
(
PushConstants
.
HOSTNAME
))
{
if
(
intent
.
hasExtra
(
PushConstants
.
HOSTNAME
))
{
SharedPreferences
.
Editor
editor
=
RocketChatCache
.
get
(
this
).
edit
();
rocketChatCache
.
setSelectedServerHostname
(
intent
.
getStringExtra
(
PushConstants
.
HOSTNAME
));
editor
.
putString
(
RocketChatCache
.
KEY_SELECTED_SERVER_HOSTNAME
,
intent
.
getStringExtra
(
PushConstants
.
HOSTNAME
));
if
(
intent
.
hasExtra
(
PushConstants
.
ROOM_ID
))
{
if
(
intent
.
hasExtra
(
PushConstants
.
ROOM_ID
))
{
editor
.
putString
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
,
rocketChatCache
.
setSelectedRoomId
(
intent
.
getStringExtra
(
PushConstants
.
ROOM_ID
));
intent
.
getStringExtra
(
PushConstants
.
ROOM_ID
));
}
}
editor
.
apply
();
}
}
if
(
intent
.
hasExtra
(
PushConstants
.
NOT_ID
))
{
if
(
intent
.
hasExtra
(
PushConstants
.
NOT_ID
))
{
...
@@ -65,13 +62,12 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -65,13 +62,12 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
}
}
}
}
private
void
updateHostnameIfNeeded
(
SharedPreferences
prefs
)
{
private
void
updateHostnameIfNeeded
(
String
newHostname
)
{
String
newHostname
=
prefs
.
getString
(
RocketChatCache
.
KEY_SELECTED_SERVER_HOSTNAME
,
null
);
if
(
hostname
==
null
)
{
if
(
hostname
==
null
)
{
if
(
newHostname
!=
null
&&
assertServerRealmStoreExists
(
newHostname
))
{
if
(
newHostname
!=
null
&&
assertServerRealmStoreExists
(
newHostname
))
{
updateHostname
(
newHostname
);
updateHostname
(
newHostname
);
}
else
{
}
else
{
recoverFromHostnameError
(
prefs
);
recoverFromHostnameError
();
}
}
}
else
{
}
else
{
if
(
hostname
.
equals
(
newHostname
))
{
if
(
hostname
.
equals
(
newHostname
))
{
...
@@ -82,7 +78,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -82,7 +78,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
if
(
assertServerRealmStoreExists
(
newHostname
))
{
if
(
assertServerRealmStoreExists
(
newHostname
))
{
updateHostname
(
newHostname
);
updateHostname
(
newHostname
);
}
else
{
}
else
{
recoverFromHostnameError
(
prefs
);
recoverFromHostnameError
();
}
}
}
}
}
}
...
@@ -96,7 +92,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -96,7 +92,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
onHostnameUpdated
();
onHostnameUpdated
();
}
}
private
void
recoverFromHostnameError
(
SharedPreferences
prefs
)
{
private
void
recoverFromHostnameError
()
{
final
List
<
ServerInfo
>
serverInfoList
=
final
List
<
ServerInfo
>
serverInfoList
=
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getServerList
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getServerList
();
if
(
serverInfoList
==
null
||
serverInfoList
.
size
()
==
0
)
{
if
(
serverInfoList
==
null
||
serverInfoList
.
size
()
==
0
)
{
...
@@ -106,26 +102,24 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -106,26 +102,24 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
// just connect to the first available
// just connect to the first available
final
ServerInfo
serverInfo
=
serverInfoList
.
get
(
0
);
final
ServerInfo
serverInfo
=
serverInfoList
.
get
(
0
);
prefs
.
edit
()
.
putString
(
RocketChatCache
.
KEY_SELECTED_SERVER_HOSTNAME
,
serverInfo
.
getHostname
())
rocketChatCache
.
setSelectedServerHostname
(
serverInfo
.
getHostname
());
.
remove
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
)
rocketChatCache
.
setSelectedRoomId
(
null
);
.
apply
();
}
}
private
void
updateRoomIdIfNeeded
(
SharedPreferences
prefs
)
{
private
void
updateRoomIdIfNeeded
(
String
newRoomId
)
{
String
newRoomId
=
prefs
.
getString
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
,
null
);
if
(
roomId
==
null
)
{
if
(
roomId
==
null
)
{
if
(
newRoomId
!=
null
&&
assertRoomSubscriptionExists
(
newRoomId
,
prefs
))
{
if
(
newRoomId
!=
null
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
updateRoomId
(
newRoomId
);
}
}
}
else
{
}
else
{
if
(!
roomId
.
equals
(
newRoomId
)
&&
assertRoomSubscriptionExists
(
newRoomId
,
prefs
))
{
if
(!
roomId
.
equals
(
newRoomId
)
&&
assertRoomSubscriptionExists
(
newRoomId
))
{
updateRoomId
(
newRoomId
);
updateRoomId
(
newRoomId
);
}
}
}
}
}
}
private
boolean
assertRoomSubscriptionExists
(
String
roomId
,
SharedPreferences
prefs
)
{
private
boolean
assertRoomSubscriptionExists
(
String
roomId
)
{
if
(!
assertServerRealmStoreExists
(
hostname
))
{
if
(!
assertServerRealmStoreExists
(
hostname
))
{
return
false
;
return
false
;
}
}
...
@@ -133,9 +127,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -133,9 +127,7 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
RealmRoom
room
=
RealmStore
.
get
(
hostname
).
executeTransactionForRead
(
realm
->
RealmRoom
room
=
RealmStore
.
get
(
hostname
).
executeTransactionForRead
(
realm
->
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
RealmRoom
.
ROOM_ID
,
roomId
).
findFirst
());
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
RealmRoom
.
ROOM_ID
,
roomId
).
findFirst
());
if
(
room
==
null
)
{
if
(
room
==
null
)
{
prefs
.
edit
()
rocketChatCache
.
setSelectedRoomId
(
null
);
.
remove
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
)
.
apply
();
return
false
;
return
false
;
}
}
return
true
;
return
true
;
...
@@ -157,16 +149,15 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -157,16 +149,15 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
super
.
onResume
();
super
.
onResume
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
();
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
keepAliveServer
();
SharedPreferences
prefs
=
RocketChatCache
.
get
(
this
);
updateHostnameIfNeeded
(
rocketChatCache
.
getSelectedServerHostname
()
);
update
HostnameIfNeeded
(
prefs
);
update
RoomIdIfNeeded
(
rocketChatCache
.
getSelectedRoomId
()
);
updateRoomIdIfNeeded
(
prefs
);
prefs
.
registerOnSharedPreferenceChangeListener
(
preferenceChangeListener
);
subscribeToConfigChanges
(
);
}
}
@Override
@Override
protected
void
onPause
()
{
protected
void
onPause
()
{
SharedPreferences
prefs
=
RocketChatCache
.
get
(
this
);
compositeDisposable
.
clear
();
prefs
.
unregisterOnSharedPreferenceChangeListener
(
preferenceChangeListener
);
super
.
onPause
();
super
.
onPause
();
}
}
...
@@ -175,4 +166,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
...
@@ -175,4 +166,22 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
protected
void
onSaveInstanceState
(
Bundle
outState
)
{
protected
void
onSaveInstanceState
(
Bundle
outState
)
{
super
.
onSaveInstanceState
(
outState
);
super
.
onSaveInstanceState
(
outState
);
}
}
private
void
subscribeToConfigChanges
()
{
compositeDisposable
.
add
(
rocketChatCache
.
getSelectedServerHostnamePublisher
()
.
distinctUntilChanged
()
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateHostnameIfNeeded
)
);
compositeDisposable
.
add
(
rocketChatCache
.
getSelectedRoomIdPublisher
()
.
distinctUntilChanged
()
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
(
this
::
updateRoomIdIfNeeded
)
);
}
}
}
app/src/main/java/chat/rocket/android/activity/MainActivity.java
View file @
6cb7bfdf
...
@@ -14,11 +14,9 @@ import chat.rocket.android.api.MethodCallHelper;
...
@@ -14,11 +14,9 @@ import chat.rocket.android.api.MethodCallHelper;
import
chat.rocket.android.fragment.chatroom.HomeFragment
;
import
chat.rocket.android.fragment.chatroom.HomeFragment
;
import
chat.rocket.android.fragment.chatroom.RoomFragment
;
import
chat.rocket.android.fragment.chatroom.RoomFragment
;
import
chat.rocket.android.fragment.sidebar.SidebarMainFragment
;
import
chat.rocket.android.fragment.sidebar.SidebarMainFragment
;
import
chat.rocket.android.helper.LogIfError
;
import
chat.rocket.core.interactors.CanCreateRoomInteractor
;
import
chat.rocket.core.interactors.CanCreateRoomInteractor
;
import
chat.rocket.core.interactors.RoomInteractor
;
import
chat.rocket.core.interactors.RoomInteractor
;
import
chat.rocket.core.interactors.SessionInteractor
;
import
chat.rocket.core.interactors.SessionInteractor
;
import
chat.rocket.core.models.User
;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android.service.ConnectivityManager
;
import
chat.rocket.android.widget.RoomToolbar
;
import
chat.rocket.android.widget.RoomToolbar
;
import
chat.rocket.persistence.realm.repositories.RealmRoomRepository
;
import
chat.rocket.persistence.realm.repositories.RealmRoomRepository
;
...
@@ -47,29 +45,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
...
@@ -47,29 +45,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
statusTicker
=
new
StatusTicker
();
statusTicker
=
new
StatusTicker
();
setupSidebar
();
setupSidebar
();
if
(
roomId
==
null
)
{
showFragment
(
new
HomeFragment
());
}
if
(
shouldLaunchAddServerActivity
())
{
LaunchUtil
.
showAddServerActivity
(
this
);
}
}
@Override
protected
void
onStart
()
{
super
.
onStart
();
setUserOnlineIfServerAvailable
();
}
@Override
protected
void
onResume
()
{
super
.
onResume
();
if
(
presenter
!=
null
)
{
presenter
.
bindView
(
this
);
}
}
}
@Override
@Override
...
@@ -81,27 +56,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
...
@@ -81,27 +56,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
super
.
onPause
();
super
.
onPause
();
}
}
@Override
protected
void
onStop
()
{
setUserAwayIfServerAvailable
();
super
.
onStop
();
}
private
void
setUserOnlineIfServerAvailable
()
{
if
(
hostname
!=
null
)
{
new
MethodCallHelper
(
this
,
hostname
).
setUserPresence
(
User
.
STATUS_ONLINE
)
.
continueWith
(
new
LogIfError
());
}
}
private
void
setUserAwayIfServerAvailable
()
{
if
(
hostname
!=
null
)
{
new
MethodCallHelper
(
this
,
hostname
).
setUserPresence
(
User
.
STATUS_AWAY
)
.
continueWith
(
new
LogIfError
());
}
}
private
void
setupSidebar
()
{
private
void
setupSidebar
()
{
SlidingPaneLayout
pane
=
(
SlidingPaneLayout
)
findViewById
(
R
.
id
.
sliding_pane
);
SlidingPaneLayout
pane
=
(
SlidingPaneLayout
)
findViewById
(
R
.
id
.
sliding_pane
);
if
(
pane
==
null
)
{
if
(
pane
==
null
)
{
...
@@ -157,10 +111,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
...
@@ -157,10 +111,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
return
false
;
return
false
;
}
}
private
boolean
shouldLaunchAddServerActivity
()
{
return
ConnectivityManager
.
getInstance
(
getApplicationContext
()).
getServerList
().
isEmpty
();
}
@DebugLog
@DebugLog
@Override
@Override
protected
void
onHostnameUpdated
()
{
protected
void
onHostnameUpdated
()
{
...
@@ -184,10 +134,14 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
...
@@ -184,10 +134,14 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
presenter
=
new
MainPresenter
(
presenter
=
new
MainPresenter
(
roomInteractor
,
roomInteractor
,
createRoomInteractor
,
createRoomInteractor
,
sessionInteractor
sessionInteractor
,
new
MethodCallHelper
(
this
,
hostname
),
ConnectivityManager
.
getInstance
(
getApplicationContext
())
);
);
updateSidebarMainFragment
();
updateSidebarMainFragment
();
presenter
.
bindView
(
this
);
}
}
private
void
updateSidebarMainFragment
()
{
private
void
updateSidebarMainFragment
()
{
...
@@ -226,6 +180,11 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
...
@@ -226,6 +180,11 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
}
}
}
}
@Override
public
void
showAddServerScreen
()
{
LaunchUtil
.
showAddServerActivity
(
this
);
}
@Override
@Override
public
void
showLoginScreen
()
{
public
void
showLoginScreen
()
{
LaunchUtil
.
showLoginActivity
(
this
,
hostname
);
LaunchUtil
.
showLoginActivity
(
this
,
hostname
);
...
...
app/src/main/java/chat/rocket/android/activity/MainContract.java
View file @
6cb7bfdf
...
@@ -12,6 +12,8 @@ public interface MainContract {
...
@@ -12,6 +12,8 @@ public interface MainContract {
void
showUnreadCount
(
long
roomsCount
,
int
mentionsCount
);
void
showUnreadCount
(
long
roomsCount
,
int
mentionsCount
);
void
showAddServerScreen
();
void
showLoginScreen
();
void
showLoginScreen
();
void
showConnectionError
();
void
showConnectionError
();
...
...
app/src/main/java/chat/rocket/android/activity/MainPresenter.java
View file @
6cb7bfdf
...
@@ -8,11 +8,15 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
...
@@ -8,11 +8,15 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import
io.reactivex.disposables.Disposable
;
import
io.reactivex.disposables.Disposable
;
import
chat.rocket.android.BackgroundLooper
;
import
chat.rocket.android.BackgroundLooper
;
import
chat.rocket.android.api.MethodCallHelper
;
import
chat.rocket.android.helper.LogIfError
;
import
chat.rocket.android.service.ConnectivityManagerApi
;
import
chat.rocket.android.shared.BasePresenter
;
import
chat.rocket.android.shared.BasePresenter
;
import
chat.rocket.core.interactors.CanCreateRoomInteractor
;
import
chat.rocket.core.interactors.CanCreateRoomInteractor
;
import
chat.rocket.core.interactors.RoomInteractor
;
import
chat.rocket.core.interactors.RoomInteractor
;
import
chat.rocket.core.interactors.SessionInteractor
;
import
chat.rocket.core.interactors.SessionInteractor
;
import
chat.rocket.core.models.Session
;
import
chat.rocket.core.models.Session
;
import
chat.rocket.core.models.User
;
public
class
MainPresenter
extends
BasePresenter
<
MainContract
.
View
>
public
class
MainPresenter
extends
BasePresenter
<
MainContract
.
View
>
implements
MainContract
.
Presenter
{
implements
MainContract
.
Presenter
{
...
@@ -20,21 +24,42 @@ public class MainPresenter extends BasePresenter<MainContract.View>
...
@@ -20,21 +24,42 @@ public class MainPresenter extends BasePresenter<MainContract.View>
private
final
CanCreateRoomInteractor
canCreateRoomInteractor
;
private
final
CanCreateRoomInteractor
canCreateRoomInteractor
;
private
final
RoomInteractor
roomInteractor
;
private
final
RoomInteractor
roomInteractor
;
private
final
SessionInteractor
sessionInteractor
;
private
final
SessionInteractor
sessionInteractor
;
private
final
MethodCallHelper
methodCallHelper
;
private
final
ConnectivityManagerApi
connectivityManagerApi
;
public
MainPresenter
(
RoomInteractor
roomInteractor
,
public
MainPresenter
(
RoomInteractor
roomInteractor
,
CanCreateRoomInteractor
canCreateRoomInteractor
,
CanCreateRoomInteractor
canCreateRoomInteractor
,
SessionInteractor
sessionInteractor
)
{
SessionInteractor
sessionInteractor
,
MethodCallHelper
methodCallHelper
,
ConnectivityManagerApi
connectivityManagerApi
)
{
this
.
roomInteractor
=
roomInteractor
;
this
.
roomInteractor
=
roomInteractor
;
this
.
canCreateRoomInteractor
=
canCreateRoomInteractor
;
this
.
canCreateRoomInteractor
=
canCreateRoomInteractor
;
this
.
sessionInteractor
=
sessionInteractor
;
this
.
sessionInteractor
=
sessionInteractor
;
this
.
methodCallHelper
=
methodCallHelper
;
this
.
connectivityManagerApi
=
connectivityManagerApi
;
}
}
@Override
@Override
public
void
bindView
(
@NonNull
MainContract
.
View
view
)
{
public
void
bindView
(
@NonNull
MainContract
.
View
view
)
{
super
.
bindView
(
view
);
super
.
bindView
(
view
);
view
.
showHome
();
if
(
shouldLaunchAddServerActivity
())
{
view
.
showAddServerScreen
();
return
;
}
subscribeToUnreadCount
();
subscribeToUnreadCount
();
subscribeToSession
();
subscribeToSession
();
setUserOnline
();
}
@Override
public
void
release
()
{
setUserAway
();
super
.
release
();
}
}
@Override
@Override
...
@@ -101,4 +126,18 @@ public class MainPresenter extends BasePresenter<MainContract.View>
...
@@ -101,4 +126,18 @@ public class MainPresenter extends BasePresenter<MainContract.View>
addSubscription
(
subscription
);
addSubscription
(
subscription
);
}
}
private
void
setUserOnline
()
{
methodCallHelper
.
setUserPresence
(
User
.
STATUS_ONLINE
)
.
continueWith
(
new
LogIfError
());
}
private
void
setUserAway
()
{
methodCallHelper
.
setUserPresence
(
User
.
STATUS_AWAY
)
.
continueWith
(
new
LogIfError
());
}
private
boolean
shouldLaunchAddServerActivity
()
{
return
connectivityManagerApi
.
getServerList
().
isEmpty
();
}
}
}
app/src/main/java/chat/rocket/android/api/rest/DefaultCookieProvider.java
View file @
6cb7bfdf
package
chat
.
rocket
.
android
.
api
.
rest
;
package
chat
.
rocket
.
android
.
api
.
rest
;
import
android.content.Context
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.persistence.realm.models.ddp.RealmUser
;
import
chat.rocket.persistence.realm.models.ddp.RealmUser
;
import
chat.rocket.persistence.realm.models.internal.RealmSession
;
import
chat.rocket.persistence.realm.models.internal.RealmSession
;
...
@@ -10,10 +8,10 @@ import chat.rocket.persistence.realm.RealmStore;
...
@@ -10,10 +8,10 @@ import chat.rocket.persistence.realm.RealmStore;
public
class
DefaultCookieProvider
implements
CookieProvider
{
public
class
DefaultCookieProvider
implements
CookieProvider
{
private
final
Context
applicationContext
;
private
RocketChatCache
rocketChatCache
;
public
DefaultCookieProvider
(
Context
context
)
{
public
DefaultCookieProvider
(
RocketChatCache
rocketChatCache
)
{
applicationContext
=
context
.
getApplicationContext
()
;
this
.
rocketChatCache
=
rocketChatCache
;
}
}
@Override
@Override
...
@@ -46,6 +44,6 @@ public class DefaultCookieProvider implements CookieProvider {
...
@@ -46,6 +44,6 @@ public class DefaultCookieProvider implements CookieProvider {
}
}
private
String
getHostnameFromCache
()
{
private
String
getHostnameFromCache
()
{
return
RocketChatCache
.
getSelectedServerHostname
(
applicationContext
);
return
rocketChatCache
.
getSelectedServerHostname
(
);
}
}
}
}
app/src/main/java/chat/rocket/android/fragment/add_server/InputHostnameFragment.java
View file @
6cb7bfdf
...
@@ -32,7 +32,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
...
@@ -32,7 +32,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
final
Context
appContext
=
getContext
().
getApplicationContext
();
final
Context
appContext
=
getContext
().
getApplicationContext
();
presenter
=
new
InputHostnamePresenter
(
presenter
=
new
InputHostnamePresenter
(
RocketChatCache
.
get
(
appContext
),
new
RocketChatCache
(
appContext
),
ConnectivityManager
.
getInstance
(
appContext
));
ConnectivityManager
.
getInstance
(
appContext
));
}
}
...
...
app/src/main/java/chat/rocket/android/fragment/add_server/InputHostnamePresenter.java
View file @
6cb7bfdf
package
chat
.
rocket
.
android
.
fragment
.
add_server
;
package
chat
.
rocket
.
android
.
fragment
.
add_server
;
import
android.content.SharedPreferences
;
import
io.reactivex.android.schedulers.AndroidSchedulers
;
import
io.reactivex.android.schedulers.AndroidSchedulers
;
import
io.reactivex.disposables.Disposable
;
import
io.reactivex.disposables.Disposable
;
import
io.reactivex.schedulers.Schedulers
;
import
io.reactivex.schedulers.Schedulers
;
...
@@ -18,10 +16,10 @@ import chat.rocket.android.shared.BasePresenter;
...
@@ -18,10 +16,10 @@ import chat.rocket.android.shared.BasePresenter;
public
class
InputHostnamePresenter
extends
BasePresenter
<
InputHostnameContract
.
View
>
public
class
InputHostnamePresenter
extends
BasePresenter
<
InputHostnameContract
.
View
>
implements
InputHostnameContract
.
Presenter
{
implements
InputHostnameContract
.
Presenter
{
private
final
SharedPreferences
rocketChatCache
;
private
final
RocketChatCache
rocketChatCache
;
private
final
ConnectivityManagerApi
connectivityManager
;
private
final
ConnectivityManagerApi
connectivityManager
;
public
InputHostnamePresenter
(
SharedPreferences
rocketChatCache
,
public
InputHostnamePresenter
(
RocketChatCache
rocketChatCache
,
ConnectivityManagerApi
connectivityManager
)
{
ConnectivityManagerApi
connectivityManager
)
{
this
.
rocketChatCache
=
rocketChatCache
;
this
.
rocketChatCache
=
rocketChatCache
;
this
.
connectivityManager
=
connectivityManager
;
this
.
connectivityManager
=
connectivityManager
;
...
@@ -63,9 +61,7 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.
...
@@ -63,9 +61,7 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.
}
}
private
void
onServerValid
(
final
String
hostname
,
boolean
usesSecureConnection
)
{
private
void
onServerValid
(
final
String
hostname
,
boolean
usesSecureConnection
)
{
rocketChatCache
.
edit
()
rocketChatCache
.
setSelectedServerHostname
(
hostname
);
.
putString
(
RocketChatCache
.
KEY_SELECTED_SERVER_HOSTNAME
,
hostname
)
.
apply
();
connectivityManager
.
addOrUpdateServer
(
hostname
,
hostname
,
!
usesSecureConnection
);
connectivityManager
.
addOrUpdateServer
(
hostname
,
hostname
,
!
usesSecureConnection
);
connectivityManager
.
keepAliveServer
();
connectivityManager
.
keepAliveServer
();
...
...
app/src/main/java/chat/rocket/android/fragment/sidebar/SidebarMainFragment.java
View file @
6cb7bfdf
...
@@ -47,6 +47,8 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
...
@@ -47,6 +47,8 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
private
String
hostname
;
private
String
hostname
;
private
RocketChatCache
rocketChatCache
;
public
SidebarMainFragment
()
{
public
SidebarMainFragment
()
{
}
}
...
@@ -70,6 +72,8 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
...
@@ -70,6 +72,8 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
Bundle
args
=
getArguments
();
Bundle
args
=
getArguments
();
hostname
=
args
==
null
?
null
:
args
.
getString
(
HOSTNAME
);
hostname
=
args
==
null
?
null
:
args
.
getString
(
HOSTNAME
);
rocketChatCache
=
new
RocketChatCache
(
getContext
());
presenter
=
new
SidebarMainPresenter
(
presenter
=
new
SidebarMainPresenter
(
hostname
,
hostname
,
new
RoomInteractor
(
new
RealmRoomRepository
(
hostname
)),
new
RoomInteractor
(
new
RealmRoomRepository
(
hostname
)),
...
@@ -103,9 +107,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
...
@@ -103,9 +107,7 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
setupVersionInfo
();
setupVersionInfo
();
adapter
=
new
RoomListAdapter
();
adapter
=
new
RoomListAdapter
();
adapter
.
setOnItemClickListener
(
room
->
RocketChatCache
.
get
(
getContext
()).
edit
()
adapter
.
setOnItemClickListener
(
room
->
rocketChatCache
.
setSelectedRoomId
(
room
.
getRoomId
()));
.
putString
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
,
room
.
getRoomId
())
.
apply
());
RecyclerView
recyclerView
=
(
RecyclerView
)
rootView
.
findViewById
(
R
.
id
.
room_list_container
);
RecyclerView
recyclerView
=
(
RecyclerView
)
rootView
.
findViewById
(
R
.
id
.
room_list_container
);
recyclerView
.
setLayoutManager
(
recyclerView
.
setLayoutManager
(
...
...
app/src/main/java/chat/rocket/android/helper/OkHttpHelper.java
View file @
6cb7bfdf
...
@@ -4,6 +4,7 @@ import android.content.Context;
...
@@ -4,6 +4,7 @@ import android.content.Context;
import
com.facebook.stetho.okhttp3.StethoInterceptor
;
import
com.facebook.stetho.okhttp3.StethoInterceptor
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.TimeUnit
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.api.rest.CookieInterceptor
;
import
chat.rocket.android.api.rest.CookieInterceptor
;
import
chat.rocket.android.api.rest.DefaultCookieProvider
;
import
chat.rocket.android.api.rest.DefaultCookieProvider
;
import
okhttp3.OkHttpClient
;
import
okhttp3.OkHttpClient
;
...
@@ -20,7 +21,8 @@ public class OkHttpHelper {
...
@@ -20,7 +21,8 @@ public class OkHttpHelper {
if
(
httpClientForDownloadFile
==
null
)
{
if
(
httpClientForDownloadFile
==
null
)
{
httpClientForDownloadFile
=
new
OkHttpClient
.
Builder
()
httpClientForDownloadFile
=
new
OkHttpClient
.
Builder
()
.
addNetworkInterceptor
(
new
StethoInterceptor
())
.
addNetworkInterceptor
(
new
StethoInterceptor
())
.
addInterceptor
(
new
CookieInterceptor
(
new
DefaultCookieProvider
(
context
)))
.
addInterceptor
(
new
CookieInterceptor
(
new
DefaultCookieProvider
(
new
RocketChatCache
(
context
))))
.
build
();
.
build
();
}
}
return
httpClientForDownloadFile
;
return
httpClientForDownloadFile
;
...
...
app/src/main/java/chat/rocket/android/service/internal/AbstractRocketChatCacheObserver.java
View file @
6cb7bfdf
package
chat
.
rocket
.
android
.
service
.
internal
;
package
chat
.
rocket
.
android
.
service
.
internal
;
import
android.content.Context
;
import
android.content.Context
;
import
android.content.SharedPreferences
;
import
io.reactivex.disposables.CompositeDisposable
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.RocketChatCache
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.helper.TextUtils
;
...
@@ -13,20 +14,14 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
...
@@ -13,20 +14,14 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
private
final
Context
context
;
private
final
Context
context
;
private
final
RealmHelper
realmHelper
;
private
final
RealmHelper
realmHelper
;
private
String
roomId
;
private
String
roomId
;
private
SharedPreferences
.
OnSharedPreferenceChangeListener
listener
=
private
CompositeDisposable
compositeDisposable
=
new
CompositeDisposable
();
(
prefs
,
key
)
->
{
if
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
.
equals
(
key
))
{
updateRoomIdWith
(
prefs
);
}
};
protected
AbstractRocketChatCacheObserver
(
Context
context
,
RealmHelper
realmHelper
)
{
protected
AbstractRocketChatCacheObserver
(
Context
context
,
RealmHelper
realmHelper
)
{
this
.
context
=
context
;
this
.
context
=
context
;
this
.
realmHelper
=
realmHelper
;
this
.
realmHelper
=
realmHelper
;
}
}
private
void
updateRoomIdWith
(
SharedPreferences
prefs
)
{
private
void
updateRoomIdWith
(
String
roomId
)
{
String
roomId
=
prefs
.
getString
(
RocketChatCache
.
KEY_SELECTED_ROOM_ID
,
null
);
if
(!
TextUtils
.
isEmpty
(
roomId
))
{
if
(!
TextUtils
.
isEmpty
(
roomId
))
{
RealmRoom
room
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmRoom
room
=
realmHelper
.
executeTransactionForRead
(
realm
->
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
"rid"
,
roomId
).
findFirst
());
realm
.
where
(
RealmRoom
.
class
).
equalTo
(
"rid"
,
roomId
).
findFirst
());
...
@@ -49,13 +44,15 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
...
@@ -49,13 +44,15 @@ public abstract class AbstractRocketChatCacheObserver implements Registrable {
@Override
@Override
public
final
void
register
()
{
public
final
void
register
()
{
SharedPreferences
prefs
=
RocketChatCache
.
get
(
context
);
compositeDisposable
.
add
(
prefs
.
registerOnSharedPreferenceChangeListener
(
listener
);
new
RocketChatCache
(
context
)
updateRoomIdWith
(
prefs
);
.
getSelectedRoomIdPublisher
()
.
subscribe
(
this
::
updateRoomIdWith
)
);
}
}
@Override
@Override
public
final
void
unregister
()
{
public
final
void
unregister
()
{
RocketChatCache
.
get
(
context
).
unregisterOnSharedPreferenceChangeListener
(
listener
);
compositeDisposable
.
clear
(
);
}
}
}
}
app/src/main/java/chat/rocket/android/service/observer/GcmPushRegistrationObserver.java
View file @
6cb7bfdf
...
@@ -26,8 +26,8 @@ import chat.rocket.android.service.DDPClientRef;
...
@@ -26,8 +26,8 @@ import chat.rocket.android.service.DDPClientRef;
*/
*/
public
class
GcmPushRegistrationObserver
extends
AbstractModelObserver
<
GcmPushRegistration
>
{
public
class
GcmPushRegistrationObserver
extends
AbstractModelObserver
<
GcmPushRegistration
>
{
public
GcmPushRegistrationObserver
(
Context
context
,
String
hostname
,
public
GcmPushRegistrationObserver
(
Context
context
,
String
hostname
,
RealmHelper
realmHelper
,
RealmHelper
realmHelper
,
DDPClientRef
ddpClientRef
)
{
DDPClientRef
ddpClientRef
)
{
super
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
super
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
}
}
...
@@ -50,10 +50,10 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
...
@@ -50,10 +50,10 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
return
null
;
return
null
;
}).
onSuccessTask
(
_task
->
registerGcmTokenForServer
()
}).
onSuccessTask
(
_task
->
registerGcmTokenForServer
()
).
onSuccessTask
(
_task
->
).
onSuccessTask
(
_task
->
realmHelper
.
executeTransaction
(
realm
->
{
realmHelper
.
executeTransaction
(
realm
->
{
GcmPushRegistration
.
queryDefault
(
realm
).
findFirst
().
setSyncState
(
SyncState
.
SYNCED
);
GcmPushRegistration
.
queryDefault
(
realm
).
findFirst
().
setSyncState
(
SyncState
.
SYNCED
);
return
null
;
return
null
;
})
})
).
continueWith
(
task
->
{
).
continueWith
(
task
->
{
if
(
task
.
isFaulted
())
{
if
(
task
.
isFaulted
())
{
realmHelper
.
executeTransaction
(
realm
->
{
realmHelper
.
executeTransaction
(
realm
->
{
...
@@ -73,7 +73,7 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
...
@@ -73,7 +73,7 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
final
RealmUser
currentUser
=
realmHelper
.
executeTransactionForRead
(
realm
->
final
RealmUser
currentUser
=
realmHelper
.
executeTransactionForRead
(
realm
->
RealmUser
.
queryCurrentUser
(
realm
).
findFirst
());
RealmUser
.
queryCurrentUser
(
realm
).
findFirst
());
final
String
userId
=
currentUser
!=
null
?
currentUser
.
getId
()
:
null
;
final
String
userId
=
currentUser
!=
null
?
currentUser
.
getId
()
:
null
;
final
String
pushId
=
RocketChatCache
.
getOrCreatePushId
(
context
);
final
String
pushId
=
new
RocketChatCache
(
context
).
getOrCreatePushId
(
);
return
new
RaixPushHelper
(
realmHelper
,
ddpClientRef
)
return
new
RaixPushHelper
(
realmHelper
,
ddpClientRef
)
.
pushUpdate
(
pushId
,
gcmToken
,
userId
);
.
pushUpdate
(
pushId
,
gcmToken
,
userId
);
...
...
app/src/main/java/chat/rocket/android/service/observer/SessionObserver.java
View file @
6cb7bfdf
...
@@ -33,7 +33,8 @@ public class SessionObserver extends AbstractModelObserver<RealmSession> {
...
@@ -33,7 +33,8 @@ public class SessionObserver extends AbstractModelObserver<RealmSession> {
super
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
super
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
count
=
0
;
count
=
0
;
streamNotifyMessage
=
new
StreamRoomMessageManager
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
streamNotifyMessage
=
new
StreamRoomMessageManager
(
context
,
hostname
,
realmHelper
,
ddpClientRef
);
pushHelper
=
new
RaixPushHelper
(
realmHelper
,
ddpClientRef
);
pushHelper
=
new
RaixPushHelper
(
realmHelper
,
ddpClientRef
);
}
}
...
@@ -72,7 +73,7 @@ public class SessionObserver extends AbstractModelObserver<RealmSession> {
...
@@ -72,7 +73,7 @@ public class SessionObserver extends AbstractModelObserver<RealmSession> {
// update push info
// update push info
pushHelper
pushHelper
.
pushSetUser
(
RocketChatCache
.
getOrCreatePushId
(
context
))
.
pushSetUser
(
new
RocketChatCache
(
context
).
getOrCreatePushId
(
))
.
continueWith
(
new
LogIfError
());
.
continueWith
(
new
LogIfError
());
}
}
...
...
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