Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/activity/SignInActivity.kt

1100 lines
47 KiB
Kotlin
Raw Normal View History

2016-07-07 09:39:32 +02: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
2016-12-03 06:48:40 +01:00
import android.accounts.Account
2016-12-02 05:36:50 +01:00
import android.accounts.AccountAuthenticatorResponse
import android.accounts.AccountManager
2016-07-07 09:39:32 +02:00
import android.app.Activity
import android.app.Dialog
2017-01-19 12:43:20 +01:00
import android.content.ActivityNotFoundException
2016-07-07 09:39:32 +02:00
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.ColorStateList
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
2017-01-19 12:43:20 +01:00
import android.support.customtabs.CustomTabsIntent
2016-07-07 09:39:32 +02:00
import android.support.v4.app.DialogFragment
import android.support.v4.content.ContextCompat
import android.support.v4.util.ArraySet
2016-07-07 09:39:32 +02:00
import android.support.v4.view.ViewCompat
import android.support.v7.app.AlertDialog
import android.text.Editable
import android.text.InputType
import android.text.TextUtils
import android.text.TextWatcher
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.View.OnClickListener
import android.widget.EditText
import android.widget.TextView
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_sign_in.*
2017-03-22 13:00:29 +01:00
import nl.komponents.kovenant.combine.and
import nl.komponents.kovenant.task
import nl.komponents.kovenant.ui.alwaysUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.kpreferences.get
2017-01-02 09:33:27 +01:00
import org.mariotaku.ktextension.*
2016-07-07 09:39:32 +02:00
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.TwitterOAuth
import org.mariotaku.microblog.library.twitter.auth.BasicAuthorization
import org.mariotaku.microblog.library.twitter.auth.EmptyAuthorization
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.User
import org.mariotaku.restfu.http.Endpoint
import org.mariotaku.restfu.oauth.OAuthAuthorization
import org.mariotaku.restfu.oauth.OAuthToken
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
2016-12-03 06:48:40 +01:00
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_API_CONFIG
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CREDENTIALS_TYPE
2017-01-19 12:43:20 +01:00
import org.mariotaku.twidere.constant.chromeCustomTabKey
import org.mariotaku.twidere.constant.defaultAPIConfigKey
import org.mariotaku.twidere.constant.randomizeAccountNameKey
2017-02-05 14:42:20 +01:00
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.extension.getNonEmptyString
2016-12-29 06:50:18 +01:00
import org.mariotaku.twidere.extension.model.getColor
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
2016-12-15 06:11:32 +01:00
import org.mariotaku.twidere.extension.model.official
2017-01-25 13:08:23 +01:00
import org.mariotaku.twidere.fragment.APIEditorDialogFragment
2016-07-07 09:39:32 +02:00
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.model.CustomAPIConfig
2016-12-03 06:48:40 +01:00
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
2016-12-15 06:11:32 +01:00
import org.mariotaku.twidere.model.account.AccountExtras
2016-12-03 06:48:40 +01:00
import org.mariotaku.twidere.model.account.StatusNetAccountExtras
import org.mariotaku.twidere.model.account.TwitterAccountExtras
import org.mariotaku.twidere.model.account.cred.BasicCredentials
import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.model.account.cred.EmptyCredentials
import org.mariotaku.twidere.model.account.cred.OAuthCredentials
2016-12-15 06:11:32 +01:00
import org.mariotaku.twidere.model.analyzer.SignIn
2016-12-04 04:58:03 +01:00
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableUserUtils
import org.mariotaku.twidere.model.util.UserKeyUtils
2016-07-07 09:39:32 +02:00
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator.*
2017-01-07 17:13:11 +01:00
import java.io.IOException
2016-07-07 09:39:32 +02:00
import java.lang.ref.WeakReference
import java.util.*
2016-07-07 09:39:32 +02:00
2016-12-02 05:36:50 +01:00
2017-04-06 05:55:04 +02:00
class SignInActivity : BaseActivity(), OnClickListener, TextWatcher,
APIEditorDialogFragment.APIEditorCallback {
private lateinit var apiConfig: CustomAPIConfig
2016-07-07 09:39:32 +02:00
private var apiChangeTimestamp: Long = 0
private var signInTask: AbstractSignInTask? = null
2016-12-02 05:36:50 +01:00
private var accountAuthenticatorResponse: AccountAuthenticatorResponse? = null
private var accountAuthenticatorResult: Bundle? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
accountAuthenticatorResponse = intent.getParcelableExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE)
accountAuthenticatorResponse?.onRequestContinued()
setContentView(R.layout.activity_sign_in)
if (savedInstanceState != null) {
apiConfig = savedInstanceState.getParcelable(EXTRA_API_CONFIG)
2016-12-02 05:36:50 +01:00
apiChangeTimestamp = savedInstanceState.getLong(EXTRA_API_LAST_CHANGE)
} else {
apiConfig = kPreferences[defaultAPIConfigKey]
2016-12-02 05:36:50 +01:00
}
val isTwipOMode = apiConfig.credentialsType == Credentials.Type.EMPTY
2016-12-02 05:36:50 +01:00
usernamePasswordContainer.visibility = if (isTwipOMode) View.GONE else View.VISIBLE
editUsername.addTextChangedListener(this)
editPassword.addTextChangedListener(this)
2016-07-07 09:39:32 +02:00
2016-12-02 05:36:50 +01:00
signIn.setOnClickListener(this)
signUp.setOnClickListener(this)
passwordSignIn.setOnClickListener(this)
val color = ColorStateList.valueOf(ContextCompat.getColor(this,
R.color.material_light_green))
ViewCompat.setBackgroundTintList(signIn, color)
updateSignInType()
setSignInButton()
if (savedInstanceState == null) {
// Only start at the first time
updateDefaultFeatures()
}
2016-07-07 09:39:32 +02:00
}
2016-12-02 05:36:50 +01:00
override fun onDestroy() {
loaderManager.destroyLoader(0)
super.onDestroy()
}
2016-07-07 09:39:32 +02:00
2016-12-02 05:36:50 +01:00
override fun onCreateOptionsMenu(menu: Menu): Boolean {
2016-12-29 06:50:18 +01:00
super.onCreateOptionsMenu(menu)
2016-12-02 05:36:50 +01:00
menuInflater.inflate(R.menu.menu_sign_in, menu)
return true
2016-07-07 09:39:32 +02:00
}
2016-12-02 05:36:50 +01:00
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
2016-07-07 09:39:32 +02:00
when (requestCode) {
REQUEST_EDIT_API -> {
if (resultCode == Activity.RESULT_OK) {
apiConfig = data!!.getParcelableExtra(EXTRA_API_CONFIG)
2016-07-07 09:39:32 +02:00
updateSignInType()
}
setSignInButton()
invalidateOptionsMenu()
}
REQUEST_BROWSER_SIGN_IN -> {
if (resultCode == Activity.RESULT_OK && data != null) {
2017-01-27 15:35:28 +01:00
handleBrowserLoginResult(data)
2016-07-07 09:39:32 +02:00
}
}
}
super.onActivityResult(requestCode, resultCode, data)
}
2016-12-02 05:36:50 +01:00
override fun finish() {
accountAuthenticatorResponse?.let { response ->
// send the result bundle back if set, otherwise send an error.
if (accountAuthenticatorResult != null) {
response.onResult(accountAuthenticatorResult)
} else {
response.onError(AccountManager.ERROR_CODE_CANCELED, "canceled")
}
accountAuthenticatorResponse = null
}
super.finish()
}
override fun afterTextChanged(s: Editable) {
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
2016-07-07 09:39:32 +02:00
override fun onClick(v: View) {
when (v) {
signUp -> {
2017-01-19 12:43:20 +01:00
val signUpUrl = apiConfig.signUpUrl ?: return
val uri = Uri.parse(signUpUrl)
if (preferences[chromeCustomTabKey]) {
val builder = CustomTabsIntent.Builder()
builder.setToolbarColor(overrideTheme.colorToolbar)
val intent = builder.build()
try {
intent.launchUrl(this, uri)
} catch (e: ActivityNotFoundException) {
// Ignore
}
} else {
val intent = Intent(Intent.ACTION_VIEW, uri)
try {
startActivity(intent)
} catch (e: ActivityNotFoundException) {
// Ignore
}
}
2016-07-07 09:39:32 +02:00
}
signIn -> {
if (usernamePasswordContainer.visibility != View.VISIBLE) {
editUsername.text = null
editPassword.text = null
}
2017-01-27 15:35:28 +01:00
if (apiConfig.credentialsType == Credentials.Type.OAUTH) {
doBrowserLogin()
} else {
doLogin()
}
2016-07-07 09:39:32 +02:00
}
passwordSignIn -> {
2017-01-19 12:43:20 +01:00
executeAfterFragmentResumed { fragment ->
2016-07-07 09:39:32 +02:00
val df = PasswordSignInDialogFragment()
2017-01-19 12:43:20 +01:00
df.show(fragment.supportFragmentManager, "password_sign_in")
2016-07-07 09:39:32 +02:00
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> {
val accountKeys = DataStoreUtils.getActivatedAccountKeys(this)
2016-12-03 06:48:40 +01:00
if (accountKeys.isNotEmpty()) {
2016-07-07 09:39:32 +02:00
onBackPressed()
}
}
R.id.settings -> {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
return false
2017-03-13 06:35:11 +01:00
startActivity(IntentUtils.settings())
2016-07-07 09:39:32 +02:00
}
R.id.edit_api -> {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
return false
setDefaultAPI()
2017-01-25 13:08:23 +01:00
val df = APIEditorDialogFragment()
df.arguments = Bundle {
this[EXTRA_API_CONFIG] = apiConfig
this[APIEditorDialogFragment.EXTRA_SHOW_LOAD_DEFAULTS] = true
}
df.show(supportFragmentManager, "edit_api_config")
2016-07-07 09:39:32 +02:00
}
}
return super.onOptionsItemSelected(item)
}
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
super.onPrepareOptionsMenu(menu)
2016-07-07 09:39:32 +02:00
val itemBrowser = menu.findItem(R.id.open_in_browser)
if (itemBrowser != null) {
val is_oauth = apiConfig.credentialsType == Credentials.Type.OAUTH
2016-07-07 09:39:32 +02:00
itemBrowser.isVisible = is_oauth
itemBrowser.isEnabled = is_oauth
}
return true
}
2017-01-25 13:08:23 +01:00
override fun onSaveInstanceState(outState: Bundle) {
outState.putParcelable(EXTRA_API_CONFIG, apiConfig)
2016-07-07 09:39:32 +02:00
outState.putLong(EXTRA_API_LAST_CHANGE, apiChangeTimestamp)
super.onSaveInstanceState(outState)
}
2016-12-02 05:36:50 +01:00
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
2016-07-07 09:39:32 +02:00
setSignInButton()
}
2017-01-25 13:08:23 +01:00
override fun onSaveAPIConfig(config: CustomAPIConfig) {
apiConfig = config
updateSignInType()
setSignInButton()
invalidateOptionsMenu()
}
internal fun updateSignInType() {
when (apiConfig.credentialsType) {
Credentials.Type.XAUTH, Credentials.Type.BASIC -> {
usernamePasswordContainer.visibility = View.VISIBLE
}
Credentials.Type.EMPTY -> {
usernamePasswordContainer.visibility = View.GONE
}
else -> {
usernamePasswordContainer.visibility = View.GONE
}
}
}
2017-01-27 15:35:28 +01:00
internal fun doBrowserLogin(): Boolean {
2017-01-25 13:08:23 +01:00
if (apiConfig.credentialsType != Credentials.Type.OAUTH || signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
return true
val intent = Intent(this, BrowserSignInActivity::class.java)
intent.putExtra(EXTRA_API_CONFIG, apiConfig)
startActivityForResult(intent, REQUEST_BROWSER_SIGN_IN)
return false
}
2016-07-07 09:39:32 +02:00
internal fun doLogin() {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING) {
signInTask!!.cancel(true)
}
2016-07-07 09:39:32 +02:00
val username = editUsername.text.toString()
val password = editPassword.text.toString()
2017-01-07 17:13:11 +01:00
signInTask = SignInTask(this, username, password, apiConfig)
2016-07-07 09:39:32 +02:00
AsyncTaskUtils.executeTask<AbstractSignInTask, Any>(signInTask)
}
2017-01-25 13:08:23 +01:00
internal fun onSignInResult(result: SignInResponse) {
val am = AccountManager.get(this)
setSignInButton()
if (result.alreadyLoggedIn) {
result.updateAccount(am)
contentResolver.deleteAccountData(result.user.key)
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_already_logged_in, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
} else {
result.addAccount(am, preferences[randomizeAccountNameKey])
2017-03-27 15:56:03 +02:00
Analyzer.log(SignIn(true, accountType = result.typeExtras.first, credentialsType = apiConfig.credentialsType,
officialKey = result.typeExtras.second?.official ?: false))
2017-01-25 13:08:23 +01:00
finishSignIn()
}
}
internal fun dismissDialogFragment(tag: String) {
executeAfterFragmentResumed {
val fm = supportFragmentManager
val f = fm.findFragmentByTag(tag)
if (f is DialogFragment) {
f.dismiss()
}
Unit
}
}
2017-01-25 13:08:23 +01:00
internal fun onSignInError(exception: Exception) {
2017-01-26 16:15:05 +01:00
DebugLog.w(LOGTAG, "Sign in error", exception)
2017-01-25 13:08:23 +01:00
var errorReason: String? = null
if (exception is AuthenticityTokenException) {
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_wrong_api_key, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
errorReason = "wrong_api_key"
} else if (exception is WrongUserPassException) {
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_wrong_username_password, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
errorReason = "wrong_username_password"
} else if (exception is SignInTask.WrongBasicCredentialException) {
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_wrong_username_password, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
errorReason = "wrong_username_password"
} else if (exception is SignInTask.WrongAPIURLFormatException) {
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_wrong_api_key, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
errorReason = "wrong_api_key"
} else if (exception is LoginVerificationException) {
2017-01-26 14:28:43 +01:00
Toast.makeText(this, R.string.message_toast_login_verification_failed, Toast.LENGTH_SHORT).show()
2017-01-25 13:08:23 +01:00
errorReason = "login_verification_failed"
} else if (exception is AuthenticationException) {
Utils.showErrorMessage(this, getString(R.string.action_signing_in), exception.cause, true)
} else {
Utils.showErrorMessage(this, getString(R.string.action_signing_in), exception, true)
}
Analyzer.log(SignIn(false, credentialsType = apiConfig.credentialsType,
errorReason = errorReason, accountType = apiConfig.type))
}
internal fun onSignInStart() {
showSignInProgressDialog()
}
internal fun showSignInProgressDialog() {
executeAfterFragmentResumed {
if (isFinishing) return@executeAfterFragmentResumed
val fm = supportFragmentManager
val ft = fm.beginTransaction()
val fragment = ProgressDialogFragment()
fragment.isCancelable = false
fragment.show(ft, FRAGMENT_TAG_SIGN_IN_PROGRESS)
}
}
internal fun setUsernamePassword(username: String, password: String) {
editUsername.setText(username)
editPassword.setText(password)
}
private fun updateDefaultFeatures() {
val weakThis = WeakReference(this)
2017-03-22 13:00:29 +01:00
executeAfterFragmentResumed {
ProgressDialogFragment.show(it.supportFragmentManager, FRAGMENT_TAG_LOADING_DEFAULT_FEATURES)
} and task {
val activity = weakThis.get() ?: return@task
if (activity.isFinishing) return@task
activity.defaultFeatures.loadRemoteSettings(activity.restHttpClient)
}.successUi {
val activity = weakThis.get() ?: return@successUi
if (activity.isFinishing) return@successUi
val apiConfig = activity.apiConfig
val defaultFeatures = activity.defaultFeatures
val preferences = activity.preferences
if (apiConfig.consumerKey == TWITTER_CONSUMER_KEY && apiConfig.consumerSecret == TWITTER_CONSUMER_SECRET) {
apiConfig.consumerKey = defaultFeatures.defaultTwitterConsumerKey ?: TWITTER_CONSUMER_KEY
apiConfig.consumerSecret = defaultFeatures.defaultTwitterConsumerSecret ?: TWITTER_CONSUMER_SECRET
}
defaultFeatures.save(preferences)
}.fail {
DebugLog.w(LOGTAG, "Unable to update default features", it)
}.alwaysUi {
val activity = weakThis.get() ?: return@alwaysUi
if (activity.isFinishing) return@alwaysUi
2017-02-10 18:03:40 +01:00
activity.executeAfterFragmentResumed { activity ->
val fm = activity.supportFragmentManager
val df = fm.findFragmentByTag(FRAGMENT_TAG_LOADING_DEFAULT_FEATURES) as? DialogFragment
2017-02-10 18:03:40 +01:00
df?.dismiss()
}
}
}
2017-01-27 15:35:28 +01:00
private fun handleBrowserLoginResult(intent: Intent?) {
2016-07-07 09:39:32 +02:00
if (intent == null) return
if (signInTask?.status == AsyncTask.Status.RUNNING) {
signInTask?.cancel(true)
2016-07-07 09:39:32 +02:00
}
val verifier = intent.getStringExtra(EXTRA_OAUTH_VERIFIER)
val requestToken = OAuthToken(intent.getStringExtra(EXTRA_REQUEST_TOKEN),
intent.getStringExtra(EXTRA_REQUEST_TOKEN_SECRET))
2017-01-07 17:13:11 +01:00
signInTask = BrowserSignInTask(this, apiConfig, requestToken, verifier)
2016-07-07 09:39:32 +02:00
AsyncTaskUtils.executeTask<AbstractSignInTask, Any>(signInTask)
}
private fun setDefaultAPI() {
val apiLastChange = preferences.getLong(KEY_API_LAST_CHANGE, apiChangeTimestamp)
val defaultApiChanged = apiLastChange != apiChangeTimestamp
val apiUrlFormat = preferences.getNonEmptyString(KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT)
val authType = preferences.getString(KEY_CREDENTIALS_TYPE, Credentials.Type.OAUTH)
2016-07-07 09:39:32 +02:00
val sameOAuthSigningUrl = preferences.getBoolean(KEY_SAME_OAUTH_SIGNING_URL, false)
val noVersionSuffix = preferences.getBoolean(KEY_NO_VERSION_SUFFIX, false)
val consumerKey = preferences.getNonEmptyString(KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY)
val consumerSecret = preferences.getNonEmptyString(KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET)
if (TextUtils.isEmpty(apiConfig.apiUrlFormat) || defaultApiChanged) {
apiConfig.apiUrlFormat = apiUrlFormat
2016-07-07 09:39:32 +02:00
}
if (defaultApiChanged) {
apiConfig.credentialsType = authType
2016-07-07 09:39:32 +02:00
}
if (defaultApiChanged) {
apiConfig.isSameOAuthUrl = sameOAuthSigningUrl
2016-07-07 09:39:32 +02:00
}
if (defaultApiChanged) {
apiConfig.isNoVersionSuffix = noVersionSuffix
2016-07-07 09:39:32 +02:00
}
if (TextUtils.isEmpty(apiConfig.consumerKey) || defaultApiChanged) {
apiConfig.consumerKey = consumerKey
2016-07-07 09:39:32 +02:00
}
if (TextUtils.isEmpty(apiConfig.consumerSecret) || defaultApiChanged) {
apiConfig.consumerSecret = consumerSecret
2016-07-07 09:39:32 +02:00
}
if (defaultApiChanged) {
apiChangeTimestamp = apiLastChange
}
}
private fun setSignInButton() {
when (apiConfig.credentialsType) {
2016-12-03 06:48:40 +01:00
Credentials.Type.XAUTH, Credentials.Type.BASIC -> {
2016-07-07 09:39:32 +02:00
passwordSignIn.visibility = View.GONE
2016-12-03 06:48:40 +01:00
signIn.isEnabled = editPassword.text.isNotEmpty() && editUsername.text.isNotEmpty()
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
Credentials.Type.OAUTH -> {
2016-07-07 09:39:32 +02:00
passwordSignIn.visibility = View.VISIBLE
signIn.isEnabled = true
}
else -> {
passwordSignIn.visibility = View.GONE
signIn.isEnabled = true
}
}
signUp.visibility = if (apiConfig.signUpUrlOrDefault != null) {
2017-01-19 12:43:20 +01:00
View.VISIBLE
} else {
View.GONE
}
passwordSignIn.visibility = if (apiConfig.type == null || apiConfig.type == AccountType.TWITTER) {
View.VISIBLE
} else {
View.GONE
}
2016-07-07 09:39:32 +02:00
}
2016-12-17 05:10:24 +01:00
private fun finishSignIn() {
if (accountAuthenticatorResponse != null) {
accountAuthenticatorResult = Bundle {
this[AccountManager.KEY_BOOLEAN_RESULT] = true
}
} else {
val intent = Intent(this, HomeActivity::class.java)
2017-01-07 17:13:11 +01:00
//TODO refresh timelines
2016-12-17 05:10:24 +01:00
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(intent)
2016-12-03 06:48:40 +01:00
}
2016-12-17 05:10:24 +01:00
finish()
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
internal abstract class AbstractSignInTask(activity: SignInActivity) : AsyncTask<Any, Runnable, SingleResponse<SignInResponse>>() {
2016-07-07 09:39:32 +02:00
2017-03-02 03:42:40 +01:00
protected val activityRef = WeakReference(activity)
protected val profileImageSize = activity.getString(R.string.profile_image_size)
2016-07-07 09:39:32 +02:00
2016-12-03 06:48:40 +01:00
override final fun doInBackground(vararg args: Any?): SingleResponse<SignInResponse> {
try {
return SingleResponse.getInstance(performLogin())
} catch (e: Exception) {
return SingleResponse.getInstance(e)
}
}
abstract fun performLogin(): SignInResponse
override fun onPostExecute(result: SingleResponse<SignInResponse>) {
2016-07-07 09:39:32 +02:00
val activity = activityRef.get()
2016-12-15 06:49:18 +01:00
activity?.dismissDialogFragment(FRAGMENT_TAG_SIGN_IN_PROGRESS)
2016-12-03 06:48:40 +01:00
if (result.hasData()) {
activity?.onSignInResult(result.data!!)
} else {
activity?.onSignInError(result.exception!!)
}
2016-07-07 09:39:32 +02:00
}
override fun onPreExecute() {
val activity = activityRef.get()
activity?.onSignInStart()
}
override fun onProgressUpdate(vararg values: Runnable) {
for (value in values) {
value.run()
}
}
@Throws(MicroBlogException::class)
internal fun analyseUserProfileColor(user: User?): Int {
if (user == null) throw MicroBlogException("Unable to get user info")
2016-12-09 14:20:02 +01:00
return ParcelableUserUtils.parseColor(user.profileLinkColor)
2016-07-07 09:39:32 +02:00
}
}
/**
* Created by mariotaku on 16/7/7.
*/
internal class BrowserSignInTask(
context: SignInActivity,
2017-01-07 17:13:11 +01:00
private val apiConfig: CustomAPIConfig,
2016-07-07 09:39:32 +02:00
private val requestToken: OAuthToken,
2017-01-07 17:13:11 +01:00
private val oauthVerifier: String?
2016-07-07 09:39:32 +02:00
) : AbstractSignInTask(context) {
private val context: Context
init {
this.context = context
}
2016-12-03 06:48:40 +01:00
@Throws(Exception::class)
override fun performLogin(): SignInResponse {
2017-01-07 17:13:11 +01:00
val versionSuffix = if (apiConfig.isNoVersionSuffix) null else "1.1"
var endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiConfig.apiUrlFormat,
apiConfig.isSameOAuthUrl)
2016-12-08 16:45:07 +01:00
val oauth = newMicroBlogInstance(context, endpoint = endpoint,
2017-01-07 17:13:11 +01:00
auth = OAuthAuthorization(apiConfig.consumerKey, apiConfig.consumerSecret),
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type,
2016-12-08 16:45:07 +01:00
cls = TwitterOAuth::class.java)
2016-12-03 06:48:40 +01:00
val accessToken: OAuthToken
if (oauthVerifier != null) {
accessToken = oauth.getAccessToken(requestToken, oauthVerifier)
} else {
accessToken = oauth.getAccessToken(requestToken)
}
2017-01-07 17:13:11 +01:00
val auth = OAuthAuthorization(apiConfig.consumerKey,
apiConfig.consumerSecret, accessToken)
endpoint = MicroBlogAPIFactory.getOAuthEndpoint(apiConfig.apiUrlFormat, "api", versionSuffix,
apiConfig.isSameOAuthUrl)
2016-12-08 16:45:07 +01:00
val twitter = newMicroBlogInstance(context, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = MicroBlog::class.java)
2016-12-03 06:48:40 +01:00
val apiUser = twitter.verifyCredentials()
var color = analyseUserProfileColor(apiUser)
2017-03-27 15:56:03 +02:00
val typeExtras = SignInActivity.detectAccountType(twitter, apiUser, apiConfig.type)
2017-04-12 14:58:08 +02:00
val userId = apiUser.id
2016-12-03 06:48:40 +01:00
val accountKey = UserKey(userId, UserKeyUtils.getUserHost(apiUser))
2017-03-27 15:56:03 +02:00
val user = ParcelableUserUtils.fromUser(apiUser, accountKey, typeExtras.first,
2017-03-02 03:42:40 +01:00
profileImageSize = profileImageSize)
2016-12-15 06:11:32 +01:00
val am = AccountManager.get(context)
val account = AccountUtils.findByAccountKey(am, accountKey)
2016-12-03 06:48:40 +01:00
if (account != null) {
2016-12-15 06:11:32 +01:00
color = account.getColor(am)
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
val credentials = OAuthCredentials()
2017-01-07 17:13:11 +01:00
credentials.api_url_format = apiConfig.apiUrlFormat
credentials.no_version_suffix = apiConfig.isNoVersionSuffix
2016-12-03 06:48:40 +01:00
2017-01-07 17:13:11 +01:00
credentials.same_oauth_signing_url = apiConfig.isSameOAuthUrl
2016-12-03 06:48:40 +01:00
2017-01-07 17:13:11 +01:00
credentials.consumer_key = apiConfig.consumerKey
credentials.consumer_secret = apiConfig.consumerSecret
2016-12-03 06:48:40 +01:00
credentials.access_token = accessToken.oauthToken
credentials.access_token_secret = accessToken.oauthTokenSecret
2016-07-07 09:39:32 +02:00
2016-12-03 06:48:40 +01:00
return SignInResponse(account != null, Credentials.Type.OAUTH, credentials, user, color,
2017-03-27 15:56:03 +02:00
typeExtras)
2016-07-07 09:39:32 +02:00
}
}
/**
* Created by mariotaku on 16/7/7.
*/
class InputLoginVerificationDialogFragment : BaseDialogFragment(), DialogInterface.OnClickListener, DialogInterface.OnShowListener {
private var callback: SignInTask.InputLoginVerificationCallback? = null
2016-07-12 03:22:33 +02:00
var challengeType: String? = null
2016-07-07 09:39:32 +02:00
internal fun setCallback(callback: SignInTask.InputLoginVerificationCallback) {
this.callback = callback
}
override fun onCancel(dialog: DialogInterface?) {
callback!!.challengeResponse = null
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setTitle(R.string.login_verification)
builder.setView(R.layout.dialog_login_verification_code)
builder.setPositiveButton(android.R.string.ok, this)
builder.setNegativeButton(android.R.string.cancel, this)
val dialog = builder.create()
dialog.setOnShowListener(this)
return dialog
}
override fun onClick(dialog: DialogInterface, which: Int) {
when (which) {
DialogInterface.BUTTON_POSITIVE -> {
val alertDialog = dialog as AlertDialog
val editVerification = (alertDialog.findViewById(R.id.edit_verification_code) as EditText?)!!
callback!!.challengeResponse = ParseUtils.parseString(editVerification.text)
}
DialogInterface.BUTTON_NEGATIVE -> {
callback!!.challengeResponse = null
}
}
}
override fun onShow(dialog: DialogInterface) {
2017-02-05 14:42:20 +01:00
(dialog as AlertDialog).applyTheme()
val verificationHint = dialog.findViewById(R.id.verification_hint) as TextView?
val editVerification = dialog.findViewById(R.id.edit_verification_code) as EditText?
2016-07-07 09:39:32 +02:00
if (verificationHint == null || editVerification == null) return
2016-07-12 03:22:33 +02:00
when {
"Push".equals(challengeType, ignoreCase = true) -> {
verificationHint.setText(R.string.login_verification_push_hint)
editVerification.visibility = View.GONE
}
"RetypePhoneNumber".equals(challengeType, ignoreCase = true) -> {
verificationHint.setText(R.string.login_challenge_retype_phone_hint)
editVerification.inputType = InputType.TYPE_CLASS_PHONE
editVerification.visibility = View.VISIBLE
}
"RetypeEmail".equals(challengeType, ignoreCase = true) -> {
verificationHint.setText(R.string.login_challenge_retype_email_hint)
editVerification.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
editVerification.visibility = View.VISIBLE
}
"Sms".equals(challengeType, ignoreCase = true) -> {
verificationHint.setText(R.string.login_verification_pin_hint)
editVerification.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
editVerification.visibility = View.VISIBLE
}
else -> {
verificationHint.text = getString(R.string.unsupported_login_verification_type_name,
challengeType)
editVerification.visibility = View.VISIBLE
}
2016-07-07 09:39:32 +02:00
}
}
}
class PasswordSignInDialogFragment : BaseDialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = AlertDialog.Builder(context)
builder.setView(R.layout.dialog_password_sign_in)
builder.setPositiveButton(R.string.sign_in) { dialog, _ ->
2016-07-07 09:39:32 +02:00
val alertDialog = dialog as AlertDialog
val editUsername = alertDialog.findViewById(R.id.username) as EditText?
val editPassword = alertDialog.findViewById(R.id.password) as EditText?
assert(editUsername != null && editPassword != null)
val activity = activity as SignInActivity
activity.setUsernamePassword(editUsername!!.text.toString(),
editPassword!!.text.toString())
activity.doLogin()
}
builder.setNegativeButton(android.R.string.cancel, null)
val alertDialog = builder.create()
2017-02-05 14:42:20 +01:00
alertDialog.setOnShowListener {
(it as AlertDialog)
it.applyTheme()
val editUsername = it.findViewById(R.id.username) as EditText?
val editPassword = it.findViewById(R.id.password) as EditText?
2016-07-07 09:39:32 +02:00
assert(editUsername != null && editPassword != null)
val textWatcher = object : TextWatcher {
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
2017-02-05 14:42:20 +01:00
val button = it.getButton(DialogInterface.BUTTON_POSITIVE) ?: return
2016-07-07 09:39:32 +02:00
button.isEnabled = editUsername!!.length() > 0 && editPassword!!.length() > 0
}
override fun afterTextChanged(s: Editable) {
}
}
editUsername!!.addTextChangedListener(textWatcher)
editPassword!!.addTextChangedListener(textWatcher)
}
return alertDialog
}
}
internal data class SignInResponse(
val alreadyLoggedIn: Boolean,
2016-12-08 08:33:13 +01:00
@Credentials.Type val credsType: String = Credentials.Type.EMPTY,
2016-12-03 06:48:40 +01:00
val credentials: Credentials,
val user: ParcelableUser,
2016-07-07 09:39:32 +02:00
val color: Int = 0,
2017-03-27 15:56:03 +02:00
val typeExtras: Pair<String, AccountExtras?>
2016-07-07 09:39:32 +02:00
) {
private fun writeAccountInfo(action: (k: String, v: String?) -> Unit) {
action(ACCOUNT_USER_DATA_KEY, user.key.toString())
2017-03-27 15:56:03 +02:00
action(ACCOUNT_USER_DATA_TYPE, typeExtras.first)
action(ACCOUNT_USER_DATA_CREDS_TYPE, credsType)
2016-07-07 09:39:32 +02:00
action(ACCOUNT_USER_DATA_ACTIVATED, true.toString())
2017-01-02 09:33:27 +01:00
action(ACCOUNT_USER_DATA_COLOR, toHexColor(color, format = HexColorFormat.RGB))
2016-07-07 09:39:32 +02:00
action(ACCOUNT_USER_DATA_USER, JsonSerializer.serialize(user))
action(ACCOUNT_USER_DATA_EXTRAS, typeExtras.second?.let { JsonSerializer.serialize(it) })
2016-12-08 08:33:13 +01:00
}
2016-07-07 09:39:32 +02:00
2016-12-08 08:33:13 +01:00
private fun writeAuthToken(am: AccountManager, account: Account) {
val authToken = JsonSerializer.serialize(credentials)
am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, authToken)
2016-12-03 06:48:40 +01:00
}
2016-07-07 09:39:32 +02:00
2016-12-03 06:48:40 +01:00
fun updateAccount(am: AccountManager) {
val account = AccountUtils.findByAccountKey(am, user.key) ?: return
writeAccountInfo { k, v ->
2016-12-08 08:33:13 +01:00
am.setUserData(account, k, v)
}
writeAuthToken(am, account)
2016-12-03 06:48:40 +01:00
}
2016-07-07 09:39:32 +02:00
fun addAccount(am: AccountManager, randomizeAccountName: Boolean): Account {
var accountName: String
if (randomizeAccountName) {
val usedNames = ArraySet<String>()
AccountUtils.getAccounts(am).mapTo(usedNames, Account::name)
do {
accountName = UUID.randomUUID().toString()
} while (accountName in usedNames)
} else {
accountName = generateAccountName(user.screen_name, user.key.host)
}
val account = Account(accountName, ACCOUNT_TYPE)
val accountPosition = AccountUtils.getAccounts(am).size
// Don't add UserData in this method, see http://stackoverflow.com/a/29776224/859190
am.addAccountExplicitly(account, null, null)
writeAccountInfo { k, v ->
am.setUserData(account, k, v)
2016-12-08 08:33:13 +01:00
}
am.setUserData(account, ACCOUNT_USER_DATA_POSITION, accountPosition.toString())
writeAuthToken(am, account)
2016-12-08 08:33:13 +01:00
return account
2016-07-07 09:39:32 +02:00
}
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
internal class SignInTask(
activity: SignInActivity,
private val username: String,
private val password: String,
2017-01-07 17:13:11 +01:00
private val apiConfig: CustomAPIConfig
2016-12-03 06:48:40 +01:00
) : AbstractSignInTask(activity) {
2017-01-07 17:13:11 +01:00
private val verificationCallback = InputLoginVerificationCallback()
private val userAgent = UserAgentUtils.getDefaultUserAgentString(activity)
private val apiUrlFormat = apiConfig.apiUrlFormat ?: DEFAULT_TWITTER_API_URL_FORMAT
2016-07-07 09:39:32 +02:00
2016-12-03 06:48:40 +01:00
@Throws(Exception::class)
override fun performLogin(): SignInResponse {
2017-01-07 17:13:11 +01:00
when (apiConfig.credentialsType) {
2016-12-03 06:48:40 +01:00
Credentials.Type.OAUTH -> return authOAuth()
Credentials.Type.XAUTH -> return authxAuth()
Credentials.Type.BASIC -> return authBasic()
Credentials.Type.EMPTY -> return authTwipOMode()
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
return authOAuth()
2016-07-07 09:39:32 +02:00
}
@Throws(OAuthPasswordAuthenticator.AuthenticationException::class, MicroBlogException::class)
private fun authOAuth(): SignInResponse {
2016-12-03 06:48:40 +01:00
val activity = activityRef.get() ?: throw InterruptedException()
2016-07-07 09:39:32 +02:00
val endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiUrlFormat,
2017-01-07 17:13:11 +01:00
apiConfig.isSameOAuthUrl)
val auth = OAuthAuthorization(apiConfig.consumerKey,
apiConfig.consumerSecret)
2016-12-08 16:45:07 +01:00
val oauth = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = TwitterOAuth::class.java)
2016-07-07 09:39:32 +02:00
val authenticator = OAuthPasswordAuthenticator(oauth,
verificationCallback, userAgent)
val accessToken = authenticator.getOAuthAccessToken(username, password)
2017-04-12 14:58:08 +02:00
val userId = accessToken.userId
return getOAuthSignInResponse(activity, accessToken, userId, Credentials.Type.OAUTH)
2016-07-07 09:39:32 +02:00
}
@Throws(MicroBlogException::class)
private fun authxAuth(): SignInResponse {
2016-12-03 06:48:40 +01:00
val activity = activityRef.get() ?: throw InterruptedException()
2016-07-07 09:39:32 +02:00
var endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiUrlFormat,
2017-01-07 17:13:11 +01:00
apiConfig.isSameOAuthUrl)
var auth = OAuthAuthorization(apiConfig.consumerKey,
apiConfig.consumerSecret)
2016-12-08 16:45:07 +01:00
val oauth = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = TwitterOAuth::class.java)
2016-07-07 09:39:32 +02:00
val accessToken = oauth.getAccessToken(username, password)
2017-04-12 14:58:08 +02:00
val userId = accessToken.userId ?: run {
2016-07-07 09:39:32 +02:00
// Trying to fix up userId if accessToken doesn't contain one.
2017-01-07 17:13:11 +01:00
auth = OAuthAuthorization(apiConfig.consumerKey,
apiConfig.consumerSecret, accessToken)
endpoint = MicroBlogAPIFactory.getOAuthRestEndpoint(apiUrlFormat, apiConfig.isSameOAuthUrl,
apiConfig.isNoVersionSuffix)
2016-12-08 16:45:07 +01:00
val microBlog = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = MicroBlog::class.java)
2017-04-12 14:58:08 +02:00
return@run microBlog.verifyCredentials().id
2016-07-07 09:39:32 +02:00
}
2017-04-12 14:58:08 +02:00
return getOAuthSignInResponse(activity, accessToken, userId, Credentials.Type.XAUTH)
2016-07-07 09:39:32 +02:00
}
@Throws(MicroBlogException::class, OAuthPasswordAuthenticator.AuthenticationException::class)
private fun authBasic(): SignInResponse {
2016-12-03 06:48:40 +01:00
val activity = activityRef.get() ?: throw InterruptedException()
2017-01-07 17:13:11 +01:00
val versionSuffix = if (apiConfig.isNoVersionSuffix) null else "1.1"
2016-07-07 09:39:32 +02:00
val endpoint = Endpoint(MicroBlogAPIFactory.getApiUrl(apiUrlFormat, "api",
versionSuffix))
val auth = BasicAuthorization(username, password)
2016-12-08 16:45:07 +01:00
val twitter = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = MicroBlog::class.java)
2016-12-03 06:48:40 +01:00
val apiUser: User
2016-07-07 09:39:32 +02:00
try {
2016-12-03 06:48:40 +01:00
apiUser = twitter.verifyCredentials()
2016-07-07 09:39:32 +02:00
} catch (e: MicroBlogException) {
if (e.statusCode == 401) {
throw WrongBasicCredentialException()
} else if (e.statusCode == 404) {
throw WrongAPIURLFormatException()
}
throw e
}
2016-12-03 06:48:40 +01:00
val userId = apiUser.id!!
var color = analyseUserProfileColor(apiUser)
2017-03-27 15:56:03 +02:00
val typeExtras = SignInActivity.detectAccountType(twitter, apiUser, apiConfig.type)
2016-12-03 06:48:40 +01:00
val accountKey = UserKey(userId, UserKeyUtils.getUserHost(apiUser))
2017-03-27 15:56:03 +02:00
val user = ParcelableUserUtils.fromUser(apiUser, accountKey, typeExtras.first,
2017-03-02 03:42:40 +01:00
profileImageSize = profileImageSize)
2016-12-15 06:11:32 +01:00
val am = AccountManager.get(activity)
val account = AccountUtils.findByAccountKey(am, accountKey)
2016-07-07 09:39:32 +02:00
if (account != null) {
2016-12-15 06:11:32 +01:00
color = account.getColor(am)
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
val credentials = BasicCredentials()
credentials.api_url_format = apiUrlFormat
2017-01-07 17:13:11 +01:00
credentials.no_version_suffix = apiConfig.isNoVersionSuffix
2016-12-03 06:48:40 +01:00
credentials.username = username
credentials.password = password
return SignInResponse(account != null, Credentials.Type.BASIC, credentials, user,
2017-03-27 15:56:03 +02:00
color, typeExtras)
2016-07-07 09:39:32 +02:00
}
@Throws(MicroBlogException::class)
private fun authTwipOMode(): SignInResponse {
2016-12-03 06:48:40 +01:00
val activity = activityRef.get() ?: throw InterruptedException()
2017-01-07 17:13:11 +01:00
val versionSuffix = if (apiConfig.isNoVersionSuffix) null else "1.1"
2016-07-07 09:39:32 +02:00
val endpoint = Endpoint(MicroBlogAPIFactory.getApiUrl(apiUrlFormat, "api",
versionSuffix))
val auth = EmptyAuthorization()
2016-12-08 16:45:07 +01:00
val twitter = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = MicroBlog::class.java)
2016-12-03 06:48:40 +01:00
val apiUser = twitter.verifyCredentials()
val userId = apiUser.id!!
var color = analyseUserProfileColor(apiUser)
2017-03-27 15:56:03 +02:00
val typeExtras = SignInActivity.detectAccountType(twitter, apiUser, apiConfig.type)
2016-12-03 06:48:40 +01:00
val accountKey = UserKey(userId, UserKeyUtils.getUserHost(apiUser))
2017-03-27 15:56:03 +02:00
val user = ParcelableUserUtils.fromUser(apiUser, accountKey, typeExtras.first,
2017-03-02 03:42:40 +01:00
profileImageSize = profileImageSize)
2016-12-15 06:11:32 +01:00
val am = AccountManager.get(activity)
val account = AccountUtils.findByAccountKey(am, accountKey)
2016-07-07 09:39:32 +02:00
if (account != null) {
2016-12-15 06:11:32 +01:00
color = account.getColor(am)
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
val credentials = EmptyCredentials()
credentials.api_url_format = apiUrlFormat
2017-01-07 17:13:11 +01:00
credentials.no_version_suffix = apiConfig.isNoVersionSuffix
2016-12-03 06:48:40 +01:00
return SignInResponse(account != null, Credentials.Type.EMPTY, credentials, user, color,
2017-03-27 15:56:03 +02:00
typeExtras)
2016-07-07 09:39:32 +02:00
}
@Throws(MicroBlogException::class)
private fun getOAuthSignInResponse(activity: SignInActivity,
2017-02-11 10:03:35 +01:00
accessToken: OAuthToken,
userId: String, @Credentials.Type authType: String): SignInResponse {
2017-01-07 17:13:11 +01:00
val auth = OAuthAuthorization(apiConfig.consumerKey,
apiConfig.consumerSecret, accessToken)
2016-07-07 09:39:32 +02:00
val endpoint = MicroBlogAPIFactory.getOAuthRestEndpoint(apiUrlFormat,
2017-01-07 17:13:11 +01:00
apiConfig.isSameOAuthUrl, apiConfig.isNoVersionSuffix)
2016-12-08 16:45:07 +01:00
val twitter = newMicroBlogInstance(activity, endpoint = endpoint, auth = auth,
2017-02-11 10:03:35 +01:00
accountType = apiConfig.type, cls = MicroBlog::class.java)
2016-12-03 06:48:40 +01:00
val apiUser = twitter.verifyCredentials()
var color = analyseUserProfileColor(apiUser)
2017-03-27 15:56:03 +02:00
val typeExtras = SignInActivity.detectAccountType(twitter, apiUser, apiConfig.type)
2016-12-03 06:48:40 +01:00
val accountKey = UserKey(userId, UserKeyUtils.getUserHost(apiUser))
2017-03-27 15:56:03 +02:00
val user = ParcelableUserUtils.fromUser(apiUser, accountKey, typeExtras.first,
2017-03-02 03:42:40 +01:00
profileImageSize = profileImageSize)
2016-12-15 06:11:32 +01:00
val am = AccountManager.get(activity)
val account = AccountUtils.findByAccountKey(am, accountKey)
2016-07-07 09:39:32 +02:00
if (account != null) {
2016-12-15 06:11:32 +01:00
color = account.getColor(am)
2016-07-07 09:39:32 +02:00
}
2016-12-03 06:48:40 +01:00
val credentials = OAuthCredentials()
credentials.api_url_format = apiUrlFormat
2017-01-07 17:13:11 +01:00
credentials.no_version_suffix = apiConfig.isNoVersionSuffix
2016-12-03 06:48:40 +01:00
2017-01-07 17:13:11 +01:00
credentials.same_oauth_signing_url = apiConfig.isSameOAuthUrl
2016-12-03 06:48:40 +01:00
2017-01-07 17:13:11 +01:00
credentials.consumer_key = apiConfig.consumerKey
credentials.consumer_secret = apiConfig.consumerSecret
2016-12-03 06:48:40 +01:00
credentials.access_token = accessToken.oauthToken
credentials.access_token_secret = accessToken.oauthTokenSecret
2017-03-27 15:56:03 +02:00
return SignInResponse(account != null, authType, credentials, user, color, typeExtras)
2016-07-07 09:39:32 +02:00
}
internal class WrongBasicCredentialException : OAuthPasswordAuthenticator.AuthenticationException()
internal class WrongAPIURLFormatException : OAuthPasswordAuthenticator.AuthenticationException()
internal inner class InputLoginVerificationCallback : OAuthPasswordAuthenticator.LoginVerificationCallback {
var isChallengeFinished: Boolean = false
var challengeResponse: String? = null
set(value) {
isChallengeFinished = true
field = value
}
2016-07-14 14:00:27 +02:00
override fun getLoginVerification(challengeType: String): String? {
2016-07-07 09:39:32 +02:00
// Dismiss current progress dialog
publishProgress(Runnable {
2016-12-15 06:49:18 +01:00
activityRef.get()?.dismissDialogFragment(SignInActivity.FRAGMENT_TAG_SIGN_IN_PROGRESS)
2016-07-07 09:39:32 +02:00
})
// Show verification input dialog and wait for user input
publishProgress(Runnable {
val activity = activityRef.get() ?: return@Runnable
activity.executeAfterFragmentResumed { activity ->
val sia = activity as SignInActivity
val df = InputLoginVerificationDialogFragment()
df.isCancelable = false
df.setCallback(this@InputLoginVerificationCallback)
2016-07-12 03:22:33 +02:00
df.challengeType = challengeType
2016-12-15 06:49:18 +01:00
df.show(sia.supportFragmentManager, "login_challenge_$challengeType")
2016-07-07 09:39:32 +02:00
}
})
while (!isChallengeFinished) {
// Wait for 50ms
try {
Thread.sleep(50)
} catch (e: InterruptedException) {
// Ignore
}
}
// Show progress dialog
publishProgress(Runnable {
val activity = activityRef.get() ?: return@Runnable
activity.showSignInProgressDialog()
})
return challengeResponse
}
}
}
companion object {
private val FRAGMENT_TAG_SIGN_IN_PROGRESS = "sign_in_progress"
private val FRAGMENT_TAG_LOADING_DEFAULT_FEATURES = "loading_default_features"
2016-07-07 09:39:32 +02:00
private val EXTRA_API_LAST_CHANGE = "api_last_change"
private val DEFAULT_TWITTER_API_URL_FORMAT = "https://[DOMAIN.]twitter.com/"
2017-01-07 17:13:11 +01:00
@Throws(IOException::class)
internal fun detectAccountType(twitter: MicroBlog, user: User, type: String?): Pair<String, AccountExtras?> {
when (type) {
AccountType.STATUSNET -> {
2017-01-25 13:08:23 +01:00
return getStatusNetAccountType(twitter)
2017-01-07 17:13:11 +01:00
}
AccountType.TWITTER -> {
2017-01-25 13:08:23 +01:00
return getTwitterAccountType(twitter)
2017-01-07 17:13:11 +01:00
}
AccountType.FANFOU -> {
return Pair(AccountType.FANFOU, null)
}
else -> {
if (UserKeyUtils.isFanfouUser(user)) {
return Pair(AccountType.FANFOU, null)
}
2016-07-07 09:39:32 +02:00
}
}
return Pair(AccountType.TWITTER, null)
2016-07-07 09:39:32 +02:00
}
2017-01-25 13:08:23 +01:00
private fun getStatusNetAccountType(twitter: MicroBlog): Pair<String, StatusNetAccountExtras> {
// Get StatusNet specific resource
val config = twitter.statusNetConfig
val extras = StatusNetAccountExtras()
val site = config.site
if (site != null) {
extras.textLimit = site.textLimit
}
return Pair(AccountType.STATUSNET, extras)
}
private fun getTwitterAccountType(twitter: MicroBlog): Pair<String, TwitterAccountExtras> {
val extras = TwitterAccountExtras()
try {
// Get Twitter official only resource
val paging = Paging()
paging.count(1)
twitter.getActivitiesAboutMe(paging)
extras.setIsOfficialCredentials(true)
} catch (e: MicroBlogException) {
// Ignore
}
return Pair(AccountType.TWITTER, extras)
}
2016-07-07 09:39:32 +02:00
}
private val CustomAPIConfig.signUpUrlOrDefault: String?
get() = signUpUrl ?: when (type) {
AccountType.TWITTER -> "https://twitter.com/signup"
AccountType.FANFOU -> "http://fanfou.com/register"
else -> null
}
2016-07-07 09:39:32 +02:00
}