1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-02-17 04:00:48 +01:00

fixed account dashboard icon position

fixed sign in/sign out
This commit is contained in:
Mariotaku Lee 2016-12-09 13:26:26 +08:00
parent e4668f45ef
commit f890794191
14 changed files with 313 additions and 159 deletions

View File

@ -20,8 +20,8 @@
package org.mariotaku.twidere.constant;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.AuthTypeInt;
import org.mariotaku.twidere.annotation.Preference;
import org.mariotaku.twidere.model.account.cred.Credentials;
import static org.mariotaku.twidere.annotation.PreferenceType.BOOLEAN;
import static org.mariotaku.twidere.annotation.PreferenceType.INT;
@ -237,8 +237,8 @@ public interface SharedPreferenceConstants {
String KEY_SAME_OAUTH_SIGNING_URL = "same_oauth_signing_url";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = false)
String KEY_NO_VERSION_SUFFIX = "no_version_suffix";
@Preference(type = INT, hasDefault = true, defaultInt = AuthTypeInt.OAUTH)
String KEY_AUTH_TYPE = "auth_type";
@Preference(type = STRING, hasDefault = true, defaultString = Credentials.Type.OAUTH)
String KEY_CREDENTIALS_TYPE = "credentials_type";
@Preference(type = STRING, hasDefault = true, defaultString = TwidereConstants.TWITTER_CONSUMER_KEY)
String KEY_CONSUMER_KEY = "consumer_key";
@Preference(type = STRING, hasDefault = true, defaultString = TwidereConstants.TWITTER_CONSUMER_SECRET)

View File

