2016-12-12 06:42:27 +01:00
|
|
|
/*
|
|
|
|
* Twidere - Twitter client for Android
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.mariotaku.twidere.activity
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
import android.app.Activity
|
|
|
|
import android.content.Intent
|
|
|
|
import android.graphics.Bitmap
|
|
|
|
import android.net.Uri
|
|
|
|
import android.os.AsyncTask
|
|
|
|
import android.os.Bundle
|
|
|
|
import android.text.TextUtils.isEmpty
|
|
|
|
import android.util.Log
|
|
|
|
import android.view.MenuItem
|
|
|
|
import android.view.View
|
|
|
|
import android.webkit.CookieManager
|
|
|
|
import android.webkit.JavascriptInterface
|
2017-03-26 15:41:28 +02:00
|
|
|
import android.webkit.WebChromeClient
|
2016-12-12 06:42:27 +01:00
|
|
|
import android.webkit.WebView
|
|
|
|
import android.widget.Toast
|
|
|
|
import kotlinx.android.synthetic.main.activity_browser_sign_in.*
|
|
|
|
import org.attoparser.ParseException
|
2016-12-18 03:32:11 +01:00
|
|
|
import org.mariotaku.ktextension.removeAllCookiesSupport
|
2016-12-12 06:42:27 +01:00
|
|
|
import org.mariotaku.microblog.library.MicroBlogException
|
|
|
|
import org.mariotaku.microblog.library.twitter.TwitterOAuth
|
|
|
|
import org.mariotaku.restfu.oauth.OAuthAuthorization
|
|
|
|
import org.mariotaku.restfu.oauth.OAuthToken
|
|
|
|
import org.mariotaku.twidere.R
|
|
|
|
import org.mariotaku.twidere.TwidereConstants
|
|
|
|
import org.mariotaku.twidere.TwidereConstants.LOGTAG
|
|
|
|
import org.mariotaku.twidere.constant.IntentConstants.*
|
2016-12-29 06:50:18 +01:00
|
|
|
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
|
2016-12-12 06:42:27 +01:00
|
|
|
import org.mariotaku.twidere.model.CustomAPIConfig
|
|
|
|
import org.mariotaku.twidere.model.SingleResponse
|
|
|
|
import org.mariotaku.twidere.util.AsyncTaskUtils
|
2017-01-26 16:15:05 +01:00
|
|
|
import org.mariotaku.twidere.util.DebugLog
|
2016-12-12 06:42:27 +01:00
|
|
|
import org.mariotaku.twidere.util.MicroBlogAPIFactory
|
|
|
|
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator
|
|
|
|
import org.mariotaku.twidere.util.webkit.DefaultWebViewClient
|
|
|
|
import java.io.IOException
|
|
|
|
import java.io.StringReader
|
2016-12-16 07:16:41 +01:00
|
|
|
import java.lang.ref.WeakReference
|
2016-12-12 06:42:27 +01:00
|
|
|
|
|
|
|
class BrowserSignInActivity : BaseActivity() {
|
|
|
|
|
|
|
|
private var requestToken: OAuthToken? = null
|
|
|
|
private var task: GetRequestTokenTask? = null
|
|
|
|
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
|
|
when (item.itemId) {
|
|
|
|
android.R.id.home -> {
|
|
|
|
finish()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return super.onOptionsItemSelected(item)
|
|
|
|
}
|
|
|
|
|
2017-03-23 15:22:07 +01:00
|
|
|
@SuppressLint("AddJavascriptInterface", "SetJavaScriptEnabled")
|
2016-12-12 06:42:27 +01:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
|
|
|
setContentView(R.layout.activity_browser_sign_in)
|
2016-12-18 03:32:11 +01:00
|
|
|
CookieManager.getInstance().removeAllCookiesSupport()
|
2017-03-26 15:41:28 +02:00
|
|
|
webView.setWebChromeClient(AuthorizationWebChromeClient(this))
|
2016-12-12 06:42:27 +01:00
|
|
|
webView.setWebViewClient(AuthorizationWebViewClient(this))
|
|
|
|
webView.isVerticalScrollBarEnabled = false
|
|
|
|
webView.addJavascriptInterface(InjectorJavaScriptInterface(this), "injector")
|
|
|
|
val webSettings = webView.settings
|
|
|
|
webSettings.loadsImagesAutomatically = true
|
|
|
|
webSettings.javaScriptEnabled = true
|
|
|
|
webSettings.blockNetworkImage = false
|
|
|
|
webSettings.saveFormData = true
|
|
|
|
getRequestToken()
|
|
|
|
}
|
|
|
|
|
2017-01-30 14:41:34 +01:00
|
|
|
override fun onDestroy() {
|
2016-12-16 07:24:44 +01:00
|
|
|
if (task?.status == AsyncTask.Status.RUNNING) {
|
|
|
|
task?.cancel(true)
|
|
|
|
}
|
2017-03-23 15:22:07 +01:00
|
|
|
webView?.destroy()
|
2016-12-16 07:24:44 +01:00
|
|
|
super.onDestroy()
|
|
|
|
}
|
|
|
|
|
2017-03-23 15:22:07 +01:00
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
webView.onResume()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPause() {
|
|
|
|
webView.onPause()
|
|
|
|
super.onPause()
|
|
|
|
}
|
|
|
|
|
2016-12-12 06:42:27 +01:00
|
|
|
private fun getRequestToken() {
|
2016-12-16 07:24:44 +01:00
|
|
|
if (requestToken != null || task?.status == AsyncTask.Status.RUNNING) return
|
2016-12-12 06:42:27 +01:00
|
|
|
task = GetRequestTokenTask(this)
|
2016-12-16 07:24:44 +01:00
|
|
|
AsyncTaskUtils.executeTask(task)
|
2016-12-12 06:42:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private fun loadUrl(url: String) {
|
|
|
|
webView.loadUrl(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun readOAuthPin(html: String): String? {
|
|
|
|
try {
|
|
|
|
val data = OAuthPasswordAuthenticator.OAuthPinData()
|
|
|
|
OAuthPasswordAuthenticator.readOAuthPINFromHtml(StringReader(html), data)
|
|
|
|
return data.oauthPin
|
|
|
|
} catch (e: ParseException) {
|
|
|
|
Log.w(LOGTAG, e)
|
|
|
|
} catch (e: IOException) {
|
|
|
|
Log.w(LOGTAG, e)
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun setLoadProgressShown(shown: Boolean) {
|
|
|
|
progressContainer.visibility = if (shown) View.VISIBLE else View.GONE
|
|
|
|
}
|
|
|
|
|
2017-03-26 15:41:28 +02:00
|
|
|
private fun setLoadProgress(progress: Int) {
|
|
|
|
loadProgress.progress = progress
|
|
|
|
}
|
|
|
|
|
2016-12-12 06:42:27 +01:00
|
|
|
private fun setRequestToken(token: OAuthToken) {
|
|
|
|
requestToken = token
|
|
|
|
}
|
|
|
|
|
2017-03-26 15:41:28 +02:00
|
|
|
internal class AuthorizationWebChromeClient(val activity: BrowserSignInActivity) : WebChromeClient() {
|
|
|
|
override fun onProgressChanged(view: WebView, newProgress: Int) {
|
|
|
|
super.onProgressChanged(view, newProgress)
|
|
|
|
activity.setLoadProgress(newProgress)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class AuthorizationWebViewClient(activity: BrowserSignInActivity) : DefaultWebViewClient<BrowserSignInActivity>(activity) {
|
|
|
|
|
|
|
|
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
|
|
|
super.onPageStarted(view, url, favicon)
|
|
|
|
activity.setLoadProgressShown(true)
|
|
|
|
}
|
2016-12-12 06:42:27 +01:00
|
|
|
|
|
|
|
override fun onPageFinished(view: WebView, url: String) {
|
|
|
|
super.onPageFinished(view, url)
|
|
|
|
view.loadUrl(INJECT_CONTENT)
|
|
|
|
activity.setLoadProgressShown(false)
|
|
|
|
val uri = Uri.parse(url)
|
|
|
|
// Hack for fanfou
|
|
|
|
if ("fanfou.com" == uri.host) {
|
|
|
|
val path = uri.path
|
|
|
|
val paramNames = uri.queryParameterNames
|
|
|
|
if ("/oauth/authorize" == path && paramNames.contains("oauth_callback")) {
|
|
|
|
// Sign in successful response.
|
|
|
|
val requestToken = activity.requestToken
|
|
|
|
if (requestToken != null) {
|
|
|
|
val intent = Intent()
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.oauthToken)
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.oauthTokenSecret)
|
|
|
|
activity.setResult(Activity.RESULT_OK, intent)
|
|
|
|
activity.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-04 08:39:08 +01:00
|
|
|
@Suppress("Deprecation")
|
2016-12-12 06:42:27 +01:00
|
|
|
override fun onReceivedError(view: WebView, errorCode: Int, description: String?,
|
2017-03-23 15:22:07 +01:00
|
|
|
failingUrl: String?) {
|
2016-12-12 06:42:27 +01:00
|
|
|
super.onReceivedError(view, errorCode, description, failingUrl)
|
|
|
|
val activity = activity
|
|
|
|
Toast.makeText(activity, description, Toast.LENGTH_SHORT).show()
|
|
|
|
activity.finish()
|
|
|
|
}
|
|
|
|
|
2017-02-04 08:39:08 +01:00
|
|
|
@Suppress("Deprecation")
|
2016-12-12 06:42:27 +01:00
|
|
|
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
|
|
|
|
val uri = Uri.parse(url)
|
|
|
|
if (url.startsWith(TwidereConstants.OAUTH_CALLBACK_URL)) {
|
|
|
|
val oauthVerifier = uri.getQueryParameter(EXTRA_OAUTH_VERIFIER)
|
2017-03-26 15:41:28 +02:00
|
|
|
val activity = activity
|
2016-12-12 06:42:27 +01:00
|
|
|
val requestToken = activity.requestToken
|
|
|
|
if (oauthVerifier != null && requestToken != null) {
|
|
|
|
val intent = Intent()
|
|
|
|
intent.putExtra(EXTRA_OAUTH_VERIFIER, oauthVerifier)
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.oauthToken)
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.oauthTokenSecret)
|
|
|
|
activity.setResult(Activity.RESULT_OK, intent)
|
|
|
|
activity.finish()
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-12-16 07:16:41 +01:00
|
|
|
internal class GetRequestTokenTask(activity: BrowserSignInActivity) : AsyncTask<Any, Any, SingleResponse<OAuthToken>>() {
|
2017-01-26 14:28:43 +01:00
|
|
|
private val activityRef: WeakReference<BrowserSignInActivity> = WeakReference(activity)
|
|
|
|
private val apiConfig: CustomAPIConfig = activity.intent.getParcelableExtra(EXTRA_API_CONFIG)
|
2016-12-12 06:42:27 +01:00
|
|
|
|
|
|
|
override fun doInBackground(vararg params: Any): SingleResponse<OAuthToken> {
|
2016-12-16 07:16:41 +01:00
|
|
|
val activity = activityRef.get() ?: return SingleResponse(exception = InterruptedException())
|
2016-12-12 06:42:27 +01:00
|
|
|
if (isEmpty(apiConfig.consumerKey) || isEmpty(apiConfig.consumerSecret)) {
|
|
|
|
return SingleResponse()
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
val endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiConfig.apiUrlFormat,
|
|
|
|
apiConfig.isSameOAuthUrl)
|
|
|
|
val auth = OAuthAuthorization(apiConfig.consumerKey, apiConfig.consumerSecret)
|
2017-03-11 14:15:29 +01:00
|
|
|
val oauth = newMicroBlogInstance(activity, endpoint, auth, apiConfig.type, TwitterOAuth::class.java)
|
2016-12-16 07:16:41 +01:00
|
|
|
return SingleResponse(oauth.getRequestToken(TwidereConstants.OAUTH_CALLBACK_OOB))
|
2016-12-12 06:42:27 +01:00
|
|
|
} catch (e: MicroBlogException) {
|
2016-12-16 07:16:41 +01:00
|
|
|
return SingleResponse(exception = e)
|
2016-12-12 06:42:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPostExecute(result: SingleResponse<OAuthToken>) {
|
2016-12-16 07:16:41 +01:00
|
|
|
val activity = activityRef.get() ?: return
|
2016-12-12 06:42:27 +01:00
|
|
|
activity.setLoadProgressShown(false)
|
|
|
|
if (result.hasData()) {
|
|
|
|
val token = result.data!!
|
|
|
|
activity.setRequestToken(token)
|
|
|
|
val endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiConfig.apiUrlFormat, true)
|
|
|
|
activity.loadUrl(endpoint.construct("/oauth/authorize", arrayOf("oauth_token", token.oauthToken)))
|
|
|
|
} else {
|
2017-01-26 16:15:05 +01:00
|
|
|
DebugLog.w(LOGTAG, "Error while browser sign in", result.exception)
|
2016-12-12 06:42:27 +01:00
|
|
|
if (!activity.isFinishing) {
|
2017-01-26 14:28:43 +01:00
|
|
|
Toast.makeText(activity, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
|
2016-12-12 06:42:27 +01:00
|
|
|
activity.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPreExecute() {
|
2016-12-16 07:16:41 +01:00
|
|
|
val activity = activityRef.get() ?: return
|
2016-12-12 06:42:27 +01:00
|
|
|
activity.setLoadProgressShown(true)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
internal class InjectorJavaScriptInterface(private val activity: BrowserSignInActivity) {
|
|
|
|
|
|
|
|
@JavascriptInterface
|
|
|
|
fun processHTML(html: String) {
|
|
|
|
val oauthVerifier = activity.readOAuthPin(html)
|
|
|
|
val requestToken = activity.requestToken
|
|
|
|
if (oauthVerifier != null && requestToken != null) {
|
|
|
|
val intent = Intent()
|
|
|
|
intent.putExtra(EXTRA_OAUTH_VERIFIER, oauthVerifier)
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.oauthToken)
|
|
|
|
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.oauthTokenSecret)
|
|
|
|
activity.setResult(Activity.RESULT_OK, intent)
|
|
|
|
activity.finish()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
|
|
|
private val INJECT_CONTENT = "javascript:window.injector.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');"
|
|
|
|
}
|
|
|
|
}
|