adding test
This commit is contained in:
parent
a9fff789f8
commit
756a6dbb4d
|
@ -37,6 +37,8 @@ public interface TwidereConstants extends SharedPreferenceConstants, IntentConst
|
|||
String TWIDERE_PROJECT_EMAIL = "twidere.project@gmail.com";
|
||||
String TWIDERE_PACKAGE_NAME = "org.mariotaku.twidere";
|
||||
|
||||
String ACCOUNT_TYPE = "org.mariotaku.twidere.account";
|
||||
|
||||
String LOGTAG = TWIDERE_APP_NAME;
|
||||
|
||||
String USER_NICKNAME_PREFERENCES_NAME = "user_nicknames";
|
||||
|
|
|
@ -175,6 +175,7 @@ dependencies {
|
|||
compile 'nl.komponents.kovenant:kovenant:3.3.0'
|
||||
compile 'nl.komponents.kovenant:kovenant-android:3.3.0'
|
||||
compile 'nl.komponents.kovenant:kovenant-functional:3.3.0'
|
||||
compile 'nl.komponents.kovenant:kovenant-combine:3.3.0'
|
||||
}
|
||||
|
||||
task svgToDrawable(type: SvgDrawableTask) {
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
package org.mariotaku.twidere.activity
|
||||
|
||||
import android.net.Uri
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.mariotaku.twidere.fragment.ImagePageFragment
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 16/3/3.
|
||||
*/
|
||||
class ImagePageFragmentTest {
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testReplaceTwitterMediaUri() {
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.png:large")).toString())
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:orig",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.png:orig")).toString())
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.jpg:large")).toString())
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:large",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.jpg:orig")).toString())
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.jpg")).toString())
|
||||
assertEquals("https://pbs.twimg.com/media/DEADBEEF.png:",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://pbs.twimg.com/media/DEADBEEF.jpg:")).toString())
|
||||
assertEquals("https://example.com/media/DEADBEEF.jpg",
|
||||
ImagePageFragment.replaceTwitterMediaUri(Uri.parse(
|
||||
"https://example.com/media/DEADBEEF.jpg")).toString())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package org.mariotaku.twidere.test.account
|
||||
|
||||
import android.accounts.Account
|
||||
import android.accounts.AccountManager
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mariotaku.ktextension.Bundle
|
||||
import org.mariotaku.twidere.TwidereConstants.ACCOUNT_TYPE
|
||||
import org.mariotaku.twidere.extension.model.account_name
|
||||
import org.mariotaku.twidere.model.util.ParcelableAccountUtils
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore
|
||||
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts
|
||||
import org.mariotaku.twidere.util.support.AccountManagerSupport
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MigrationTest {
|
||||
@Test
|
||||
fun testMigration() {
|
||||
|
||||
val context = InstrumentationRegistry.getTargetContext()
|
||||
|
||||
val am = AccountManager.get(context)
|
||||
|
||||
am.getAccountsByType(ACCOUNT_TYPE).map { account ->
|
||||
AccountManagerSupport.removeAccount(am, account, null, null, null)
|
||||
}
|
||||
|
||||
ParcelableAccountUtils.getAccounts(context).forEach { pAccount ->
|
||||
val account = Account(pAccount.account_name, ACCOUNT_TYPE)
|
||||
val userdata = Bundle {
|
||||
this[Accounts.ACCOUNT_KEY]
|
||||
}
|
||||
am.addAccountExplicitly(account, null, userdata)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package org.mariotaku.twidere.util.support;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.accounts.AccountManagerCallback;
|
||||
import android.accounts.AccountManagerFuture;
|
||||
import android.accounts.AuthenticatorException;
|
||||
import android.accounts.OperationCanceledException;
|
||||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.RequiresApi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
|
||||
public class AccountManagerSupport {
|
||||
public static AccountManagerFuture<Bundle> removeAccount(AccountManager am, Account account,
|
||||
Activity activity,
|
||||
final AccountManagerCallback<Bundle> callback,
|
||||
Handler handler) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
return AccountManagerSupportL.removeAccount(am, account, activity, callback, handler);
|
||||
}
|
||||
//noinspection deprecation
|
||||
final AccountManagerFuture<Boolean> future = am.removeAccount(account, new AccountManagerCallback<Boolean>() {
|
||||
@Override
|
||||
public void run(AccountManagerFuture<Boolean> future) {
|
||||
callback.run(new BooleanToBundleAccountManagerFuture(future));
|
||||
}
|
||||
}, handler);
|
||||
return new BooleanToBundleAccountManagerFuture(future);
|
||||
}
|
||||
|
||||
private static class AccountManagerSupportL {
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
static AccountManagerFuture<Bundle> removeAccount(AccountManager am, Account account,
|
||||
Activity activity,
|
||||
AccountManagerCallback<Bundle> callback,
|
||||
Handler handler) {
|
||||
return am.removeAccount(account, activity, callback, handler);
|
||||
}
|
||||
}
|
||||
|
||||
private static class BooleanToBundleAccountManagerFuture implements AccountManagerFuture<Bundle> {
|
||||
|
||||
private final AccountManagerFuture<Boolean> future;
|
||||
|
||||
BooleanToBundleAccountManagerFuture(AccountManagerFuture<Boolean> future) {
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
return future.cancel(mayInterruptIfRunning);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return future.isCancelled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return future.isDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getResult() throws OperationCanceledException, IOException, AuthenticatorException {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, future.getResult());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getResult(long timeout, TimeUnit unit) throws OperationCanceledException, IOException, AuthenticatorException {
|
||||
Bundle result = new Bundle();
|
||||
result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, future.getResult(timeout, unit));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package org.mariotaku.ktextension
|
||||
|
||||
import nl.komponents.kovenant.Deferred
|
||||
import nl.komponents.kovenant.Promise
|
||||
import nl.komponents.kovenant.deferred
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
fun <V, E> combine(promises: List<Promise<V, E>>): Promise<List<V>, E> {
|
||||
return concreteCombine(promises)
|
||||
}
|
||||
|
||||
fun <V, E> concreteCombine(promises: List<Promise<V, E>>): Promise<List<V>, E> {
|
||||
val deferred = deferred<List<V>, E>()
|
||||
|
||||
val results = AtomicReferenceArray<V>(promises.size)
|
||||
val successCount = AtomicInteger(promises.size)
|
||||
|
||||
fun createArray(): List<V> {
|
||||
return (0 until results.length()).map { results[it] }
|
||||
}
|
||||
|
||||
fun Promise<V, *>.registerSuccess(idx: Int) {
|
||||
success { v ->
|
||||
results.set(idx, v)
|
||||
if (successCount.decrementAndGet() == 0) {
|
||||
deferred.resolve(createArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <V, E> Deferred<V, E>.registerFail(promises: List<Promise<*, E>>) {
|
||||
val failCount = AtomicInteger(0)
|
||||
promises.forEach { promise ->
|
||||
promise.fail { e ->
|
||||
if (failCount.incrementAndGet() == 1) {
|
||||
this.reject(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promises.forEachIndexed { idx, promise ->
|
||||
promise.registerSuccess(idx)
|
||||
}
|
||||
deferred.registerFail(promises)
|
||||
|
||||
return deferred.promise
|
||||
}
|
|
@ -19,6 +19,8 @@
|
|||
|
||||
package org.mariotaku.twidere.activity
|
||||
|
||||
import android.accounts.AccountAuthenticatorResponse
|
||||
import android.accounts.AccountManager
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ContentValues
|
||||
|
@ -79,6 +81,7 @@ import org.mariotaku.twidere.util.OAuthPasswordAuthenticator.*
|
|||
import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
||||
private var apiUrlFormat: String? = null
|
||||
private var authType: Int = 0
|
||||
|
@ -89,15 +92,66 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
|||
private var noVersionSuffix: Boolean = false
|
||||
private var signInTask: AbstractSignInTask? = null
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
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) {
|
||||
apiUrlFormat = savedInstanceState.getString(Accounts.API_URL_FORMAT)
|
||||
authType = savedInstanceState.getInt(Accounts.AUTH_TYPE)
|
||||
sameOAuthSigningUrl = savedInstanceState.getBoolean(Accounts.SAME_OAUTH_SIGNING_URL)
|
||||
consumerKey = savedInstanceState.getString(Accounts.CONSUMER_KEY)?.trim()
|
||||
consumerSecret = savedInstanceState.getString(Accounts.CONSUMER_SECRET)?.trim()
|
||||
apiChangeTimestamp = savedInstanceState.getLong(EXTRA_API_LAST_CHANGE)
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
val isTwipOMode = authType == AuthType.TWIP_O_MODE
|
||||
usernamePasswordContainer.visibility = if (isTwipOMode) View.GONE else View.VISIBLE
|
||||
signInSignUpContainer.orientation = if (isTwipOMode) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
|
||||
|
||||
editUsername.addTextChangedListener(this)
|
||||
editPassword.addTextChangedListener(this)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
val consumerKey = preferences.getString(KEY_CONSUMER_KEY, null)
|
||||
val consumerSecret = preferences.getString(KEY_CONSUMER_SECRET, null)
|
||||
if (BuildConfig.SHOW_CUSTOM_TOKEN_DIALOG && savedInstanceState == null &&
|
||||
!preferences.getBoolean(KEY_CONSUMER_KEY_SECRET_SET, false) &&
|
||||
!Utils.isCustomConsumerKeySecret(consumerKey, consumerSecret)) {
|
||||
val df = SetConsumerKeySecretDialogFragment()
|
||||
df.isCancelable = false
|
||||
df.show(supportFragmentManager, "set_consumer_key_secret")
|
||||
}
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
updateSignInType()
|
||||
setSignInButton()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
loaderManager.destroyLoader(0)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_sign_in, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
REQUEST_EDIT_API -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
|
@ -121,6 +175,27 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
|||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
}
|
||||
|
||||
internal fun updateSignInType() {
|
||||
when (authType) {
|
||||
AuthType.XAUTH, AuthType.BASIC -> {
|
||||
|
@ -162,16 +237,6 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_sign_in, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
loaderManager.destroyLoader(0)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
|
@ -237,54 +302,11 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
|
|||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
setSignInButton()
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_sign_in)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
apiUrlFormat = savedInstanceState.getString(Accounts.API_URL_FORMAT)
|
||||
authType = savedInstanceState.getInt(Accounts.AUTH_TYPE)
|
||||
sameOAuthSigningUrl = savedInstanceState.getBoolean(Accounts.SAME_OAUTH_SIGNING_URL)
|
||||
consumerKey = savedInstanceState.getString(Accounts.CONSUMER_KEY)?.trim()
|
||||
consumerSecret = savedInstanceState.getString(Accounts.CONSUMER_SECRET)?.trim()
|
||||
apiChangeTimestamp = savedInstanceState.getLong(EXTRA_API_LAST_CHANGE)
|
||||
}
|
||||
|
||||
val isTwipOMode = authType == AuthType.TWIP_O_MODE
|
||||
usernamePasswordContainer.visibility = if (isTwipOMode) View.GONE else View.VISIBLE
|
||||
signInSignUpContainer.orientation = if (isTwipOMode) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
|
||||
|
||||
editUsername.addTextChangedListener(this)
|
||||
editPassword.addTextChangedListener(this)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
val consumerKey = preferences.getString(KEY_CONSUMER_KEY, null)
|
||||
val consumerSecret = preferences.getString(KEY_CONSUMER_SECRET, null)
|
||||
if (BuildConfig.SHOW_CUSTOM_TOKEN_DIALOG && savedInstanceState == null &&
|
||||
!preferences.getBoolean(KEY_CONSUMER_KEY_SECRET_SET, false) &&
|
||||
!Utils.isCustomConsumerKeySecret(consumerKey, consumerSecret)) {
|
||||
val df = SetConsumerKeySecretDialogFragment()
|
||||
df.isCancelable = false
|
||||
df.show(supportFragmentManager, "set_consumer_key_secret")
|
||||
}
|
||||
|
||||
updateSignInType()
|
||||
setSignInButton()
|
||||
}
|
||||
|
||||
internal fun doLogin() {
|
||||
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING) {
|
||||
signInTask!!.cancel(true)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package org.mariotaku.twidere.extension.model
|
||||
|
||||
import org.mariotaku.twidere.model.ParcelableAccount
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
|
||||
val ParcelableAccount.account_name: String
|
||||
get() = UserKey(screen_name, account_key.host).toString()
|
||||
|
|
@ -10,7 +10,9 @@ import android.content.Intent
|
|||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import org.mariotaku.ktextension.set
|
||||
import org.mariotaku.twidere.TwidereConstants
|
||||
import org.mariotaku.twidere.activity.SignInActivity
|
||||
import org.mariotaku.twidere.util.support.AccountManagerSupport
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue