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
03f23709
Commit
03f23709
authored
Nov 07, 2016
by
Yusuke Iwaki
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
GitHub authentication
parent
eb93a930
Changes
15
Show whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
632 additions
and
15 deletions
+632
-15
ServerConfigActivity.java
...va/chat/rocket/android/activity/ServerConfigActivity.java
+29
-4
AbstractWebViewFragment.java
...chat/rocket/android/fragment/AbstractWebViewFragment.java
+98
-0
GitHubOAuthWebViewFragment.java
...et/android/fragment/login/GitHubOAuthWebViewFragment.java
+164
-0
AuthenticatingFragment.java
...ndroid/fragment/server_config/AuthenticatingFragment.java
+19
-0
ConnectingToHostFragment.java
...roid/fragment/server_config/ConnectingToHostFragment.java
+4
-1
LoginFragment.java
.../rocket/android/fragment/server_config/LoginFragment.java
+65
-0
ServerConfig.java
...src/main/java/chat/rocket/android/model/ServerConfig.java
+5
-5
ServerConfigCredential.java
...ava/chat/rocket/android/model/ServerConfigCredential.java
+76
-0
RocketChatWebSocketThread.java
...hat/rocket/android/service/RocketChatWebSocketThread.java
+12
-2
LoginCredentialObserver.java
...ket/android/service/observer/LoginCredentialObserver.java
+77
-0
RocketChatWebSocketAPI.java
...n/java/chat/rocket/android/ws/RocketChatWebSocketAPI.java
+55
-0
fragment_login.xml
app/src/main/res/layout/fragment_login.xml
+2
-1
fragment_wait_for_connection.xml
app/src/main/res/layout/fragment_wait_for_connection.xml
+18
-2
webview.xml
app/src/main/res/layout/webview.xml
+7
-0
margin_dimens.xml
app/src/main/res/values/margin_dimens.xml
+1
-0
No files found.
app/src/main/java/chat/rocket/android/activity/ServerConfigActivity.java
View file @
03f23709
...
@@ -7,16 +7,21 @@ import android.support.annotation.Nullable;
...
@@ -7,16 +7,21 @@ import android.support.annotation.Nullable;
import
android.support.v4.app.Fragment
;
import
android.support.v4.app.Fragment
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.LaunchUtil
;
import
chat.rocket.android.R
;
import
chat.rocket.android.R
;
import
chat.rocket.android.fragment.login.GitHubOAuthWebViewFragment
;
import
chat.rocket.android.fragment.server_config.AuthenticatingFragment
;
import
chat.rocket.android.fragment.server_config.ConnectingToHostFragment
;
import
chat.rocket.android.fragment.server_config.ConnectingToHostFragment
;
import
chat.rocket.android.fragment.server_config.InputHostnameFragment
;
import
chat.rocket.android.fragment.server_config.InputHostnameFragment
;
import
chat.rocket.android.fragment.server_config.LoginFragment
;
import
chat.rocket.android.fragment.server_config.LoginFragment
;
import
chat.rocket.android.helper.LogcatIfError
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.model.ServerConfigCredential
;
import
chat.rocket.android.service.RocketChatService
;
import
chat.rocket.android.service.RocketChatService
;
import
io.realm.Realm
;
import
io.realm.Realm
;
import
io.realm.RealmQuery
;
import
io.realm.RealmQuery
;
import
java.util.List
;
import
java.util.List
;
import
jp.co.crowdworks.realm_java_helpers.RealmObjectObserver
;
import
jp.co.crowdworks.realm_java_helpers.RealmObjectObserver
;
import
jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts
;
/**
/**
* Activity for Login, Sign-up, and Connecting...
* Activity for Login, Sign-up, and Connecting...
...
@@ -54,7 +59,8 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
...
@@ -54,7 +59,8 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
}
}
for
(
ServerConfig
config
:
configList
)
{
for
(
ServerConfig
config
:
configList
)
{
if
(
TextUtils
.
isEmpty
(
config
.
getSelectedProviderName
()))
{
ServerConfigCredential
credential
=
config
.
getCredential
();
if
(
credential
!=
null
&&
!
TextUtils
.
isEmpty
(
credential
.
getType
()))
{
return
launchFor
(
context
,
config
);
return
launchFor
(
context
,
config
);
}
}
}
}
...
@@ -128,9 +134,13 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
...
@@ -128,9 +134,13 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
return
;
return
;
}
}
final
String
selectedProviderName
=
config
.
getSelectedProviderName
();
final
ServerConfigCredential
credential
=
config
.
getCredential
();
if
(!
TextUtils
.
isEmpty
(
selectedProviderName
))
{
if
(
credential
!=
null
&&
!
TextUtils
.
isEmpty
(
credential
.
getType
()))
{
if
(
ServerConfigCredential
.
hasSecret
(
credential
))
{
showFragment
(
new
AuthenticatingFragment
());
}
else
{
showFragment
(
getAuthFragmentFor
(
credential
.
getType
()));
}
return
;
return
;
}
}
...
@@ -149,6 +159,21 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
...
@@ -149,6 +159,21 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
showFragment
(
new
InputHostnameFragment
());
showFragment
(
new
InputHostnameFragment
());
}
}
private
Fragment
getAuthFragmentFor
(
final
String
authType
)
{
if
(
"github"
.
equals
(
authType
))
{
return
GitHubOAuthWebViewFragment
.
create
(
serverConfigId
);
}
else
if
(
"twitter"
.
equals
(
authType
))
{
// TODO
}
RealmHelperBolts
.
executeTransaction
(
realm
->
realm
.
where
(
ServerConfigCredential
.
class
)
.
equalTo
(
"type"
,
authType
)
.
findAll
()
.
deleteAllFromRealm
()
).
continueWith
(
new
LogcatIfError
());
throw
new
IllegalArgumentException
(
"Invalid authType given:"
+
authType
);
}
@Override
protected
void
showFragment
(
Fragment
fragment
)
{
@Override
protected
void
showFragment
(
Fragment
fragment
)
{
injectServerConfigIdArgTo
(
fragment
);
injectServerConfigIdArgTo
(
fragment
);
super
.
showFragment
(
fragment
);
super
.
showFragment
(
fragment
);
...
...
app/src/main/java/chat/rocket/android/fragment/AbstractWebViewFragment.java
0 → 100644
View file @
03f23709
package
chat
.
rocket
.
android
.
fragment
;
import
android.graphics.Bitmap
;
import
android.os.Build
;
import
android.os.Message
;
import
android.view.View
;
import
android.webkit.WebResourceError
;
import
android.webkit.WebResourceRequest
;
import
android.webkit.WebSettings
;
import
android.webkit.WebView
;
import
android.webkit.WebViewClient
;
import
chat.rocket.android.R
;
import
chat.rocket.android.helper.OnBackPressListener
;
import
hugo.weaving.DebugLog
;
public
abstract
class
AbstractWebViewFragment
extends
AbstractFragment
implements
OnBackPressListener
{
private
WebView
webview
;
private
WebViewClient
mWebViewClient
=
new
WebViewClient
()
{
private
boolean
mError
;
@Override
public
void
onPageStarted
(
WebView
webview
,
String
url
,
Bitmap
favicon
)
{
mError
=
false
;
}
@Override
public
void
onPageFinished
(
WebView
webview
,
String
url
)
{
if
(!
mError
)
onPageLoaded
(
webview
,
url
);
}
@Override
public
void
onReceivedError
(
WebView
view
,
WebResourceRequest
request
,
WebResourceError
error
)
{
super
.
onReceivedError
(
view
,
request
,
error
);
mError
=
true
;
}
@Override
public
boolean
shouldOverrideUrlLoading
(
WebView
webview
,
String
url
)
{
return
(
shouldOverride
(
webview
,
url
)
&&
onHandleCallback
(
webview
,
url
))
||
super
.
shouldOverrideUrlLoading
(
webview
,
url
);
}
@DebugLog
@Override
public
void
onFormResubmission
(
WebView
view
,
Message
dontResend
,
Message
resend
)
{
//resend POST request without confirmation.
resend
.
sendToTarget
();
}
};
@Override
protected
int
getLayout
()
{
return
R
.
layout
.
webview
;
}
@Override
protected
void
onSetupView
()
{
webview
=
(
WebView
)
rootView
.
findViewById
(
R
.
id
.
webview
);
setupWebView
();
navigateToInitialPage
(
webview
);
}
private
void
setupWebView
()
{
WebSettings
settings
=
webview
.
getSettings
();
if
(
settings
!=
null
)
{
settings
.
setJavaScriptEnabled
(
true
);
}
webview
.
setHorizontalScrollBarEnabled
(
false
);
webview
.
setWebViewClient
(
mWebViewClient
);
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
KITKAT
)
{
WebView
.
setWebContentsDebuggingEnabled
(
true
);
}
if
(
Build
.
VERSION
.
SDK_INT
==
Build
.
VERSION_CODES
.
JELLY_BEAN
)
{
//refs: https://code.google.com/p/android/issues/detail?id=35288
webview
.
setLayerType
(
View
.
LAYER_TYPE_SOFTWARE
,
null
);
}
}
@Override
public
boolean
onBackPressed
()
{
if
(
webview
.
canGoBack
())
{
webview
.
goBack
();
return
true
;
}
else
{
return
false
;
}
}
protected
abstract
void
navigateToInitialPage
(
WebView
webview
);
protected
void
onPageLoaded
(
WebView
webview
,
String
url
)
{
}
protected
boolean
shouldOverride
(
WebView
webview
,
String
url
)
{
return
false
;
}
protected
boolean
onHandleCallback
(
WebView
webview
,
String
url
)
{
return
false
;
}
;
}
app/src/main/java/chat/rocket/android/fragment/login/GitHubOAuthWebViewFragment.java
0 → 100644
View file @
03f23709
package
chat
.
rocket
.
android
.
fragment
.
login
;
import
android.os.Bundle
;
import
android.support.annotation.Nullable
;
import
android.support.v4.app.Fragment
;
import
android.text.TextUtils
;
import
android.util.Base64
;
import
android.webkit.JavascriptInterface
;
import
android.webkit.WebView
;
import
chat.rocket.android.fragment.AbstractWebViewFragment
;
import
chat.rocket.android.helper.LogcatIfError
;
import
chat.rocket.android.model.MeteorLoginServiceConfiguration
;
import
chat.rocket.android.model.ServerConfig
;
import
jp.co.crowdworks.realm_java_helpers.RealmHelper
;
import
jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts
;
import
okhttp3.HttpUrl
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
timber.log.Timber
;
public
class
GitHubOAuthWebViewFragment
extends
AbstractWebViewFragment
{
private
String
serverConfigId
;
private
String
hostname
;
private
String
url
;
private
boolean
resultOK
;
public
static
Fragment
create
(
final
String
serverConfigId
)
{
Bundle
args
=
new
Bundle
();
args
.
putString
(
"server_config_id"
,
serverConfigId
);
Fragment
fragment
=
new
GitHubOAuthWebViewFragment
();
fragment
.
setArguments
(
args
);
return
fragment
;
}
private
boolean
hasValidArgs
(
Bundle
args
)
{
return
args
!=
null
&&
args
.
containsKey
(
"server_config_id"
);
}
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
Bundle
args
=
getArguments
();
if
(!
hasValidArgs
(
args
))
{
throw
new
IllegalArgumentException
(
"server_config_id required"
);
}
serverConfigId
=
args
.
getString
(
"server_config_id"
);
ServerConfig
serverConfig
=
RealmHelper
.
executeTransactionForRead
(
realm
->
realm
.
where
(
ServerConfig
.
class
).
equalTo
(
"id"
,
serverConfigId
).
findFirst
());
MeteorLoginServiceConfiguration
oauthConfig
=
RealmHelper
.
executeTransactionForRead
(
realm
->
realm
.
where
(
MeteorLoginServiceConfiguration
.
class
)
.
equalTo
(
"service"
,
"github"
)
.
equalTo
(
"serverConfig.id"
,
serverConfigId
)
.
findFirst
());
if
(
serverConfig
==
null
||
oauthConfig
==
null
)
{
throw
new
IllegalArgumentException
(
"Invalid server_config_id given,"
);
}
hostname
=
serverConfig
.
getHostname
();
url
=
generateURL
(
oauthConfig
.
getClientId
());
}
private
String
generateURL
(
String
clientId
)
{
try
{
String
state
=
Base64
.
encodeToString
(
new
JSONObject
().
put
(
"loginStyle"
,
"popup"
)
.
put
(
"credentialToken"
,
"github"
+
System
.
currentTimeMillis
())
.
put
(
"isCordova"
,
true
)
.
toString
()
.
getBytes
(),
Base64
.
NO_WRAP
);
return
new
HttpUrl
.
Builder
().
scheme
(
"https"
)
.
host
(
"github.com"
)
.
addPathSegment
(
"login"
)
.
addPathSegment
(
"oauth"
)
.
addPathSegment
(
"authorize"
)
.
addQueryParameter
(
"client_id"
,
clientId
)
.
addQueryParameter
(
"scope"
,
"user:email"
)
.
addQueryParameter
(
"state"
,
state
)
.
build
()
.
toString
();
}
catch
(
Exception
exception
)
{
Timber
.
e
(
exception
,
"failed to generate GitHub OAUth URL"
);
}
return
null
;
}
@Override
protected
void
navigateToInitialPage
(
WebView
webview
)
{
if
(
TextUtils
.
isEmpty
(
url
))
{
finish
();
return
;
}
resultOK
=
false
;
webview
.
loadUrl
(
url
);
webview
.
addJavascriptInterface
(
new
JSInterface
(
result
->
{
// onPageFinish is called twice... Should ignore latter one.
if
(
resultOK
)
{
return
;
}
if
(
result
!=
null
&&
result
.
optBoolean
(
"setCredentialToken"
,
false
))
{
try
{
final
String
credentialToken
=
result
.
getString
(
"credentialToken"
);
final
String
credentialSecret
=
result
.
getString
(
"credentialSecret"
);
handleOAuthCallback
(
credentialToken
,
credentialSecret
);
resultOK
=
true
;
}
catch
(
JSONException
exception
)
{
Timber
.
e
(
exception
,
"failed to parse OAuth result."
);
}
}
onOAuthCompleted
();
}),
"_rocketchet_hook"
);
}
@Override
protected
void
onPageLoaded
(
WebView
webview
,
String
url
)
{
super
.
onPageLoaded
(
webview
,
url
);
if
(
url
.
contains
(
hostname
)
&&
url
.
contains
(
"_oauth/github?close"
))
{
webview
.
loadUrl
(
"javascript:window._rocketchet_hook.handleConfig(document.getElementById('config').innerText);"
);
}
}
private
interface
JSInterfaceCallback
{
void
hanldeResult
(
@Nullable
JSONObject
result
);
}
private
static
final
class
JSInterface
{
private
final
JSInterfaceCallback
mCallback
;
public
JSInterface
(
JSInterfaceCallback
callback
)
{
mCallback
=
callback
;
}
@JavascriptInterface
public
void
handleConfig
(
String
config
)
{
try
{
mCallback
.
hanldeResult
(
new
JSONObject
(
config
));
}
catch
(
Exception
e
)
{
mCallback
.
hanldeResult
(
null
);
}
}
}
private
void
handleOAuthCallback
(
final
String
credentialToken
,
final
String
credentialSecret
)
{
RealmHelperBolts
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
ServerConfig
.
class
,
new
JSONObject
()
.
put
(
"id"
,
serverConfigId
)
.
put
(
"credential"
,
new
JSONObject
()
.
put
(
"type"
,
"github"
)
.
put
(
"credentialToken"
,
credentialToken
)
.
put
(
"credentialSecret"
,
credentialSecret
))
)
).
continueWith
(
new
LogcatIfError
());
}
private
void
onOAuthCompleted
()
{
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/AuthenticatingFragment.java
0 → 100644
View file @
03f23709
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
import
android.view.View
;
import
android.widget.TextView
;
import
chat.rocket.android.R
;
/**
*/
public
class
AuthenticatingFragment
extends
AbstractServerConfigFragment
{
@Override
protected
int
getLayout
()
{
return
R
.
layout
.
fragment_wait_for_connection
;
}
@Override
protected
void
onSetupView
()
{
TextView
caption
=
(
TextView
)
rootView
.
findViewById
(
R
.
id
.
txt_caption
);
caption
.
setVisibility
(
View
.
VISIBLE
);
caption
.
setText
(
"Authenticationg..."
);
}
}
\ No newline at end of file
app/src/main/java/chat/rocket/android/fragment/server_config/ConnectingToHostFragment.java
View file @
03f23709
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
import
android.view.View
;
import
android.widget.TextView
;
import
chat.rocket.android.R
;
import
chat.rocket.android.R
;
/**
/**
...
@@ -11,6 +13,7 @@ public class ConnectingToHostFragment extends AbstractServerConfigFragment {
...
@@ -11,6 +13,7 @@ public class ConnectingToHostFragment extends AbstractServerConfigFragment {
}
}
@Override
protected
void
onSetupView
()
{
@Override
protected
void
onSetupView
()
{
TextView
caption
=
(
TextView
)
rootView
.
findViewById
(
R
.
id
.
txt_caption
);
caption
.
setVisibility
(
View
.
GONE
);
}
}
}
}
app/src/main/java/chat/rocket/android/fragment/server_config/LoginFragment.java
View file @
03f23709
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
package
chat
.
rocket
.
android
.
fragment
.
server_config
;
import
android.view.View
;
import
android.view.View
;
import
android.widget.TextView
;
import
chat.rocket.android.R
;
import
chat.rocket.android.R
;
import
chat.rocket.android.helper.LogcatIfError
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.model.MeteorLoginServiceConfiguration
;
import
chat.rocket.android.model.MeteorLoginServiceConfiguration
;
import
chat.rocket.android.model.ServerConfig
;
import
io.realm.Realm
;
import
io.realm.Realm
;
import
io.realm.RealmResults
;
import
io.realm.RealmResults
;
import
java.security.MessageDigest
;
import
java.security.NoSuchAlgorithmException
;
import
java.util.List
;
import
java.util.List
;
import
jp.co.crowdworks.realm_java_helpers.RealmListObserver
;
import
jp.co.crowdworks.realm_java_helpers.RealmListObserver
;
import
jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts
;
import
org.json.JSONObject
;
/**
/**
* Login screen.
* Login screen.
...
@@ -30,7 +38,39 @@ public class LoginFragment extends AbstractServerConfigFragment {
...
@@ -30,7 +38,39 @@ public class LoginFragment extends AbstractServerConfigFragment {
};
};
@Override
protected
void
onSetupView
()
{
@Override
protected
void
onSetupView
()
{
final
View
btnEmail
=
rootView
.
findViewById
(
R
.
id
.
btn_login_with_email
);
final
TextView
txtUsername
=
(
TextView
)
rootView
.
findViewById
(
R
.
id
.
editor_username
);
final
TextView
txtPasswd
=
(
TextView
)
rootView
.
findViewById
(
R
.
id
.
editor_passwd
);
btnEmail
.
setOnClickListener
(
view
->
{
final
CharSequence
username
=
txtUsername
.
getText
();
final
CharSequence
passwd
=
txtPasswd
.
getText
();
if
(
TextUtils
.
isEmpty
(
username
)
||
TextUtils
.
isEmpty
(
passwd
))
return
;
RealmHelperBolts
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
ServerConfig
.
class
,
new
JSONObject
()
.
put
(
"id"
,
serverConfigId
)
.
put
(
"credential"
,
new
JSONObject
()
.
put
(
"type"
,
"email"
)
.
put
(
"username"
,
username
.
toString
())
.
put
(
"hashedPasswd"
,
sha256sum
(
passwd
.
toString
())))
)
).
continueWith
(
new
LogcatIfError
());
});
}
private
static
String
sha256sum
(
String
orig
)
{
MessageDigest
d
=
null
;
try
{
d
=
MessageDigest
.
getInstance
(
"SHA-256"
);
}
catch
(
NoSuchAlgorithmException
e
)
{
return
null
;
}
d
.
update
(
orig
.
getBytes
());
StringBuilder
sb
=
new
StringBuilder
();
for
(
byte
b
:
d
.
digest
())
sb
.
append
(
String
.
format
(
"%02x"
,
b
&
0xff
));
return
sb
.
toString
();
}
}
private
void
onRenderAuthProviders
(
List
<
MeteorLoginServiceConfiguration
>
authProviders
)
{
private
void
onRenderAuthProviders
(
List
<
MeteorLoginServiceConfiguration
>
authProviders
)
{
...
@@ -42,13 +82,38 @@ public class LoginFragment extends AbstractServerConfigFragment {
...
@@ -42,13 +82,38 @@ public class LoginFragment extends AbstractServerConfigFragment {
for
(
MeteorLoginServiceConfiguration
authProvider
:
authProviders
)
{
for
(
MeteorLoginServiceConfiguration
authProvider
:
authProviders
)
{
if
(!
hasTwitter
&&
"twitter"
.
equals
(
authProvider
.
getService
()))
{
if
(!
hasTwitter
&&
"twitter"
.
equals
(
authProvider
.
getService
()))
{
hasTwitter
=
true
;
hasTwitter
=
true
;
btnTwitter
.
setOnClickListener
(
view
->
{
setAuthType
(
"twitter"
);
});
}
}
if
(!
hasGitHub
&&
"github"
.
equals
(
authProvider
.
getService
()))
{
if
(!
hasGitHub
&&
"github"
.
equals
(
authProvider
.
getService
()))
{
hasGitHub
=
true
;
hasGitHub
=
true
;
btnGitHub
.
setOnClickListener
(
view
->
{
setAuthType
(
"github"
);
});
}
}
}
}
btnTwitter
.
setVisibility
(
hasTwitter
?
View
.
VISIBLE
:
View
.
GONE
);
btnTwitter
.
setVisibility
(
hasTwitter
?
View
.
VISIBLE
:
View
.
GONE
);
btnGitHub
.
setVisibility
(
hasGitHub
?
View
.
VISIBLE
:
View
.
GONE
);
btnGitHub
.
setVisibility
(
hasGitHub
?
View
.
VISIBLE
:
View
.
GONE
);
}
}
private
void
setAuthType
(
final
String
authType
)
{
RealmHelperBolts
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
ServerConfig
.
class
,
new
JSONObject
()
.
put
(
"id"
,
serverConfigId
)
.
put
(
"credential"
,
new
JSONObject
()
.
put
(
"type"
,
authType
)))
).
continueWith
(
new
LogcatIfError
());
}
@Override
public
void
onResume
()
{
super
.
onResume
();
authProvidersObserver
.
sub
();
}
@Override
public
void
onPause
()
{
authProvidersObserver
.
unsub
();
super
.
onPause
();
}
}
}
app/src/main/java/chat/rocket/android/model/ServerConfig.java
View file @
03f23709
...
@@ -21,7 +21,7 @@ public class ServerConfig extends RealmObject {
...
@@ -21,7 +21,7 @@ public class ServerConfig extends RealmObject {
private
String
session
;
private
String
session
;
private
String
token
;
private
String
token
;
private
boolean
tokenVerified
;
private
boolean
tokenVerified
;
private
S
tring
selectedProviderName
;
private
S
erverConfigCredential
credential
;
public
static
RealmQuery
<
ServerConfig
>
queryLoginRequiredConnections
(
Realm
realm
)
{
public
static
RealmQuery
<
ServerConfig
>
queryLoginRequiredConnections
(
Realm
realm
)
{
return
realm
.
where
(
ServerConfig
.
class
).
equalTo
(
"tokenVerified"
,
false
);
return
realm
.
where
(
ServerConfig
.
class
).
equalTo
(
"tokenVerified"
,
false
);
...
@@ -95,11 +95,11 @@ public class ServerConfig extends RealmObject {
...
@@ -95,11 +95,11 @@ public class ServerConfig extends RealmObject {
this
.
tokenVerified
=
tokenVerified
;
this
.
tokenVerified
=
tokenVerified
;
}
}
public
S
tring
getSelectedProviderName
()
{
public
S
erverConfigCredential
getCredential
()
{
return
selectedProviderName
;
return
credential
;
}
}
public
void
set
SelectedProviderName
(
String
selectedProviderName
)
{
public
void
set
Credential
(
ServerConfigCredential
credential
)
{
this
.
selectedProviderName
=
selectedProviderName
;
this
.
credential
=
credential
;
}
}
}
}
app/src/main/java/chat/rocket/android/model/ServerConfigCredential.java
0 → 100644
View file @
03f23709
package
chat
.
rocket
.
android
.
model
;
import
chat.rocket.android.helper.TextUtils
;
import
io.realm.RealmObject
;
import
io.realm.annotations.PrimaryKey
;
import
jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts
;
import
org.json.JSONObject
;
public
class
ServerConfigCredential
extends
RealmObject
{
@PrimaryKey
private
String
type
;
private
String
credentialToken
;
private
String
credentialSecret
;
private
String
username
;
private
String
hashedPasswd
;
public
String
getType
()
{
return
type
;
}
public
void
setType
(
String
type
)
{
this
.
type
=
type
;
}
public
String
getCredentialToken
()
{
return
credentialToken
;
}
public
void
setCredentialToken
(
String
credentialToken
)
{
this
.
credentialToken
=
credentialToken
;
}
public
String
getCredentialSecret
()
{
return
credentialSecret
;
}
public
void
setCredentialSecret
(
String
credentialSecret
)
{
this
.
credentialSecret
=
credentialSecret
;
}
public
String
getUsername
()
{
return
username
;
}
public
void
setUsername
(
String
username
)
{
this
.
username
=
username
;
}
public
String
getHashedPasswd
()
{
return
hashedPasswd
;
}
public
void
setHashedPasswd
(
String
hashedPasswd
)
{
this
.
hashedPasswd
=
hashedPasswd
;
}
public
static
boolean
hasSecret
(
ServerConfigCredential
credential
)
{
if
(
credential
==
null
)
{
return
false
;
}
final
String
authType
=
credential
.
getType
();
if
(
TextUtils
.
isEmpty
(
authType
))
{
return
false
;
}
if
(
"github"
.
equals
(
authType
)
||
"twitter"
.
equals
(
authType
))
{
return
!
TextUtils
.
isEmpty
(
credential
.
getCredentialToken
())
&&
!
TextUtils
.
isEmpty
(
credential
.
getCredentialSecret
());
}
else
if
(
"email"
.
equals
(
authType
))
{
return
!
TextUtils
.
isEmpty
(
credential
.
getUsername
())
&&
!
TextUtils
.
isEmpty
(
credential
.
getHashedPasswd
());
}
return
false
;
}
}
app/src/main/java/chat/rocket/android/service/RocketChatWebSocketThread.java
View file @
03f23709
...
@@ -10,6 +10,7 @@ import chat.rocket.android.helper.LogcatIfError;
...
@@ -10,6 +10,7 @@ import chat.rocket.android.helper.LogcatIfError;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.service.ddp_subscriber.LoginServiceConfigurationSubscriber
;
import
chat.rocket.android.service.ddp_subscriber.LoginServiceConfigurationSubscriber
;
import
chat.rocket.android.service.observer.LoginCredentialObserver
;
import
chat.rocket.android.ws.RocketChatWebSocketAPI
;
import
chat.rocket.android.ws.RocketChatWebSocketAPI
;
import
chat.rocket.android_ddp.DDPClientCallback
;
import
chat.rocket.android_ddp.DDPClientCallback
;
import
hugo.weaving.DebugLog
;
import
hugo.weaving.DebugLog
;
...
@@ -26,7 +27,8 @@ import timber.log.Timber;
...
@@ -26,7 +27,8 @@ import timber.log.Timber;
*/
*/
public
class
RocketChatWebSocketThread
extends
HandlerThread
{
public
class
RocketChatWebSocketThread
extends
HandlerThread
{
private
static
final
Class
[]
REGISTERABLE_CLASSES
=
{
private
static
final
Class
[]
REGISTERABLE_CLASSES
=
{
LoginServiceConfigurationSubscriber
.
class
LoginServiceConfigurationSubscriber
.
class
,
LoginCredentialObserver
.
class
};
};
private
final
Context
appContext
;
private
final
Context
appContext
;
private
final
String
serverConfigId
;
private
final
String
serverConfigId
;
...
@@ -163,6 +165,14 @@ public class RocketChatWebSocketThread extends HandlerThread {
...
@@ -163,6 +165,14 @@ public class RocketChatWebSocketThread extends HandlerThread {
//@DebugLog
//@DebugLog
private
void
registerListenersActually
()
{
private
void
registerListenersActually
()
{
if
(!
Thread
.
currentThread
().
getName
().
equals
(
"RC_thread_"
+
serverConfigId
))
{
// execute in Looper.
new
Handler
(
getLooper
()).
post
(()
->
{
registerListenersActually
();
});
return
;
}
if
(
listenersRegistered
)
{
if
(
listenersRegistered
)
{
return
;
return
;
}
}
...
...
app/src/main/java/chat/rocket/android/service/observer/LoginCredentialObserver.java
0 → 100644
View file @
03f23709
package
chat
.
rocket
.
android
.
service
.
observer
;
import
android.content.Context
;
import
bolts.Task
;
import
chat.rocket.android.helper.LogcatIfError
;
import
chat.rocket.android.helper.TextUtils
;
import
chat.rocket.android.model.ServerConfig
;
import
chat.rocket.android.ws.RocketChatWebSocketAPI
;
import
chat.rocket.android_ddp.DDPClientCallback
;
import
io.realm.Realm
;
import
io.realm.RealmResults
;
import
java.util.List
;
import
jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts
;
import
org.json.JSONObject
;
public
class
LoginCredentialObserver
extends
AbstractModelObserver
<
ServerConfig
>
{
public
LoginCredentialObserver
(
Context
context
,
String
serverConfigId
,
RocketChatWebSocketAPI
api
)
{
super
(
context
,
serverConfigId
,
api
);
}
@Override
protected
RealmResults
<
ServerConfig
>
queryItems
(
Realm
realm
)
{
return
realm
.
where
(
ServerConfig
.
class
)
.
equalTo
(
"tokenVerified"
,
false
)
.
beginGroup
()
.
equalTo
(
"credential.type"
,
"email"
)
.
isNotNull
(
"credential.username"
)
.
isNotNull
(
"credential.hashedPasswd"
)
.
or
()
.
notEqualTo
(
"credential.type"
,
"email"
)
.
isNotNull
(
"credential.credentialToken"
)
.
isNotNull
(
"credential.credentialSecret"
)
.
endGroup
()
.
findAll
();
}
@Override
protected
void
onCollectionChanged
(
List
<
ServerConfig
>
list
)
{
if
(
list
.
isEmpty
())
{
return
;
}
ServerConfig
config
=
list
.
get
(
0
);
final
String
serverConfigId
=
config
.
getId
();
login
(
config
).
onSuccessTask
(
task
->
{
final
String
token
=
task
.
getResult
().
result
.
getString
(
"token"
);
return
RealmHelperBolts
.
executeTransaction
(
realm
->
realm
.
createOrUpdateObjectFromJson
(
ServerConfig
.
class
,
new
JSONObject
()
.
put
(
"id"
,
serverConfigId
)
.
put
(
"token"
,
token
)
.
put
(
"tokenVerified"
,
true
)));
}).
continueWith
(
task
->
{
if
(
task
.
isFaulted
())
{
RealmHelperBolts
.
executeTransaction
(
realm
->
{
ServerConfig
_config
=
realm
.
where
(
ServerConfig
.
class
)
.
equalTo
(
"id"
,
serverConfigId
)
.
findFirst
();
if
(
_config
!=
null
)
{
_config
.
getCredential
().
deleteFromRealm
();
_config
.
setToken
(
null
);
}
return
null
;
}).
continueWith
(
new
LogcatIfError
());
}
return
null
;
});
}
private
Task
<
DDPClientCallback
.
RPC
>
login
(
ServerConfig
config
)
{
if
(!
TextUtils
.
isEmpty
(
config
.
getToken
()))
{
return
webSocketAPI
.
loginWithToken
(
config
.
getToken
());
}
return
webSocketAPI
.
login
(
config
.
getCredential
());
}
}
app/src/main/java/chat/rocket/android/ws/RocketChatWebSocketAPI.java
View file @
03f23709
package
chat
.
rocket
.
android
.
ws
;
package
chat
.
rocket
.
android
.
ws
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.util.Patterns
;
import
bolts.Task
;
import
bolts.Task
;
import
chat.rocket.android.helper.OkHttpHelper
;
import
chat.rocket.android.helper.OkHttpHelper
;
import
chat.rocket.android.model.ServerConfigCredential
;
import
chat.rocket.android_ddp.DDPClient
;
import
chat.rocket.android_ddp.DDPClient
;
import
chat.rocket.android_ddp.DDPClientCallback
;
import
chat.rocket.android_ddp.DDPClientCallback
;
import
chat.rocket.android_ddp.DDPSubscription
;
import
chat.rocket.android_ddp.DDPSubscription
;
import
java.util.UUID
;
import
java.util.UUID
;
import
org.json.JSONArray
;
import
org.json.JSONArray
;
import
org.json.JSONException
;
import
org.json.JSONObject
;
import
rx.Observable
;
import
rx.Observable
;
/**
/**
...
@@ -70,4 +74,55 @@ public class RocketChatWebSocketAPI {
...
@@ -70,4 +74,55 @@ public class RocketChatWebSocketAPI {
public
Observable
<
DDPSubscription
.
Event
>
getSubscriptionCallback
()
{
public
Observable
<
DDPSubscription
.
Event
>
getSubscriptionCallback
()
{
return
ddpClient
.
getSubscriptionCallback
();
return
ddpClient
.
getSubscriptionCallback
();
}
}
private
String
generateId
(
String
method
)
{
return
method
+
"-"
+
UUID
.
randomUUID
().
toString
().
replace
(
"-"
,
""
);
}
/**
* Login with ServerConfigCredential.
*/
public
Task
<
DDPClientCallback
.
RPC
>
login
(
ServerConfigCredential
credential
)
{
JSONObject
param
=
new
JSONObject
();
try
{
String
authType
=
credential
.
getType
();
if
(
"email"
.
equals
(
authType
))
{
String
username
=
credential
.
getUsername
();
if
(
Patterns
.
EMAIL_ADDRESS
.
matcher
(
username
).
matches
())
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"email"
,
username
));
}
else
{
param
.
put
(
"user"
,
new
JSONObject
().
put
(
"username"
,
username
));
}
param
.
put
(
"password"
,
new
JSONObject
()
.
put
(
"digest"
,
credential
.
getHashedPasswd
())
.
put
(
"algorithm"
,
"sha-256"
));
}
else
if
(
"github"
.
equals
(
authType
)
||
"twitter"
.
equals
(
authType
))
{
param
.
put
(
"oauth"
,
new
JSONObject
()
.
put
(
"credentialToken"
,
credential
.
getCredentialToken
())
.
put
(
"credentialSecret"
,
credential
.
getCredentialSecret
()));
}
}
catch
(
JSONException
e
)
{
return
Task
.
forError
(
e
);
}
return
ddpClient
.
rpc
(
"login"
,
new
JSONArray
().
put
(
param
),
generateId
(
"login"
));
}
public
Task
<
DDPClientCallback
.
RPC
>
loginWithToken
(
final
String
token
)
{
JSONObject
param
=
new
JSONObject
();
try
{
param
.
put
(
"resume"
,
token
);
}
catch
(
JSONException
e
)
{
return
Task
.
forError
(
e
);
}
return
ddpClient
.
rpc
(
"login"
,
new
JSONArray
().
put
(
param
),
generateId
(
"login-token"
));
}
public
Task
<
DDPClientCallback
.
RPC
>
logout
()
{
return
ddpClient
.
rpc
(
"logout"
,
null
,
generateId
(
"logout"
));
}
}
}
app/src/main/res/layout/fragment_login.xml
View file @
03f23709
...
@@ -35,6 +35,7 @@
...
@@ -35,6 +35,7 @@
android:text=
"@string/fa_twitter"
android:text=
"@string/fa_twitter"
android:textSize=
"16dp"
android:textSize=
"16dp"
android:layout_marginEnd=
"@dimen/margin_8"
android:layout_marginEnd=
"@dimen/margin_8"
android:enabled=
"false"
/>
/>
<chat.rocket.android.view.FontAwesomeButton
<chat.rocket.android.view.FontAwesomeButton
android:id=
"@+id/btn_login_with_github"
android:id=
"@+id/btn_login_with_github"
...
@@ -94,7 +95,7 @@
...
@@ -94,7 +95,7 @@
/>
/>
<android.support.design.widget.FloatingActionButton
<android.support.design.widget.FloatingActionButton
android:id=
"@+id/btn_login"
android:id=
"@+id/btn_login
_with_email
"
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"end|bottom"
android:layout_gravity=
"end|bottom"
...
...
app/src/main/res/layout/fragment_wait_for_connection.xml
View file @
03f23709
...
@@ -3,11 +3,27 @@
...
@@ -3,11 +3,27 @@
android:layout_width=
"match_parent"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_height=
"match_parent"
android:background=
"?attr/colorPrimaryDark"
android:background=
"?attr/colorPrimaryDark"
android:theme=
"@style/AppTheme.Dark"
>
>
<
chat.rocket.android.view.WaitingView
<
LinearLayout
android:layout_width=
"wrap_content"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_height=
"wrap_content"
android:orientation=
"vertical"
android:layout_gravity=
"center"
android:layout_gravity=
"center"
android:gravity=
"center"
>
<chat.rocket.android.view.WaitingView
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
/>
/>
<TextView
android:id=
"@+id/txt_caption"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:textAppearance=
"@style/TextAppearance.AppCompat.Caption"
android:layout_marginTop=
"@dimen/margin_32"
android:text=
"Connecting..."
/>
</LinearLayout>
</FrameLayout>
</FrameLayout>
\ No newline at end of file
app/src/main/res/layout/webview.xml
0 → 100644
View file @
03f23709
<?xml version="1.0" encoding="utf-8"?>
<WebView
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:id=
"@+id/webview"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
/>
\ No newline at end of file
app/src/main/res/values/margin_dimens.xml
View file @
03f23709
...
@@ -3,4 +3,5 @@
...
@@ -3,4 +3,5 @@
<dimen
name=
"margin_8"
>
8dp
</dimen>
<dimen
name=
"margin_8"
>
8dp
</dimen>
<dimen
name=
"margin_16"
>
16dp
</dimen>
<dimen
name=
"margin_16"
>
16dp
</dimen>
<dimen
name=
"margin_24"
>
24dp
</dimen>
<dimen
name=
"margin_24"
>
24dp
</dimen>
<dimen
name=
"margin_32"
>
32dp
</dimen>
</resources>
</resources>
\ 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