@ -7,10 +7,9 @@ import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.bluelinelabs.logansquare.typeconverters.StringBasedTypeConverter;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.annotation.AuthTypeInt;
import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.Utils;
@ -35,9 +34,9 @@ public final class CustomAPIConfig {
String localizedName;
@JsonField(name = "api_url_format")
String apiUrlFormat;
@AuthTypeInt
@JsonField(name = "auth_type", typeConverter = AuthTypeConverter.class)
int authType;
@Credentials.Type
@JsonField(name = "auth_type")
String credentialsType;
@JsonField(name = "same_oauth_url")
boolean sameOAuthUrl;
@JsonField(name = "no_version_suffix")
@ -50,11 +49,11 @@ public final class CustomAPIConfig {
CustomAPIConfig() {
}
public CustomAPIConfig(String name, String apiUrlFormat, int authType, boolean sameOAuthUrl,
public CustomAPIConfig(String name, String apiUrlFormat, String credentialsType, boolean sameOAuthUrl,
boolean noVersionSuffix, String consumerKey, String consumerSecret) {
this.name = name;
this.apiUrlFormat = apiUrlFormat;
this.authType = authType;
this.credentialsType = credentialsType;
this.sameOAuthUrl = sameOAuthUrl;
this.noVersionSuffix = noVersionSuffix;
this.consumerKey = consumerKey;
@ -79,8 +78,8 @@ public final class CustomAPIConfig {
return apiUrlFormat;
}
public int getAuthType() {
return authType;
public String getCredentialsType() {
return credentialsType;
}
public boolean isSameOAuthUrl() {
@ -117,50 +116,8 @@ public final class CustomAPIConfig {
public static List<CustomAPIConfig> listBuiltin(@NonNull Context context) {
return Collections.singletonList(new CustomAPIConfig(context.getString(R.string.provider_default),
DEFAULT_TWITTER_API_URL_FORMAT, AuthTypeInt.OAUTH, true, false,
DEFAULT_TWITTER_API_URL_FORMAT, Credentials.Type.OAUTH, true, false,
TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET));
}
static class AuthTypeConverter extends StringBasedTypeConverter<Integer> {
@Override
@AuthTypeInt
public Integer getFromString(String string) {
if (string == null) return AuthTypeInt.OAUTH;
switch (string) {
case "oauth": {
return AuthTypeInt.OAUTH;
}
case "xauth": {
return AuthTypeInt.XAUTH;
}
case "basic": {
return AuthTypeInt.BASIC;
}
case "twip_o_mode": {
return AuthTypeInt.TWIP_O_MODE;
}
}
return AuthTypeInt.OAUTH;
}
@Override
public String convertToString(@AuthTypeInt Integer object) {
if (object == null) return "oauth";
switch (object) {
case AuthTypeInt.OAUTH: {
return "oauth";
}
case AuthTypeInt.XAUTH: {
return "xauth";
}
case AuthTypeInt.BASIC: {
return "basic";
}
case AuthTypeInt.TWIP_O_MODE: {
return "twip_o_mode";
}
}
return "oauth";
}
}
}

View File

@ -42,8 +42,8 @@ import android.widget.Toast;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.APIEditorActivity;
import org.mariotaku.twidere.annotation.AuthTypeInt;
import org.mariotaku.twidere.fragment.ThemedPreferenceDialogFragmentCompat;
import org.mariotaku.twidere.model.account.cred.Credentials;
import org.mariotaku.twidere.preference.iface.IDialogPreference;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.util.ParseUtils;
@ -116,13 +116,13 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
mEditAuthType.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
final int authType = APIEditorActivity.Companion.getCheckedAuthType(checkedId);
final boolean isOAuth = authType == AuthTypeInt.OAUTH || authType == AuthTypeInt.XAUTH;
final String authType = APIEditorActivity.Companion.getCheckedAuthType(checkedId);
final boolean isOAuth = Credentials.Type.OAUTH.equals(authType) || Credentials.Type.XAUTH.equals(authType);
mEditSameOAuthSigningUrl.setVisibility(isOAuth ? View.VISIBLE : View.GONE);
mEditConsumerKey.setVisibility(isOAuth ? View.VISIBLE : View.GONE);
mEditConsumerSecret.setVisibility(isOAuth ? View.VISIBLE : View.GONE);
if (!mEditNoVersionSuffixChanged) {
mEditNoVersionSuffix.setChecked(authType == AuthTypeInt.TWIP_O_MODE);
mEditNoVersionSuffix.setChecked(Credentials.Type.EMPTY.equals(authType));
}
}
});
@ -135,7 +135,7 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
if (savedInstanceState != null) {
final String apiUrlFormat = savedInstanceState.getString(Accounts.API_URL_FORMAT);
final int authType = savedInstanceState.getInt(Accounts.AUTH_TYPE);
final String authType = savedInstanceState.getString(Accounts.AUTH_TYPE);
final boolean sameOAuthSigningUrl = savedInstanceState.getBoolean(Accounts.SAME_OAUTH_SIGNING_URL);
final boolean noVersionSuffix = savedInstanceState.getBoolean(Accounts.NO_VERSION_SUFFIX);
final String consumerKey = trim(savedInstanceState.getString(Accounts.CONSUMER_KEY));
@ -144,7 +144,7 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
} else {
final SharedPreferences preferences = preference.getSharedPreferences();
final String apiUrlFormat = preferences.getString(KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT);
final int authType = preferences.getInt(KEY_AUTH_TYPE, AuthTypeInt.OAUTH);
final String authType = preferences.getString(KEY_CREDENTIALS_TYPE, Credentials.Type.OAUTH);
final boolean sameOAuthSigningUrl = preferences.getBoolean(KEY_SAME_OAUTH_SIGNING_URL, true);
final boolean noVersionSuffix = preferences.getBoolean(KEY_NO_VERSION_SUFFIX, false);
final String consumerKey = trim(preferences.getString(KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY));
@ -163,7 +163,7 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
final SharedPreferences preferences = preference.getSharedPreferences();
final String apiUrlFormat = ParseUtils.parseString(mEditAPIUrlFormat.getText());
final int authType = APIEditorActivity.Companion.getCheckedAuthType(mEditAuthType.getCheckedRadioButtonId());
final String authType = APIEditorActivity.Companion.getCheckedAuthType(mEditAuthType.getCheckedRadioButtonId());
final boolean sameOAuthSigningUrl = mEditSameOAuthSigningUrl.isChecked();
final boolean noVersionSuffix = mEditNoVersionSuffix.isChecked();
final String consumerKey = ParseUtils.parseString(mEditConsumerKey.getText());
@ -177,7 +177,7 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
editor.remove(KEY_CONSUMER_SECRET);
}
editor.putString(KEY_API_URL_FORMAT, apiUrlFormat);
editor.putInt(KEY_AUTH_TYPE, authType);
editor.putString(KEY_CREDENTIALS_TYPE, authType);
editor.putBoolean(KEY_SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl);
editor.putBoolean(KEY_NO_VERSION_SUFFIX, noVersionSuffix);
editor.apply();
@ -187,24 +187,20 @@ public class DefaultAPIPreference extends DialogPreference implements Constants,
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(Accounts.API_URL_FORMAT, ParseUtils.parseString(mEditAPIUrlFormat.getText()));
outState.putInt(Accounts.AUTH_TYPE, APIEditorActivity.Companion.getCheckedAuthType(mEditAuthType.getCheckedRadioButtonId()));
outState.putString(Accounts.AUTH_TYPE, APIEditorActivity.Companion.getCheckedAuthType(mEditAuthType.getCheckedRadioButtonId()));
outState.putBoolean(Accounts.SAME_OAUTH_SIGNING_URL, mEditSameOAuthSigningUrl.isChecked());
outState.putString(Accounts.CONSUMER_KEY, ParseUtils.parseString(mEditConsumerKey.getText()));
outState.putString(Accounts.CONSUMER_SECRET, ParseUtils.parseString(mEditConsumerSecret.getText()));
}
private void setValues(final String apiUrlFormat, final int authType, final boolean sameOAuthSigningUrl,
private void setValues(final String apiUrlFormat, final String authType, final boolean sameOAuthSigningUrl,
final boolean noVersionSuffix, final String consumerKey, final String consumerSecret) {
mEditAPIUrlFormat.setText(apiUrlFormat);
mEditSameOAuthSigningUrl.setChecked(sameOAuthSigningUrl);
mEditNoVersionSuffix.setChecked(noVersionSuffix);
mEditConsumerKey.setText(consumerKey);
mEditConsumerSecret.setText(consumerSecret);
mButtonOAuth.setChecked(authType == AuthTypeInt.OAUTH);
mButtonxAuth.setChecked(authType == AuthTypeInt.XAUTH);
mButtonBasic.setChecked(authType == AuthTypeInt.BASIC);
mButtonTwipOMode.setChecked(authType == AuthTypeInt.TWIP_O_MODE);
mEditAuthType.check(APIEditorActivity.Companion.getAuthTypeId(authType));
if (mEditAuthType.getCheckedRadioButtonId() == -1) {
mButtonOAuth.setChecked(true);
}

View File

@ -0,0 +1,62 @@
/*
* 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.view;
import android.content.Context;
import android.support.v4.widget.Space;
import android.util.AttributeSet;
import android.view.ViewGroup;
public class SquareSpace extends Space {
public SquareSpace(final Context context) {
this(context, null);
}
public SquareSpace(final Context context, final AttributeSet attrs) {
this(context, attrs, 0);
}
public SquareSpace(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final int width = MeasureSpec.getSize(widthMeasureSpec), height = MeasureSpec.getSize(heightMeasureSpec);
final ViewGroup.LayoutParams lp = getLayoutParams();
if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT && lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
setMeasuredDimension(height, height);
} else if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT && lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
setMeasuredDimension(width, width);
} else {
if (width > height) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
setMeasuredDimension(height, height);
} else {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
setMeasuredDimension(width, width);
}
}
}
}

View File

@ -69,7 +69,7 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.iface.APIEditorActivity
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.AuthTypeInt
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CREDENTIALS_TYPE
import org.mariotaku.twidere.extension.newMicroBlogInstance
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
@ -358,7 +358,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
val apiLastChange = preferences.getLong(KEY_API_LAST_CHANGE, apiChangeTimestamp)
val defaultApiChanged = apiLastChange != apiChangeTimestamp
val apiUrlFormat = Utils.getNonEmptyString(preferences, KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT)
val authType = AccountUtils.getCredentialsType(preferences.getInt(KEY_AUTH_TYPE, AuthTypeInt.OAUTH))
val authType = preferences.getString(KEY_CREDENTIALS_TYPE, Credentials.Type.OAUTH)
val sameOAuthSigningUrl = preferences.getBoolean(KEY_SAME_OAUTH_SIGNING_URL, false)
val noVersionSuffix = preferences.getBoolean(KEY_NO_VERSION_SUFFIX, false)
val consumerKey = Utils.getNonEmptyString(preferences, KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY)
@ -411,7 +411,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
result.updateAccount(am)
Toast.makeText(this, R.string.error_already_logged_in, Toast.LENGTH_SHORT).show()
} else {
result.insertAccount(am)
result.addAccount(am)
val intent = Intent(this, HomeActivity::class.java)
//TODO refresh time lines
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
@ -768,7 +768,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
writeAuthToken(am, account)
}
fun insertAccount(am: AccountManager): Account {
fun addAccount(am: AccountManager): Account {
val account = Account(UserKey(user.screen_name, user.key.host).toString(), ACCOUNT_TYPE)
val map: MutableMap<String, String?> = HashMap()
writeAccountInfo(map)
@ -776,7 +776,9 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
for ((k, v) in map) {
userData[k] = v
}
userData[ACCOUNT_USER_DATA_POSITION] = AccountUtils.getAccounts(am).size
am.addAccountExplicitly(account, null, userData)
writeAuthToken(am, account)
return account
}
}

View File

@ -49,9 +49,9 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.BaseActivity
import org.mariotaku.twidere.adapter.ArrayAdapter
import org.mariotaku.twidere.annotation.AuthTypeInt
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts
import org.mariotaku.twidere.util.JsonSerializer
import org.mariotaku.twidere.util.MicroBlogAPIFactory
@ -66,12 +66,12 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
override fun onCheckedChanged(group: RadioGroup, checkedId: Int) {
val authType = getCheckedAuthType(checkedId)
val isOAuth = authType == AuthTypeInt.OAUTH || authType == AuthTypeInt.XAUTH
val isOAuth = authType == Credentials.Type.OAUTH || authType == Credentials.Type.XAUTH
editSameOAuthSigningUrl.visibility = if (isOAuth) View.VISIBLE else View.GONE
editConsumerKey.visibility = if (isOAuth) View.VISIBLE else View.GONE
editConsumerSecret.visibility = if (isOAuth) View.VISIBLE else View.GONE
if (!editNoVersionSuffixChanged) {
editNoVersionSuffix.isChecked = authType == AuthTypeInt.TWIP_O_MODE
editNoVersionSuffix.isChecked = authType == Credentials.Type.EMPTY
}
}
@ -110,7 +110,7 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
val consumerKey = ParseUtils.parseString(this.editConsumerKey.text)
val consumerSecret = ParseUtils.parseString(this.editConsumerSecret.text)
outState.putString(Accounts.API_URL_FORMAT, apiUrlFormat)
outState.putInt(Accounts.AUTH_TYPE, authType)
outState.putString(Accounts.AUTH_TYPE, authType)
outState.putBoolean(Accounts.SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl)
outState.putBoolean(Accounts.NO_VERSION_SUFFIX, noVersionSuffix)
outState.putString(Accounts.CONSUMER_KEY, consumerKey)
@ -145,7 +145,7 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
setContentView(R.layout.activity_api_editor)
val apiUrlFormat: String?
val authType: Int
val authType: String
val sameOAuthSigningUrl: Boolean
val noVersionSuffix: Boolean
val consumerKey: String?
@ -153,7 +153,7 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
val pref = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
val prefApiUrlFormat = Utils.getNonEmptyString(pref, KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT)
val prefAuthType = pref.getInt(KEY_AUTH_TYPE, AuthTypeInt.OAUTH)
val prefAuthType = pref.getString(KEY_CREDENTIALS_TYPE, Credentials.Type.OAUTH)
val prefSameOAuthSigningUrl = pref.getBoolean(KEY_SAME_OAUTH_SIGNING_URL, false)
val prefNoVersionSuffix = pref.getBoolean(KEY_NO_VERSION_SUFFIX, false)
val prefConsumerKey = Utils.getNonEmptyString(pref, KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY)
@ -167,7 +167,7 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
bundle = Bundle()
}
apiUrlFormat = bundle.getString(Accounts.API_URL_FORMAT, prefApiUrlFormat)?.trim()
authType = bundle.getInt(Accounts.AUTH_TYPE, prefAuthType)
authType = bundle.getString(Accounts.AUTH_TYPE, prefAuthType)
sameOAuthSigningUrl = bundle.getBoolean(Accounts.SAME_OAUTH_SIGNING_URL, prefSameOAuthSigningUrl)
noVersionSuffix = bundle.getBoolean(Accounts.NO_VERSION_SUFFIX, prefNoVersionSuffix)
consumerKey = bundle.getString(Accounts.CONSUMER_KEY, prefConsumerKey)?.trim()
@ -187,36 +187,16 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
editConsumerKey.setText(consumerKey)
editConsumerSecret.setText(consumerSecret)
oauth.isChecked = authType == AuthTypeInt.OAUTH
xauth.isChecked = authType == AuthTypeInt.XAUTH
basic.isChecked = authType == AuthTypeInt.BASIC
twipO.isChecked = authType == AuthTypeInt.TWIP_O_MODE
editAuthType.check(getAuthTypeId(authType))
if (editAuthType.checkedRadioButtonId == -1) {
oauth.isChecked = true
}
}
private fun getAuthTypeId(authType: Int): Int {
when (authType) {
AuthTypeInt.XAUTH -> {
return R.id.xauth
}
AuthTypeInt.BASIC -> {
return R.id.basic
}
AuthTypeInt.TWIP_O_MODE -> {
return R.id.twipO
}
else -> {
return R.id.oauth
}
}
}
private fun setAPIConfig(apiConfig: CustomAPIConfig) {
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editAuthType.check(getAuthTypeId(apiConfig.authType))
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
@ -307,19 +287,37 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
companion object {
fun getCheckedAuthType(checkedId: Int): Int {
@Credentials.Type
fun getCheckedAuthType(checkedId: Int): String {
when (checkedId) {
R.id.xauth -> {
return AuthTypeInt.XAUTH
return Credentials.Type.XAUTH
}
R.id.basic -> {
return AuthTypeInt.BASIC
return Credentials.Type.BASIC
}
R.id.twipO -> {
return AuthTypeInt.TWIP_O_MODE
return Credentials.Type.EMPTY
}
else -> {
return AuthTypeInt.OAUTH
return Credentials.Type.OAUTH
}
}
}
fun getAuthTypeId(authType: String): Int {
when (authType) {
Credentials.Type.XAUTH -> {
return R.id.xauth
}
Credentials.Type.BASIC -> {
return R.id.basic
}
Credentials.Type.EMPTY -> {
return R.id.twipO
}
else -> {
return R.id.oauth
}
}
}

View File

@ -0,0 +1,68 @@
package org.mariotaku.twidere.adapter
import android.support.v4.view.PagerAdapter
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
/**
* Created by mariotaku on 2016/12/9.
*/
abstract class RecyclerPagerAdapter : PagerAdapter() {
private val viewHolders: SparseArray<ViewHolder> = SparseArray()
final override fun instantiateItem(container: ViewGroup, position: Int): Any {
val itemViewType = getItemViewType(position)
val holder = onCreateViewHolder(container, position, itemViewType)
holder.position = position
holder.itemViewType = itemViewType
viewHolders.put(position, holder)
container.addView(holder.itemView)
onBindViewHolder(holder, position, holder.itemViewType)
return holder
}
final override fun destroyItem(container: ViewGroup, position: Int, obj: Any?) {
val holder = obj as ViewHolder
viewHolders.remove(position)
container.removeView(holder.itemView)
}
final override fun getItemPosition(obj: Any?): Int {
for (i in 0 until viewHolders.size()) {
val position = viewHolders.keyAt(i)
val holder = viewHolders.valueAt(i)
if (holder === obj) {
onBindViewHolder(holder, position, holder.itemViewType)
return POSITION_UNCHANGED
}
}
return POSITION_NONE
}
final override fun isViewFromObject(view: View, obj: Any): Boolean {
return (obj as ViewHolder).itemView == view
}
abstract fun onCreateViewHolder(container: ViewGroup, position: Int, itemViewType: Int): ViewHolder
abstract fun onBindViewHolder(holder: ViewHolder, position: Int, itemViewType: Int)
open fun getItemViewType(position: Int): Int = 0
fun notifyPagesChanged(invalidateCache: Boolean = true) {
if (invalidateCache) {
viewHolders.clear()
}
super.notifyDataSetChanged()
}
abstract class ViewHolder(val itemView: View) {
var position: Int = NO_POSITION
var itemViewType: Int = 0
}
companion object {
const val NO_POSITION = -1
}
}

View File

@ -47,6 +47,7 @@ import nl.komponents.kovenant.task
import org.apache.commons.lang3.ArrayUtils
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.ktextension.configure
import org.mariotaku.mediaviewer.library.MediaDownloader
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
@ -61,6 +62,7 @@ import org.mariotaku.twidere.service.RefreshService
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.content.TwidereSQLiteOpenHelper
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import org.mariotaku.twidere.util.media.TwidereMediaDownloader
import org.mariotaku.twidere.util.net.TwidereDns
import org.mariotaku.twidere.util.theme.*
import org.mariotaku.twidere.view.ProfileImageView
@ -79,6 +81,8 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
@Inject
lateinit internal var dns: TwidereDns
@Inject
lateinit internal var mediaDownloader: MediaDownloader
@Inject
lateinit internal var defaultFeatures: DefaultFeatures
@Inject
lateinit internal var externalThemeManager: ExternalThemeManager
@ -292,13 +296,15 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
stopService(Intent(this, RefreshService::class.java))
Utils.startRefreshServiceIfNeeded(this)
}
KEY_ENABLE_PROXY, KEY_PROXY_HOST, KEY_PROXY_PORT, KEY_PROXY_TYPE, KEY_PROXY_USERNAME, KEY_PROXY_PASSWORD, KEY_CONNECTION_TIMEOUT, KEY_RETRY_ON_NETWORK_ISSUE -> {
KEY_ENABLE_PROXY, KEY_PROXY_HOST, KEY_PROXY_PORT, KEY_PROXY_TYPE, KEY_PROXY_USERNAME,
KEY_PROXY_PASSWORD, KEY_CONNECTION_TIMEOUT, KEY_RETRY_ON_NETWORK_ISSUE -> {
HttpClientFactory.reloadConnectivitySettings(this)
}
KEY_DNS_SERVER, KEY_TCP_DNS_QUERY, KEY_BUILTIN_DNS_RESOLVER -> {
reloadDnsSettings()
}
KEY_CONSUMER_KEY, KEY_CONSUMER_SECRET, KEY_API_URL_FORMAT, KEY_AUTH_TYPE, KEY_SAME_OAUTH_SIGNING_URL, KEY_THUMBOR_ENABLED, KEY_THUMBOR_ADDRESS, KEY_THUMBOR_SECURITY_KEY -> {
KEY_CONSUMER_KEY, KEY_CONSUMER_SECRET, KEY_API_URL_FORMAT, KEY_CREDENTIALS_TYPE,
KEY_SAME_OAUTH_SIGNING_URL, KEY_THUMBOR_ENABLED, KEY_THUMBOR_ADDRESS, KEY_THUMBOR_SECURITY_KEY -> {
val editor = preferences.edit()
editor.putLong(KEY_API_LAST_CHANGE, System.currentTimeMillis())
editor.apply()
@ -329,6 +335,9 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
ATE.config(this, VALUE_THEME_NAME_DARK).accentColor(ThemeUtils.getOptimalAccentColor(themeColor, Color.WHITE)).coloredActionBar(false).coloredStatusBar(true).statusBarColor(Color.BLACK).commit()
ATE.config(this, null).accentColor(ThemeUtils.getOptimalAccentColor(themeColor, Color.BLACK)).coloredActionBar(false).coloredStatusBar(false).commit()
}
KEY_THUMBOR_ADDRESS, KEY_THUMBOR_ENABLED, KEY_THUMBOR_SECURITY_KEY -> {
(mediaDownloader as TwidereMediaDownloader).reloadConnectivitySettings()
}
}
}

View File

@ -5,9 +5,10 @@ import android.os.Build
import android.text.TextUtils
import org.mariotaku.kpreferences.*
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.annotation.AuthTypeInt
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CREDENTIALS_TYPE
import org.mariotaku.twidere.extension.getNonEmptyString
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials
/**
* Created by mariotaku on 16/8/25.
@ -39,7 +40,7 @@ object defaultAPIConfigKey : KPreferenceKey<CustomAPIConfig> {
override fun read(preferences: SharedPreferences): CustomAPIConfig {
val apiUrlFormat = preferences.getNonEmptyString(KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT)
val authType = preferences.getInt(KEY_AUTH_TYPE, AuthTypeInt.OAUTH)
val authType = preferences.getString(KEY_CREDENTIALS_TYPE, Credentials.Type.OAUTH)
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).trim()
@ -57,7 +58,7 @@ object defaultAPIConfigKey : KPreferenceKey<CustomAPIConfig> {
editor.remove(KEY_CONSUMER_SECRET)
}
editor.putString(KEY_API_URL_FORMAT, value.apiUrlFormat)
editor.putInt(KEY_AUTH_TYPE, value.authType)
editor.putString(KEY_CREDENTIALS_TYPE, value.credentialsType)
editor.putBoolean(KEY_SAME_OAUTH_SIGNING_URL, value.isSameOAuthUrl)
editor.putBoolean(KEY_NO_VERSION_SUFFIX, value.isNoVersionSuffix)
return true

View File

@ -44,15 +44,11 @@ import android.support.v4.app.LoaderManager.LoaderCallbacks
import android.support.v4.content.AsyncTaskLoader
import android.support.v4.content.Loader
import android.support.v4.view.MenuItemCompat
import android.support.v4.view.PagerAdapter
import android.support.v4.view.ViewPager
import android.support.v7.view.SupportMenuInflater
import android.support.v7.widget.ActionMenuView.OnMenuItemClickListener
import android.support.v7.widget.FixedLinearLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView.ViewHolder
import android.support.v7.widget.RecyclerViewAccessor
import android.util.SparseArray
import android.view.*
import android.view.View.OnClickListener
import android.view.animation.DecelerateInterpolator
@ -65,6 +61,7 @@ import org.mariotaku.ktextension.setMenuItemTitle
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.*
import org.mariotaku.twidere.adapter.RecyclerPagerAdapter
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.annotation.CustomTabType
import org.mariotaku.twidere.annotation.Referral
@ -584,7 +581,9 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
return TwidereViewUtils.hitView(e.rawX, e.rawY, accountsSelector)
}
internal class AccountProfileImageViewHolder(val adapter: AccountSelectorAdapter, itemView: View) : ViewHolder(itemView), OnClickListener {
internal class AccountSpaceViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView)
internal class AccountProfileImageViewHolder(val adapter: AccountSelectorAdapter, itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView), OnClickListener {
val iconView: ShapedImageView
@ -602,9 +601,8 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
internal class AccountSelectorAdapter(
private val inflater: LayoutInflater,
private val fragment: AccountsDashboardFragment
) : PagerAdapter() {
) : RecyclerPagerAdapter() {
private val mediaLoader: MediaLoaderWrapper
private val viewHolders: SparseArray<AccountProfileImageViewHolder> = SparseArray()
var accounts: Array<AccountDetails>? = null
set(value) {
@ -627,7 +625,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
} else {
field = null
}
notifyDataSetChanged()
notifyPagesChanged(invalidateCache = true)
}
init {
@ -635,7 +633,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
}
fun getAdapterAccount(position: Int): AccountDetails? {
return accounts?.getOrNull(position + 1)
return accounts?.getOrNull(position - accountStart + 1)
}
var selectedAccount: AccountDetails?
@ -648,58 +646,63 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
swap(from, to)
}
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val view = inflater.inflate(R.layout.adapter_item_dashboard_account, container, false)
container.addView(view)
val holder = AccountProfileImageViewHolder(this, view)
RecyclerViewAccessor.setLayoutPosition(holder, position)
viewHolders.put(position, holder)
val account = getAdapterAccount(position)!!
bindHolder(holder, account)
return holder
}
val ITEM_VIEW_TYPE_SPACE = 1
val ITEM_VIEW_TYPE_ICON = 2
override fun getItemPosition(obj: Any?): Int {
for (i in 0 until viewHolders.size()) {
val holder = viewHolders.valueAt(i)
if (holder === obj) {
val account = getAdapterAccount(viewHolders.keyAt(i))!!
bindHolder(holder, account)
break
override fun onCreateViewHolder(container: ViewGroup, position: Int, itemViewType: Int): ViewHolder {
when (itemViewType) {
ITEM_VIEW_TYPE_SPACE -> {
val view = inflater.inflate(R.layout.adapter_item_dashboard_account_space, container, false)
return AccountSpaceViewHolder(view)
}
ITEM_VIEW_TYPE_ICON -> {
val view = inflater.inflate(R.layout.adapter_item_dashboard_account, container, false)
return AccountProfileImageViewHolder(this, view)
}
}
return POSITION_UNCHANGED
throw UnsupportedOperationException()
}
private fun bindHolder(holder: AccountProfileImageViewHolder, account: AccountDetails) {
holder.iconView.setBorderColor(account.color)
if (holder.iconView.tag != account && holder.iconView.drawable == null) {
mediaLoader.displayDashboardProfileImage(holder.iconView, account, null)
override fun onBindViewHolder(holder: ViewHolder, position: Int, itemViewType: Int) {
when (itemViewType) {
ITEM_VIEW_TYPE_ICON -> {
val account = getAdapterAccount(position)!!
holder as AccountProfileImageViewHolder
holder.iconView.setBorderColor(account.color)
if (holder.iconView.tag != account && holder.iconView.drawable == null) {
mediaLoader.displayDashboardProfileImage(holder.iconView, account, null)
}
holder.iconView.tag = account
}
}
holder.iconView.tag = account
}
override fun destroyItem(container: ViewGroup, position: Int, obj: Any?) {
val holder = obj as AccountProfileImageViewHolder
viewHolders.remove(position)
container.removeView(holder.itemView)
}
override fun isViewFromObject(view: View?, obj: Any?): Boolean {
return view == (obj as AccountProfileImageViewHolder).itemView
override fun getItemViewType(position: Int): Int {
if (position < accountStart) {
return ITEM_VIEW_TYPE_SPACE
}
return ITEM_VIEW_TYPE_ICON
}
override fun getCount(): Int {
if (accounts == null || accounts!!.isEmpty()) return 0
return accounts!!.size - 1
return Math.max(3, accountsCount)
}
val accountStart: Int
get() = Math.max(0, 3 - accountsCount)
val accountsCount: Int
get() {
val accounts = this.accounts ?: return 0
return Math.max(0, accounts.size - 1)
}
override fun getPageWidth(position: Int): Float {
return 1f / AccountsSelectorTransformer.selectorAccountsCount
}
fun dispatchItemSelected(holder: AccountProfileImageViewHolder) {
fragment.onAccountSelected(holder, getAdapterAccount(holder.layoutPosition)!!)
fragment.onAccountSelected(holder, getAdapterAccount(holder.position)!!)
}
private fun swap(from: AccountDetails, to: AccountDetails) {
@ -710,7 +713,7 @@ class AccountsDashboardFragment : BaseSupportFragment(), LoaderCallbacks<Account
val temp = accounts[toIdx]
accounts[toIdx] = accounts[fromIdx]
accounts[fromIdx] = temp
notifyDataSetChanged()
notifyPagesChanged(invalidateCache = false)
}
}

View File

@ -1,6 +1,7 @@
package org.mariotaku.twidere.loader
import android.accounts.AccountManager
import android.accounts.OnAccountsUpdateListener
import android.content.Context
import android.support.v4.content.AsyncTaskLoader
import org.mariotaku.twidere.model.AccountDetails
@ -14,6 +15,7 @@ class AccountDetailsLoader(
val filter: (AccountDetails.() -> Boolean)? = null
) : AsyncTaskLoader<List<AccountDetails>>(context) {
private val am: AccountManager
private var accountUpdateListener: OnAccountsUpdateListener? = null
init {
am = AccountManager.get(context)
@ -25,7 +27,28 @@ class AccountDetailsLoader(
}.sortedBy(AccountDetails::position)
}
override fun onReset() {
super.onReset()
onStopLoading()
if (accountUpdateListener != null) {
am.removeOnAccountsUpdatedListener(accountUpdateListener)
accountUpdateListener = null
}
}
override fun onStartLoading() {
forceLoad()
if (accountUpdateListener == null) {
accountUpdateListener = OnAccountsUpdateListener {
onContentChanged()
}
am.addOnAccountsUpdatedListener(accountUpdateListener, null, true)
}
if (takeContentChanged()) {
forceLoad()
}
}
override fun onStopLoading() {
cancelLoad()
}
}

View File

@ -66,7 +66,7 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) {
}
}
fun toHexColor(@ColorInt color: Int) = String.format(Locale.ROOT, "#%6X", color)
fun toHexColor(@ColorInt color: Int) = String.format(Locale.ROOT, "#%06X", color)
@Suppress("deprecation")
private fun ParcelableCredentials.toCredentials(): Credentials {

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/element_spacing_small"
tools:layout_height="48dp">
<org.mariotaku.twidere.view.SquareSpace
android:id="@android:id/icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>

View File

@ -80,6 +80,7 @@
android:layout_toRightOf="@+id/hasPrevAccountIndicator"
android:layout_toStartOf="@+id/hasNextAccountIndicator"
android:focusable="false"
android:overScrollMode="never"
tools:listitem="@layout/adapter_item_dashboard_account"
tools:paddingStart="@dimen/element_spacing_large"
tools:visibility="visible"/>