updated icon
This commit is contained in:
Mariotaku Lee 2016-12-12 13:42:27 +08:00
parent 67ffc5cc64
commit ab71d5c60f
32 changed files with 608 additions and 714 deletions

View File

@ -220,4 +220,5 @@ public interface IntentConstants {
String EXTRA_SELECT_ONLY_ITEM = "select_only_item";
String EXTRA_OBJECT = "object";
String EXTRA_SIMPLE_LAYOUT = "simple_layout";
String EXTRA_API_CONFIG = "api_config";
}

View File

@ -19,10 +19,12 @@
package org.mariotaku.twidere.constant;
import org.mariotaku.twidere.TwidereConstants;
import org.mariotaku.twidere.annotation.Preference;
import org.mariotaku.twidere.model.account.cred.Credentials;
import static org.mariotaku.twidere.TwidereConstants.DEFAULT_TWITTER_API_URL_FORMAT;
import static org.mariotaku.twidere.TwidereConstants.TWITTER_CONSUMER_KEY;
import static org.mariotaku.twidere.TwidereConstants.TWITTER_CONSUMER_SECRET;
import static org.mariotaku.twidere.annotation.PreferenceType.BOOLEAN;
import static org.mariotaku.twidere.annotation.PreferenceType.INT;
import static org.mariotaku.twidere.annotation.PreferenceType.LONG;
@ -231,7 +233,7 @@ public interface SharedPreferenceConstants {
String KEY_PRELOAD_WIFI_ONLY = "preload_wifi_only";
@Preference(type = BOOLEAN)
String KEY_NO_CLOSE_AFTER_TWEET_SENT = "no_close_after_tweet_sent";
@Preference(type = STRING, hasDefault = false)
@Preference(type = STRING, hasDefault = true, defaultString = DEFAULT_TWITTER_API_URL_FORMAT)
String KEY_API_URL_FORMAT = "api_url_format";
@Preference(type = BOOLEAN, hasDefault = true, defaultBoolean = true)
String KEY_SAME_OAUTH_SIGNING_URL = "same_oauth_signing_url";
@ -239,9 +241,9 @@ public interface SharedPreferenceConstants {
String KEY_NO_VERSION_SUFFIX = "no_version_suffix";
@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)
@Preference(type = STRING, hasDefault = true, defaultString = TWITTER_CONSUMER_KEY)
String KEY_CONSUMER_KEY = "consumer_key";
@Preference(type = STRING, hasDefault = true, defaultString = TwidereConstants.TWITTER_CONSUMER_SECRET)
@Preference(type = STRING, hasDefault = true, defaultString = TWITTER_CONSUMER_SECRET)
String KEY_CONSUMER_SECRET = "consumer_secret";
String KEY_SETTINGS_WIZARD_COMPLETED = "settings_wizard_completed";
String KEY_CONSUMER_KEY_SECRET_SET = "consumer_key_secret_set";

View File

@ -1,8 +0,0 @@
package org.mariotaku.twidere.model.account.conf;
/**
* Created by mariotaku on 2016/12/2.
*/
public class APIConfiguration {
}

View File

@ -86,12 +86,6 @@ public interface TwidereDataStore {
*/
String AUTH_TYPE = "auth_type";
/**
* Password of the account. (It will not stored)<br>
* Type: TEXT
*/
String PASSWORD = "password";
String BASIC_AUTH_USERNAME = "basic_auth_username";
/**
@ -143,9 +137,6 @@ public interface TwidereDataStore {
String ACCOUNT_USER = "account_user";
String[] COLUMNS_NO_CREDENTIALS = {_ID, NAME, SCREEN_NAME, ACCOUNT_KEY, PROFILE_IMAGE_URL,
PROFILE_BANNER_URL, COLOR, IS_ACTIVATED, SORT_POSITION, ACCOUNT_TYPE, ACCOUNT_USER};
String[] COLUMNS = {_ID, NAME, SCREEN_NAME, ACCOUNT_KEY, AUTH_TYPE, BASIC_AUTH_USERNAME,
BASIC_AUTH_PASSWORD, OAUTH_TOKEN, OAUTH_TOKEN_SECRET, CONSUMER_KEY, CONSUMER_SECRET,
API_URL_FORMAT, SAME_OAUTH_SIGNING_URL, NO_VERSION_SUFFIX, PROFILE_IMAGE_URL,

View File

@ -35,8 +35,8 @@ android {
applicationId "org.mariotaku.twidere"
minSdkVersion 14
targetSdkVersion 25
versionCode 230
versionName '3.3.12'
versionCode 231
versionName '3.3.13'
multiDexEnabled true
buildConfigField 'boolean', 'LEAK_CANARY_ENABLED', 'Boolean.parseBoolean("true")'

View File

@ -86,6 +86,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:resizeableActivity="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Twidere.NoActionBar"
tools:ignore="UnusedAttribute">

View File

@ -1,304 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;
import org.attoparser.ParseException;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.TwitterOAuth;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
import org.mariotaku.restfu.oauth.OAuthAuthorization;
import org.mariotaku.restfu.oauth.OAuthToken;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.extension.CredentialsExtensionsKt;
import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator;
import org.mariotaku.twidere.util.webkit.DefaultWebViewClient;
import java.io.IOException;
import java.io.StringReader;
import java.util.Set;
import static android.text.TextUtils.isEmpty;
@SuppressLint("SetJavaScriptEnabled")
public class BrowserSignInActivity extends BaseActivity {
private static final String INJECT_CONTENT = "javascript:window.injector.processHTML('<head>'+document.getElementsByTagName('html')[0].innerHTML+'</head>');";
private WebView mWebView;
private View mProgressContainer;
private WebSettings mWebSettings;
private OAuthToken mRequestToken;
private GetRequestTokenTask mTask;
@Override
public void onContentChanged() {
super.onContentChanged();
mWebView = (WebView) findViewById(R.id.webview);
mProgressContainer = findViewById(R.id.progress_container);
}
@Override
public void onDestroy() {
getLoaderManager().destroyLoader(0);
super.onDestroy();
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
finish();
return true;
}
}
return super.onOptionsItemSelected(item);
}
@SuppressLint("AddJavascriptInterface")
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_browser_sign_in);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
//noinspection deprecation
CookieManager.getInstance().removeAllCookie();
} else {
CookieManager.getInstance().removeAllCookies(null);
}
mWebView.setWebViewClient(new AuthorizationWebViewClient(this));
mWebView.setVerticalScrollBarEnabled(false);
mWebView.addJavascriptInterface(new InjectorJavaScriptInterface(this), "injector");
mWebSettings = mWebView.getSettings();
mWebSettings.setLoadsImagesAutomatically(true);
mWebSettings.setJavaScriptEnabled(true);
mWebSettings.setBlockNetworkImage(false);
mWebSettings.setSaveFormData(true);
getRequestToken();
}
private void getRequestToken() {
if (mRequestToken != null || mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING)
return;
mTask = new GetRequestTokenTask(this);
AsyncTaskUtils.executeTask(mTask);
}
private void loadUrl(final String url) {
if (mWebView == null) return;
mWebView.loadUrl(url);
}
private String readOAuthPin(final String html) {
try {
OAuthPasswordAuthenticator.OAuthPinData data = new OAuthPasswordAuthenticator.OAuthPinData();
OAuthPasswordAuthenticator.Companion.readOAuthPINFromHtml(new StringReader(html), data);
return data.getOauthPin();
} catch (final ParseException | IOException e) {
Log.w(LOGTAG, e);
}
return null;
}
private void setLoadProgressShown(final boolean shown) {
mProgressContainer.setVisibility(shown ? View.VISIBLE : View.GONE);
}
private void setRequestToken(final OAuthToken token) {
mRequestToken = token;
}
static class AuthorizationWebViewClient extends DefaultWebViewClient {
AuthorizationWebViewClient(final BrowserSignInActivity activity) {
super(activity);
}
@Override
public void onPageFinished(final WebView view, final String url) {
super.onPageFinished(view, url);
view.loadUrl(INJECT_CONTENT);
final BrowserSignInActivity activity = (BrowserSignInActivity) getActivity();
activity.setLoadProgressShown(false);
Uri uri = Uri.parse(url);
// Hack for fanfou
if ("fanfou.com".equals(uri.getHost())) {
final String path = uri.getPath();
final Set<String> paramNames = uri.getQueryParameterNames();
if ("/oauth/authorize".equals(path) && paramNames.contains("oauth_callback")) {
// Sign in successful response.
final OAuthToken requestToken = activity.mRequestToken;
if (requestToken != null) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.getOauthToken());
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.getOauthTokenSecret());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
}
}
}
@Override
public void onPageStarted(final WebView view, final String url, final Bitmap favicon) {
super.onPageStarted(view, url, favicon);
((BrowserSignInActivity) getActivity()).setLoadProgressShown(true);
}
@SuppressWarnings("deprecation")
@Override
public void onReceivedError(final WebView view, final int errorCode, final String description,
final String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
final Activity activity = getActivity();
Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
activity.finish();
}
@Override
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
final Uri uri = Uri.parse(url);
if (url.startsWith(OAUTH_CALLBACK_URL)) {
final String oauthVerifier = uri.getQueryParameter(EXTRA_OAUTH_VERIFIER);
final BrowserSignInActivity activity = (BrowserSignInActivity) getActivity();
final OAuthToken requestToken = activity.mRequestToken;
if (oauthVerifier != null && requestToken != null) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_OAUTH_VERIFIER, oauthVerifier);
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.getOauthToken());
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.getOauthTokenSecret());
activity.setResult(RESULT_OK, intent);
activity.finish();
}
return true;
}
return false;
}
}
static class GetRequestTokenTask extends AsyncTask<Object, Object, SingleResponse<OAuthToken>> {
private final String mConsumerKey, mConsumerSecret;
private final BrowserSignInActivity mActivity;
private final String mAPIUrlFormat;
private final boolean mSameOAuthSigningUrl;
public GetRequestTokenTask(final BrowserSignInActivity activity) {
mActivity = activity;
final Intent intent = activity.getIntent();
mConsumerKey = intent.getStringExtra(Accounts.CONSUMER_KEY);
mConsumerSecret = intent.getStringExtra(Accounts.CONSUMER_SECRET);
mAPIUrlFormat = intent.getStringExtra(Accounts.API_URL_FORMAT);
mSameOAuthSigningUrl = intent.getBooleanExtra(Accounts.SAME_OAUTH_SIGNING_URL, true);
}
@Override
protected SingleResponse<OAuthToken> doInBackground(final Object... params) {
if (isEmpty(mConsumerKey) || isEmpty(mConsumerSecret)) {
return new SingleResponse<>();
}
try {
final Endpoint endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(mAPIUrlFormat,
mSameOAuthSigningUrl);
final Authorization auth = new OAuthAuthorization(mConsumerKey, mConsumerSecret);
final TwitterOAuth oauth = CredentialsExtensionsKt.newMicroBlogInstance(mActivity, endpoint,
auth, true, null, TwitterOAuth.class);
return new SingleResponse<>(oauth.getRequestToken(OAUTH_CALLBACK_OOB), null, new Bundle());
} catch (final MicroBlogException e) {
return new SingleResponse<>(null, e, new Bundle());
}
}
@Override
protected void onPostExecute(final SingleResponse<OAuthToken> result) {
mActivity.setLoadProgressShown(false);
if (result.hasData()) {
final OAuthToken token = result.getData();
assert token != null;
mActivity.setRequestToken(token);
final Endpoint endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(mAPIUrlFormat, true);
mActivity.loadUrl(endpoint.construct("/oauth/authorize", new String[]{"oauth_token", token.getOauthToken()}));
} else {
if (BuildConfig.DEBUG && result.hasException()) {
Log.w(LOGTAG, "Exception while browser sign in", result.getException());
}
if (!mActivity.isFinishing()) {
Toast.makeText(mActivity, R.string.error_occurred, Toast.LENGTH_SHORT).show();
mActivity.finish();
}
}
}
@Override
protected void onPreExecute() {
mActivity.setLoadProgressShown(true);
}
}
static class InjectorJavaScriptInterface {
private final BrowserSignInActivity mActivity;
InjectorJavaScriptInterface(final BrowserSignInActivity activity) {
mActivity = activity;
}
@JavascriptInterface
public void processHTML(final String html) {
final String oauthVerifier = mActivity.readOAuthPin(html);
final OAuthToken requestToken = mActivity.mRequestToken;
if (oauthVerifier != null && requestToken != null) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_OAUTH_VERIFIER, oauthVerifier);
intent.putExtra(EXTRA_REQUEST_TOKEN, requestToken.getOauthToken());
intent.putExtra(EXTRA_REQUEST_TOKEN_SECRET, requestToken.getOauthTokenSecret());
mActivity.setResult(RESULT_OK, intent);
mActivity.finish();
}
}
}
}

View File

@ -11,11 +11,26 @@ import android.support.v7.preference.PreferenceDialogFragmentCompat;
import android.view.View;
import android.view.Window;
import org.mariotaku.kpreferences.KPreferences;
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper;
import javax.inject.Inject;
/**
* Created by mariotaku on 16/3/15.
*/
public abstract class ThemedPreferenceDialogFragmentCompat extends PreferenceDialogFragmentCompat {
@Inject
@NonNull
protected KPreferences kPreferences;
@Override
public void onAttach(Context context) {
super.onAttach(context);
GeneralComponentHelper.build(context).inject(this);
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {

View File

@ -3,10 +3,13 @@ package org.mariotaku.twidere.model;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import com.bluelinelabs.logansquare.annotation.JsonField;
import com.bluelinelabs.logansquare.annotation.JsonObject;
import com.hannesdorfmann.parcelableplease.annotation.ParcelablePlease;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.model.account.cred.Credentials;
@ -25,8 +28,9 @@ import static org.mariotaku.twidere.TwidereConstants.TWITTER_CONSUMER_SECRET;
/**
* Created by mariotaku on 16/3/12.
*/
@ParcelablePlease
@JsonObject
public final class CustomAPIConfig {
public final class CustomAPIConfig implements Parcelable {
@JsonField(name = "name")
String name;
@ -46,7 +50,7 @@ public final class CustomAPIConfig {
@JsonField(name = "consumer_secret")
String consumerSecret;
CustomAPIConfig() {
public CustomAPIConfig() {
}
public CustomAPIConfig(String name, String apiUrlFormat, String credentialsType, boolean sameOAuthUrl,
@ -98,6 +102,52 @@ public final class CustomAPIConfig {
return consumerSecret;
}
public void setApiUrlFormat(String apiUrlFormat) {
this.apiUrlFormat = apiUrlFormat;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
public void setCredentialsType(String credentialsType) {
this.credentialsType = credentialsType;
}
public void setSameOAuthUrl(boolean sameOAuthUrl) {
this.sameOAuthUrl = sameOAuthUrl;
}
public void setNoVersionSuffix(boolean noVersionSuffix) {
this.noVersionSuffix = noVersionSuffix;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
CustomAPIConfigParcelablePlease.writeToParcel(this, dest, flags);
}
public static final Creator<CustomAPIConfig> CREATOR = new Creator<CustomAPIConfig>() {
public CustomAPIConfig createFromParcel(Parcel source) {
CustomAPIConfig target = new CustomAPIConfig();
CustomAPIConfigParcelablePlease.readFromParcel(target, source);
return target;
}
public CustomAPIConfig[] newArray(int size) {
return new CustomAPIConfig[size];
}
};
@NonNull
public static List<CustomAPIConfig> listDefault(@NonNull Context context) {
final AssetManager assets = context.getAssets();
@ -114,10 +164,13 @@ public final class CustomAPIConfig {
}
}
public static List<CustomAPIConfig> listBuiltin(@NonNull Context context) {
return Collections.singletonList(new CustomAPIConfig(context.getString(R.string.provider_default),
public static CustomAPIConfig builtin(@NonNull Context context) {
return new CustomAPIConfig(context.getString(R.string.provider_default),
DEFAULT_TWITTER_API_URL_FORMAT, Credentials.Type.OAUTH, true, false,
TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET));
TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET);
}
public static List<CustomAPIConfig> listBuiltin(@NonNull Context context) {
return Collections.singletonList(builtin(context));
}
}

View File

@ -1,210 +0,0 @@
/*
* 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.preference;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.preference.DialogPreference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
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.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;
import static org.mariotaku.twidere.util.Utils.trim;
public class DefaultAPIPreference extends DialogPreference implements Constants, IDialogPreference {
public DefaultAPIPreference(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.dialogPreferenceStyle);
}
public DefaultAPIPreference(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
setDialogLayoutResource(R.layout.layout_api_editor);
}
@Override
public void displayDialog(PreferenceFragmentCompat fragment) {
DefaultAPIPreferenceDialogFragment df = DefaultAPIPreferenceDialogFragment.newInstance(getKey());
df.setTargetFragment(fragment, 0);
df.show(fragment.getFragmentManager(), getKey());
}
public static final class DefaultAPIPreferenceDialogFragment extends ThemedPreferenceDialogFragmentCompat {
public static DefaultAPIPreferenceDialogFragment newInstance(String key) {
final DefaultAPIPreferenceDialogFragment df = new DefaultAPIPreferenceDialogFragment();
final Bundle args = new Bundle();
args.putString(ARG_KEY, key);
df.setArguments(args);
return df;
}
private EditText mEditAPIUrlFormat;
private CheckBox mEditSameOAuthSigningUrl, mEditNoVersionSuffix;
private EditText mEditConsumerKey, mEditConsumerSecret;
private RadioGroup mEditAuthType;
private RadioButton mButtonOAuth, mButtonxAuth, mButtonBasic, mButtonTwipOMode;
private View mAPIFormatHelpButton;
private boolean mEditNoVersionSuffixChanged;
@NonNull
@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
final DialogPreference preference = getPreference();
final Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface dialog) {
final Dialog editDialog = (Dialog) dialog;
mEditAPIUrlFormat = (EditText) editDialog.findViewById(R.id.editApiUrlFormat);
mEditAuthType = (RadioGroup) editDialog.findViewById(R.id.editAuthType);
mButtonOAuth = (RadioButton) editDialog.findViewById(R.id.oauth);
mButtonxAuth = (RadioButton) editDialog.findViewById(R.id.xauth);
mButtonBasic = (RadioButton) editDialog.findViewById(R.id.basic);
mButtonTwipOMode = (RadioButton) editDialog.findViewById(R.id.twipO);
mEditSameOAuthSigningUrl = (CheckBox) editDialog.findViewById(R.id.editSameOAuthSigningUrl);
mEditNoVersionSuffix = (CheckBox) editDialog.findViewById(R.id.editNoVersionSuffix);
mEditConsumerKey = (EditText) editDialog.findViewById(R.id.editConsumerKey);
mEditConsumerSecret = (EditText) editDialog.findViewById(R.id.editConsumerSecret);
mAPIFormatHelpButton = editDialog.findViewById(R.id.apiUrlFormatHelp);
mEditNoVersionSuffix.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mEditNoVersionSuffixChanged = true;
}
});
mEditAuthType.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
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(Credentials.Type.EMPTY.equals(authType));
}
}
});
mAPIFormatHelpButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getContext(), R.string.api_url_format_help, Toast.LENGTH_LONG).show();
}
});
if (savedInstanceState != null) {
final String apiUrlFormat = savedInstanceState.getString(Accounts.API_URL_FORMAT);
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));
final String consumerSecret = trim(savedInstanceState.getString(Accounts.CONSUMER_SECRET));
setValues(apiUrlFormat, authType, sameOAuthSigningUrl, noVersionSuffix, consumerKey, consumerSecret);
} else {
final SharedPreferences preferences = preference.getSharedPreferences();
final String apiUrlFormat = preferences.getString(KEY_API_URL_FORMAT, DEFAULT_TWITTER_API_URL_FORMAT);
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));
final String consumerSecret = trim(preferences.getString(KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET));
setValues(apiUrlFormat, authType, sameOAuthSigningUrl, noVersionSuffix, consumerKey, consumerSecret);
}
}
});
return dialog;
}
@Override
public void onDialogClosed(boolean positiveResult) {
if (!positiveResult) return;
DefaultAPIPreference preference = (DefaultAPIPreference) getPreference();
final SharedPreferences preferences = preference.getSharedPreferences();
final String apiUrlFormat = ParseUtils.parseString(mEditAPIUrlFormat.getText());
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());
final String consumerSecret = ParseUtils.parseString(mEditConsumerSecret.getText());
final SharedPreferences.Editor editor = preferences.edit();
if (!TextUtils.isEmpty(consumerKey) && !TextUtils.isEmpty(consumerSecret)) {
editor.putString(KEY_CONSUMER_KEY, consumerKey);
editor.putString(KEY_CONSUMER_SECRET, consumerSecret);
} else {
editor.remove(KEY_CONSUMER_KEY);
editor.remove(KEY_CONSUMER_SECRET);
}
editor.putString(KEY_API_URL_FORMAT, apiUrlFormat);
editor.putString(KEY_CREDENTIALS_TYPE, authType);
editor.putBoolean(KEY_SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl);
editor.putBoolean(KEY_NO_VERSION_SUFFIX, noVersionSuffix);
editor.apply();
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(Accounts.API_URL_FORMAT, ParseUtils.parseString(mEditAPIUrlFormat.getText()));
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 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);
mEditAuthType.check(APIEditorActivity.Companion.getAuthTypeId(authType));
if (mEditAuthType.getCheckedRadioButtonId() == -1) {
mButtonOAuth.setChecked(true);
}
}
}
}

View File

@ -44,6 +44,7 @@ import com.afollestad.appthemeengine.Config
import com.afollestad.appthemeengine.customizers.ATEStatusBarCustomizer
import com.afollestad.appthemeengine.customizers.ATEToolbarCustomizer
import com.squareup.otto.Bus
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
@ -76,6 +77,8 @@ open class BaseActivity : ATEActivity(), Constants, IExtendedActivity, IThemedAc
@Inject
lateinit var preferences: SharedPreferencesWrapper
@Inject
lateinit var kPreferences: KPreferences
@Inject
lateinit var notificationManager: NotificationManagerWrapper
@Inject
lateinit var mediaLoader: MediaLoaderWrapper

View File

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

View File

@ -40,7 +40,6 @@ import android.text.InputType
import android.text.TextUtils
import android.text.TextWatcher
import android.util.Log
import android.util.Pair
import android.view.Menu
import android.view.MenuItem
import android.view.View
@ -52,7 +51,6 @@ import android.widget.Toast
import com.bluelinelabs.logansquare.LoganSquare
import com.rengwuxian.materialedittext.MaterialEditText
import kotlinx.android.synthetic.main.activity_sign_in.*
import org.mariotaku.ktextension.set
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.TwitterOAuth
@ -68,10 +66,13 @@ 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.constant.IntentConstants.EXTRA_API_CONFIG
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_CREDENTIALS_TYPE
import org.mariotaku.twidere.constant.defaultAPIConfigKey
import org.mariotaku.twidere.extension.newMicroBlogInstance
import org.mariotaku.twidere.fragment.BaseDialogFragment
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.ParcelableUser
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
@ -84,23 +85,15 @@ import org.mariotaku.twidere.model.account.cred.OAuthCredentials
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.model.util.ParcelableUserUtils
import org.mariotaku.twidere.model.util.UserKeyUtils
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator.*
import org.mariotaku.twidere.util.view.ConsumerKeySecretValidator
import java.lang.ref.WeakReference
import java.util.*
class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
private var apiUrlFormat: String? = null
@Credentials.Type
private var authType: String = Credentials.Type.EMPTY
private var consumerKey: String? = null
private var consumerSecret: String? = null
private lateinit var apiConfig: CustomAPIConfig
private var apiChangeTimestamp: Long = 0
private var sameOAuthSigningUrl: Boolean = false
private var noVersionSuffix: Boolean = false
private var signInTask: AbstractSignInTask? = null
private var accountAuthenticatorResponse: AccountAuthenticatorResponse? = null
@ -114,15 +107,13 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
setContentView(R.layout.activity_sign_in)
if (savedInstanceState != null) {
apiUrlFormat = savedInstanceState.getString(Accounts.API_URL_FORMAT)
authType = savedInstanceState.getString(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()
apiConfig = savedInstanceState.getParcelable(EXTRA_API_CONFIG)
apiChangeTimestamp = savedInstanceState.getLong(EXTRA_API_LAST_CHANGE)
} else {
apiConfig = kPreferences[defaultAPIConfigKey]
}
val isTwipOMode = authType == Credentials.Type.EMPTY
val isTwipOMode = apiConfig.credentialsType == Credentials.Type.EMPTY
usernamePasswordContainer.visibility = if (isTwipOMode) View.GONE else View.VISIBLE
signInSignUpContainer.orientation = if (isTwipOMode) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
@ -166,12 +157,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
when (requestCode) {
REQUEST_EDIT_API -> {
if (resultCode == Activity.RESULT_OK) {
apiUrlFormat = data!!.getStringExtra(Accounts.API_URL_FORMAT)
authType = data.getStringExtra(Accounts.AUTH_TYPE) ?: Credentials.Type.OAUTH
sameOAuthSigningUrl = data.getBooleanExtra(Accounts.SAME_OAUTH_SIGNING_URL, false)
noVersionSuffix = data.getBooleanExtra(Accounts.NO_VERSION_SUFFIX, false)
consumerKey = data.getStringExtra(Accounts.CONSUMER_KEY)
consumerSecret = data.getStringExtra(Accounts.CONSUMER_SECRET)
apiConfig = data!!.getParcelableExtra(EXTRA_API_CONFIG)
updateSignInType()
}
setSignInButton()
@ -208,7 +194,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
}
internal fun updateSignInType() {
when (authType) {
when (apiConfig.credentialsType) {
Credentials.Type.XAUTH, Credentials.Type.BASIC -> {
usernamePasswordContainer.visibility = View.VISIBLE
signInSignUpContainer.orientation = LinearLayout.HORIZONTAL
@ -267,12 +253,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
return false
setDefaultAPI()
val intent = Intent(this, APIEditorActivity::class.java)
intent.putExtra(Accounts.API_URL_FORMAT, apiUrlFormat)
intent.putExtra(Accounts.AUTH_TYPE, authType)
intent.putExtra(Accounts.SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl)
intent.putExtra(Accounts.NO_VERSION_SUFFIX, noVersionSuffix)
intent.putExtra(Accounts.CONSUMER_KEY, consumerKey)
intent.putExtra(Accounts.CONSUMER_SECRET, consumerSecret)
intent.putExtra(EXTRA_API_CONFIG, apiConfig)
startActivityForResult(intent, REQUEST_EDIT_API)
}
}
@ -280,13 +261,10 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
}
internal fun openBrowserLogin(): Boolean {
if (authType != Credentials.Type.OAUTH || signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
if (apiConfig.credentialsType != Credentials.Type.OAUTH || signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING)
return true
val intent = Intent(this, BrowserSignInActivity::class.java)
intent.putExtra(Accounts.CONSUMER_KEY, consumerKey)
intent.putExtra(Accounts.CONSUMER_SECRET, consumerSecret)
intent.putExtra(Accounts.API_URL_FORMAT, apiUrlFormat)
intent.putExtra(Accounts.SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl)
intent.putExtra(EXTRA_API_CONFIG, apiConfig)
startActivityForResult(intent, REQUEST_BROWSER_SIGN_IN)
return false
}
@ -294,7 +272,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
val itemBrowser = menu.findItem(R.id.open_in_browser)
if (itemBrowser != null) {
val is_oauth = authType == Credentials.Type.OAUTH
val is_oauth = apiConfig.credentialsType == Credentials.Type.OAUTH
itemBrowser.isVisible = is_oauth
itemBrowser.isEnabled = is_oauth
}
@ -302,13 +280,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
}
public override fun onSaveInstanceState(outState: Bundle) {
setDefaultAPI()
outState.putString(Accounts.API_URL_FORMAT, apiUrlFormat)
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)
outState.putString(Accounts.CONSUMER_SECRET, consumerSecret)
outState.putParcelable(EXTRA_API_CONFIG, apiConfig)
outState.putLong(EXTRA_API_LAST_CHANGE, apiChangeTimestamp)
super.onSaveInstanceState(outState)
}
@ -322,33 +294,32 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING) {
signInTask!!.cancel(true)
}
setDefaultAPI()
if (authType == Credentials.Type.OAUTH && editUsername.length() <= 0) {
if (apiConfig.credentialsType == Credentials.Type.OAUTH && editUsername.length() <= 0) {
openBrowserLogin()
return
}
val consumerKey = MicroBlogAPIFactory.getOAuthToken(this.consumerKey, consumerSecret)
val apiUrlFormat = if (TextUtils.isEmpty(this.apiUrlFormat)) DEFAULT_TWITTER_API_URL_FORMAT else this.apiUrlFormat!!
val consumerKey = MicroBlogAPIFactory.getOAuthToken(apiConfig.consumerKey, apiConfig.consumerSecret)
val apiUrlFormat = apiConfig.apiUrlFormat ?: DEFAULT_TWITTER_API_URL_FORMAT
val username = editUsername.text.toString()
val password = editPassword.text.toString()
signInTask = SignInTask(this, username, password, authType, consumerKey, apiUrlFormat,
sameOAuthSigningUrl, noVersionSuffix)
signInTask = SignInTask(this, username, password, apiConfig.credentialsType, consumerKey, apiUrlFormat,
apiConfig.isSameOAuthUrl, apiConfig.isNoVersionSuffix)
AsyncTaskUtils.executeTask<AbstractSignInTask, Any>(signInTask)
}
private fun doBrowserLogin(intent: Intent?) {
if (intent == null) return
if (signInTask != null && signInTask!!.status == AsyncTask.Status.RUNNING) {
signInTask!!.cancel(true)
if (signInTask?.status == AsyncTask.Status.RUNNING) {
signInTask?.cancel(true)
}
setDefaultAPI()
val verifier = intent.getStringExtra(EXTRA_OAUTH_VERIFIER)
val consumerKey = MicroBlogAPIFactory.getOAuthToken(this.consumerKey, consumerSecret)
val consumerKey = MicroBlogAPIFactory.getOAuthToken(apiConfig.consumerKey, apiConfig.consumerSecret)
val requestToken = OAuthToken(intent.getStringExtra(EXTRA_REQUEST_TOKEN),
intent.getStringExtra(EXTRA_REQUEST_TOKEN_SECRET))
val apiUrlFormat = if (TextUtils.isEmpty(this.apiUrlFormat)) DEFAULT_TWITTER_API_URL_FORMAT else this.apiUrlFormat!!
val apiUrlFormat = apiConfig.apiUrlFormat ?: DEFAULT_TWITTER_API_URL_FORMAT
signInTask = BrowserSignInTask(this, consumerKey, requestToken, verifier, apiUrlFormat,
sameOAuthSigningUrl, noVersionSuffix)
apiConfig.isSameOAuthUrl, apiConfig.isNoVersionSuffix)
AsyncTaskUtils.executeTask<AbstractSignInTask, Any>(signInTask)
}
@ -362,23 +333,23 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
val noVersionSuffix = preferences.getBoolean(KEY_NO_VERSION_SUFFIX, false)
val consumerKey = Utils.getNonEmptyString(preferences, KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY)
val consumerSecret = Utils.getNonEmptyString(preferences, KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET)
if (TextUtils.isEmpty(this.apiUrlFormat) || defaultApiChanged) {
this.apiUrlFormat = apiUrlFormat
if (TextUtils.isEmpty(apiConfig.apiUrlFormat) || defaultApiChanged) {
apiConfig.apiUrlFormat = apiUrlFormat
}
if (defaultApiChanged) {
this.authType = authType
apiConfig.credentialsType = authType
}
if (defaultApiChanged) {
this.sameOAuthSigningUrl = sameOAuthSigningUrl
apiConfig.isSameOAuthUrl = sameOAuthSigningUrl
}
if (defaultApiChanged) {
this.noVersionSuffix = noVersionSuffix
apiConfig.isNoVersionSuffix = noVersionSuffix
}
if (TextUtils.isEmpty(this.consumerKey) || defaultApiChanged) {
this.consumerKey = consumerKey
if (TextUtils.isEmpty(apiConfig.consumerKey) || defaultApiChanged) {
apiConfig.consumerKey = consumerKey
}
if (TextUtils.isEmpty(this.consumerSecret) || defaultApiChanged) {
this.consumerSecret = consumerSecret
if (TextUtils.isEmpty(apiConfig.consumerSecret) || defaultApiChanged) {
apiConfig.consumerSecret = consumerSecret
}
if (defaultApiChanged) {
apiChangeTimestamp = apiLastChange
@ -386,7 +357,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
}
private fun setSignInButton() {
when (authType) {
when (apiConfig.credentialsType) {
Credentials.Type.XAUTH, Credentials.Type.BASIC -> {
passwordSignIn.visibility = View.GONE
signIn.isEnabled = editPassword.text.isNotEmpty() && editUsername.text.isNotEmpty()
@ -738,19 +709,19 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
val credentials: Credentials,
val user: ParcelableUser,
val color: Int = 0,
val accountType: Pair<String, String>
val accountType: Pair<String, String?>
) {
private fun writeAccountInfo(map: MutableMap<String, String?>) {
map[ACCOUNT_USER_DATA_KEY] = user.key.toString()
map[ACCOUNT_USER_DATA_TYPE] = accountType.first
map[ACCOUNT_USER_DATA_CREDS_TYPE] = credsType
private fun writeAccountInfo(action: (k: String, v: String?) -> Unit) {
action(ACCOUNT_USER_DATA_KEY, user.key.toString())
action(ACCOUNT_USER_DATA_TYPE, accountType.first)
action(ACCOUNT_USER_DATA_CREDS_TYPE, credsType)
map[ACCOUNT_USER_DATA_ACTIVATED] = true.toString()
map[ACCOUNT_USER_DATA_COLOR] = toHexColor(color)
action(ACCOUNT_USER_DATA_ACTIVATED, true.toString())
action(ACCOUNT_USER_DATA_COLOR, toHexColor(color))
map[ACCOUNT_USER_DATA_USER] = LoganSquare.serialize(user)
map[ACCOUNT_USER_DATA_EXTRAS] = accountType.second
action(ACCOUNT_USER_DATA_USER, LoganSquare.serialize(user))
action(ACCOUNT_USER_DATA_EXTRAS, accountType.second)
}
private fun writeAuthToken(am: AccountManager, account: Account) {
@ -759,9 +730,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
fun updateAccount(am: AccountManager) {
val account = AccountUtils.findByAccountKey(am, user.key) ?: return
val map: MutableMap<String, String?> = HashMap()
writeAccountInfo(map)
for ((k, v) in map) {
writeAccountInfo { k, v ->
am.setUserData(account, k, v)
}
writeAuthToken(am, account)
@ -769,14 +738,13 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
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)
val userData = Bundle()
for ((k, v) in map) {
userData[k] = v
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)
}
userData[ACCOUNT_USER_DATA_POSITION] = AccountUtils.getAccounts(am).size.toString()
am.addAccountExplicitly(account, null, userData)
am.setUserData(account, ACCOUNT_USER_DATA_POSITION, accountPosition.toString())
writeAuthToken(am, account)
return account
}
@ -1014,7 +982,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
private val EXTRA_API_LAST_CHANGE = "api_last_change"
private val DEFAULT_TWITTER_API_URL_FORMAT = "https://[DOMAIN.]twitter.com/"
internal fun detectAccountType(twitter: MicroBlog, user: User): Pair<String, String> {
internal fun detectAccountType(twitter: MicroBlog, user: User): Pair<String, String?> {
try {
// Get StatusNet specific resource
val config = twitter.statusNetConfig
@ -1023,8 +991,8 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
if (site != null) {
extra.textLimit = site.textLimit
}
return Pair.create<String, String>(AccountType.STATUSNET,
JsonSerializer.serialize(extra, StatusNetAccountExtras::class.java))
return Pair(AccountType.STATUSNET, JsonSerializer.serialize(extra,
StatusNetAccountExtras::class.java))
} catch (e: MicroBlogException) {
// Ignore
}
@ -1036,16 +1004,16 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher {
twitter.getActivitiesAboutMe(paging)
val extra = TwitterAccountExtras()
extra.setIsOfficialCredentials(true)
return Pair.create<String, String>(AccountType.TWITTER,
JsonSerializer.serialize(extra, TwitterAccountExtras::class.java))
return Pair(AccountType.TWITTER, JsonSerializer.serialize(extra,
TwitterAccountExtras::class.java))
} catch (e: MicroBlogException) {
// Ignore
}
if (UserKeyUtils.isFanfouUser(user)) {
return Pair.create<String, String>(AccountType.FANFOU, null)
return Pair(AccountType.FANFOU, null)
}
return Pair.create<String, String>(AccountType.TWITTER, null)
return Pair(AccountType.TWITTER, null)
}
}

View File

@ -46,16 +46,15 @@ import org.mariotaku.restfu.http.HttpResponse
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.BuildConfig
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.constant.IntentConstants.EXTRA_API_CONFIG
import org.mariotaku.twidere.constant.defaultAPIConfigKey
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
import org.mariotaku.twidere.util.ParseUtils
import org.mariotaku.twidere.util.Utils
import org.mariotaku.twidere.util.dagger.GeneralComponentHelper
import java.io.IOException
@ -103,75 +102,38 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
}
public override fun onSaveInstanceState(outState: Bundle) {
val apiUrlFormat = ParseUtils.parseString(this.editApiUrlFormat.text)
val authType = getCheckedAuthType(this.editAuthType.checkedRadioButtonId)
val sameOAuthSigningUrl = this.editSameOAuthSigningUrl.isChecked
val noVersionSuffix = this.editNoVersionSuffix.isChecked
val consumerKey = ParseUtils.parseString(this.editConsumerKey.text)
val consumerSecret = ParseUtils.parseString(this.editConsumerSecret.text)
outState.putString(Accounts.API_URL_FORMAT, apiUrlFormat)
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)
outState.putString(Accounts.CONSUMER_SECRET, consumerSecret)
outState.putParcelable(EXTRA_API_CONFIG, createCustomAPIConfig())
super.onSaveInstanceState(outState)
}
fun saveAndFinish() {
val apiUrlFormat = ParseUtils.parseString(this.editApiUrlFormat.text)
val authType = getCheckedAuthType(this.editAuthType.checkedRadioButtonId)
val sameOAuthSigningUrl = this.editSameOAuthSigningUrl.isChecked
val noVersionSuffix = this.editNoVersionSuffix.isChecked
val consumerKey = ParseUtils.parseString(this.editConsumerKey.text)
val consumerSecret = ParseUtils.parseString(this.editConsumerSecret.text)
val intent = Intent()
intent.putExtra(Accounts.API_URL_FORMAT, apiUrlFormat)
intent.putExtra(Accounts.AUTH_TYPE, authType)
intent.putExtra(Accounts.SAME_OAUTH_SIGNING_URL, sameOAuthSigningUrl)
intent.putExtra(Accounts.NO_VERSION_SUFFIX, noVersionSuffix)
intent.putExtra(Accounts.CONSUMER_KEY, consumerKey)
intent.putExtra(Accounts.CONSUMER_SECRET, consumerSecret)
intent.putExtra(EXTRA_API_CONFIG, createCustomAPIConfig())
setResult(Activity.RESULT_OK, intent)
finish()
}
private fun createCustomAPIConfig(): CustomAPIConfig {
return CustomAPIConfig().apply {
this.apiUrlFormat = editApiUrlFormat.text.toString()
this.credentialsType = getCheckedAuthType(editAuthType.checkedRadioButtonId)
this.isSameOAuthUrl = editSameOAuthSigningUrl.isChecked
this.isNoVersionSuffix = editNoVersionSuffix.isChecked
this.consumerKey = editConsumerKey.text.toString()
this.consumerSecret = editConsumerSecret.text.toString()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val intent = intent
val extras = intent.extras
setContentView(R.layout.activity_api_editor)
val apiUrlFormat: String?
val authType: String
val sameOAuthSigningUrl: Boolean
val noVersionSuffix: Boolean
val consumerKey: String?
val consumerSecret: String?
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.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)
val prefConsumerSecret = Utils.getNonEmptyString(pref, KEY_CONSUMER_SECRET, TWITTER_CONSUMER_SECRET)
val bundle: Bundle
val apiConfig: CustomAPIConfig
if (savedInstanceState != null) {
bundle = savedInstanceState
} else if (extras != null) {
bundle = extras
apiConfig = savedInstanceState.getParcelable(EXTRA_API_CONFIG)
} else {
bundle = Bundle()
apiConfig = intent.getParcelableExtra(EXTRA_API_CONFIG) ?: kPreferences[defaultAPIConfigKey]
}
apiUrlFormat = bundle.getString(Accounts.API_URL_FORMAT, prefApiUrlFormat)?.trim()
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()
consumerSecret = bundle.getString(Accounts.CONSUMER_SECRET, prefConsumerSecret)?.trim()
editAuthType.setOnCheckedChangeListener(this)
editNoVersionSuffix.setOnCheckedChangeListener(this)
@ -181,13 +143,13 @@ class APIEditorActivity : BaseActivity(), OnCheckedChangeListener, OnClickListen
loadDefaults.visibility = View.VISIBLE
loadDefaults.setOnClickListener(this)
editApiUrlFormat.setText(apiUrlFormat)
editSameOAuthSigningUrl.isChecked = sameOAuthSigningUrl
editNoVersionSuffix.isChecked = noVersionSuffix
editConsumerKey.setText(consumerKey)
editConsumerSecret.setText(consumerSecret)
editApiUrlFormat.setText(apiConfig.apiUrlFormat)
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
editAuthType.check(getAuthTypeId(authType))
editAuthType.check(getAuthTypeId(apiConfig.credentialsType))
if (editAuthType.checkedRadioButtonId == -1) {
oauth.isChecked = true
}

View File

@ -29,8 +29,8 @@ val linkHighlightOptionKey = KStringKey(KEY_LINK_HIGHLIGHT_OPTION, VALUE_LINK_HI
val statusShortenerKey = KNullableStringKey(KEY_STATUS_SHORTENER, null)
val mediaUploaderKey = KNullableStringKey(KEY_MEDIA_UPLOADER, null)
val newDocumentApiKey = KBooleanKey(KEY_NEW_DOCUMENT_API, Build.VERSION.SDK_INT == Build.VERSION_CODES.M)
val loadItemLimitKey: KIntKey = KIntKey(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT)
val defaultFeatureLastUpdated: KLongKey = KLongKey("default_feature_last_updated", -1)
val loadItemLimitKey = KIntKey(KEY_LOAD_ITEM_LIMIT, DEFAULT_LOAD_ITEM_LIMIT)
val defaultFeatureLastUpdated = KLongKey("default_feature_last_updated", -1)
object defaultAPIConfigKey : KPreferenceKey<CustomAPIConfig> {
override fun contains(preferences: SharedPreferences): Boolean {

View File

@ -49,7 +49,7 @@ class AddUserFilterDialogFragment : AbsUserMuteBlockDialogFragment() {
companion object {
val FRAGMENT_TAG = "add_user_filter"
const val FRAGMENT_TAG = "add_user_filter"
fun show(fm: FragmentManager, user: ParcelableUser): AddUserFilterDialogFragment {
val args = Bundle()

View File

@ -0,0 +1,152 @@
/*
* 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.preference
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.support.v7.preference.DialogPreference
import android.support.v7.preference.PreferenceDialogFragmentCompat
import android.support.v7.preference.PreferenceFragmentCompat
import android.util.AttributeSet
import android.view.View
import android.widget.CheckBox
import android.widget.EditText
import android.widget.RadioGroup
import android.widget.Toast
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.twidere.R
import org.mariotaku.twidere.activity.iface.APIEditorActivity
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_API_CONFIG
import org.mariotaku.twidere.constant.defaultAPIConfigKey
import org.mariotaku.twidere.fragment.ThemedPreferenceDialogFragmentCompat
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.account.cred.Credentials
import org.mariotaku.twidere.preference.iface.IDialogPreference
import org.mariotaku.twidere.util.ParseUtils
import org.mariotaku.twidere.util.dagger.DependencyHolder
class DefaultAPIPreference @JvmOverloads constructor(context: Context,
attrs: AttributeSet?,
defStyle: Int = R.attr.dialogPreferenceStyle
) : DialogPreference(context, attrs, defStyle), IDialogPreference {
private var kPreferences: KPreferences
init {
dialogLayoutResource = R.layout.layout_api_editor
kPreferences = DependencyHolder.get(context).kPreferences
}
override fun displayDialog(fragment: PreferenceFragmentCompat) {
val df = DefaultAPIPreferenceDialogFragment.newInstance(key)
df.setTargetFragment(fragment, 0)
df.show(fragment.fragmentManager, key)
}
class DefaultAPIPreferenceDialogFragment : ThemedPreferenceDialogFragmentCompat() {
private val editAPIUrlFormat: EditText by lazy { dialog.findViewById(R.id.editApiUrlFormat) as EditText }
private val editSameOAuthSigningUrl: CheckBox by lazy { dialog.findViewById(R.id.editSameOAuthSigningUrl) as CheckBox }
private val editNoVersionSuffix: CheckBox by lazy { dialog.findViewById(R.id.editNoVersionSuffix) as CheckBox }
private val editConsumerKey: EditText by lazy { dialog.findViewById(R.id.editConsumerKey) as EditText }
private val editConsumerSecret: EditText by lazy { dialog.findViewById(R.id.editConsumerSecret) as EditText }
private val editAuthType: RadioGroup by lazy { dialog.findViewById(R.id.editAuthType) as RadioGroup }
private val apiFormatHelpButton: View by lazy { dialog.findViewById(R.id.apiUrlFormatHelp) }
private var editNoVersionSuffixChanged: Boolean = false
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val preference = preference
val dialog = super.onCreateDialog(savedInstanceState)
dialog.setOnShowListener { dialog ->
editNoVersionSuffix.setOnCheckedChangeListener { buttonView, isChecked -> editNoVersionSuffixChanged = true }
editAuthType.setOnCheckedChangeListener { group, checkedId ->
val authType = APIEditorActivity.getCheckedAuthType(checkedId)
val isOAuth = Credentials.Type.OAUTH == authType || Credentials.Type.XAUTH == authType
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 = Credentials.Type.EMPTY == authType
}
}
apiFormatHelpButton.setOnClickListener { Toast.makeText(context, R.string.api_url_format_help, Toast.LENGTH_LONG).show() }
if (savedInstanceState != null) {
setValues(savedInstanceState.getParcelable(EXTRA_API_CONFIG))
} else {
setValues(kPreferences[defaultAPIConfigKey])
}
}
return dialog
}
override fun onDialogClosed(positiveResult: Boolean) {
if (!positiveResult) return
kPreferences[defaultAPIConfigKey] = createCustomAPIConfig()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val apiConfig = createCustomAPIConfig()
outState.putParcelable(EXTRA_API_CONFIG, apiConfig)
}
private fun createCustomAPIConfig(): CustomAPIConfig {
val apiConfig = CustomAPIConfig()
apiConfig.apiUrlFormat = ParseUtils.parseString(editAPIUrlFormat.text)
apiConfig.credentialsType = APIEditorActivity.getCheckedAuthType(editAuthType.checkedRadioButtonId)
apiConfig.consumerKey = ParseUtils.parseString(editConsumerKey.text)
apiConfig.consumerSecret = ParseUtils.parseString(editConsumerSecret.text)
apiConfig.isSameOAuthUrl = editSameOAuthSigningUrl.isChecked
apiConfig.isNoVersionSuffix = editNoVersionSuffix.isChecked
return apiConfig
}
private fun setValues(apiConfig: CustomAPIConfig) {
editAPIUrlFormat.setText(apiConfig.apiUrlFormat)
editSameOAuthSigningUrl.isChecked = apiConfig.isSameOAuthUrl
editNoVersionSuffix.isChecked = apiConfig.isNoVersionSuffix
editConsumerKey.setText(apiConfig.consumerKey)
editConsumerSecret.setText(apiConfig.consumerSecret)
editAuthType.check(APIEditorActivity.getAuthTypeId(apiConfig.credentialsType))
if (editAuthType.checkedRadioButtonId == -1) {
editAuthType.check(R.id.oauth)
}
}
companion object {
fun newInstance(key: String): DefaultAPIPreferenceDialogFragment {
val df = DefaultAPIPreferenceDialogFragment()
val args = Bundle()
args.putString(PreferenceDialogFragmentCompat.ARG_KEY, key)
df.arguments = args
return df
}
}
}
}

View File

@ -28,7 +28,7 @@ open class UpdateProfileImageTask<ResultHandler>(
private val accountKey: UserKey,
private val imageUri: Uri,
private val deleteImage: Boolean
) : AbstractTask<Any, SingleResponse<ParcelableUser>, ResultHandler>() {
) : AbstractTask<Unit, SingleResponse<ParcelableUser>, ResultHandler>() {
@Inject
lateinit var bus: Bus
@ -36,9 +36,10 @@ open class UpdateProfileImageTask<ResultHandler>(
init {
@Suppress("UNCHECKED_CAST")
GeneralComponentHelper.build(context).inject(this as UpdateProfileImageTask<Any>)
setParams(Unit)
}
override fun doLongOperation(params: Any): SingleResponse<ParcelableUser> {
override fun doLongOperation(params: Unit): SingleResponse<ParcelableUser> {
try {
val microBlog = MicroBlogAPIFactory.getInstance(context, accountKey)!!
TwitterWrapper.updateProfileImage(context, microBlog, imageUri, deleteImage)

View File

@ -3,7 +3,6 @@ package org.mariotaku.twidere.util
import android.accounts.Account
import android.accounts.AccountManager
import android.database.sqlite.SQLiteDatabase
import android.os.Bundle
import android.support.annotation.ColorInt
import com.bluelinelabs.logansquare.LoganSquare
import org.mariotaku.twidere.TwidereConstants.*
@ -33,14 +32,15 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) {
while (!cur.isAfterLast) {
val credentials = indices.newObject(cur)
val account = Account(credentials.account_name, ACCOUNT_TYPE)
val userdata = Bundle()
userdata.putString(ACCOUNT_USER_DATA_KEY, credentials.account_key.toString())
userdata.putString(ACCOUNT_USER_DATA_TYPE, credentials.account_type)
userdata.putString(ACCOUNT_USER_DATA_ACTIVATED, credentials.is_activated.toString())
userdata.putString(ACCOUNT_USER_DATA_CREDS_TYPE, credentials.getCredentialsType())
userdata.putString(ACCOUNT_USER_DATA_COLOR, toHexColor(credentials.color))
userdata.putString(ACCOUNT_USER_DATA_POSITION, credentials.sort_position)
userdata.putString(ACCOUNT_USER_DATA_USER, LoganSquare.serialize(credentials.account_user ?: run {
// Don't add UserData in this method, see http://stackoverflow.com/a/29776224/859190
am.addAccountExplicitly(account, null, null)
am.setUserData(account, ACCOUNT_USER_DATA_KEY, credentials.account_key.toString())
am.setUserData(account, ACCOUNT_USER_DATA_TYPE, credentials.account_type)
am.setUserData(account, ACCOUNT_USER_DATA_ACTIVATED, credentials.is_activated.toString())
am.setUserData(account, ACCOUNT_USER_DATA_CREDS_TYPE, credentials.getCredentialsType())
am.setUserData(account, ACCOUNT_USER_DATA_COLOR, toHexColor(credentials.color))
am.setUserData(account, ACCOUNT_USER_DATA_POSITION, credentials.sort_position)
am.setUserData(account, ACCOUNT_USER_DATA_USER, LoganSquare.serialize(credentials.account_user ?: run {
val user = ParcelableUser()
user.account_key = credentials.account_key
user.key = credentials.account_key
@ -51,8 +51,7 @@ fun migrateAccounts(am: AccountManager, db: SQLiteDatabase) {
user.profile_image_url = credentials.profile_image_url
return@run user
}))
userdata.putString(ACCOUNT_USER_DATA_EXTRAS, credentials.account_extras)
am.addAccountExplicitly(account, null, userdata)
am.setUserData(account, ACCOUNT_USER_DATA_EXTRAS, credentials.account_extras)
am.setAuthToken(account, ACCOUNT_AUTH_TOKEN_TYPE, LoganSquare.serialize(credentials.toCredentials()))
cur.moveToNext()
}

View File

@ -22,6 +22,7 @@ package org.mariotaku.twidere.util.dagger
import android.content.Context
import edu.tsinghua.hotmobi.HotMobiLogger
import okhttp3.ConnectionPool
import org.mariotaku.kpreferences.KPreferences
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.model.DefaultFeatures
import org.mariotaku.twidere.util.*
@ -68,6 +69,9 @@ class DependencyHolder internal constructor(context: Context) {
@Inject
lateinit var userColorNameManager: UserColorNameManager
internal set
@Inject
lateinit var kPreferences: KPreferences
internal set
init {
GeneralComponentHelper.build(context).inject(this)

View File

@ -43,7 +43,6 @@ import org.mariotaku.twidere.task.twitter.GetStatusesTask
import org.mariotaku.twidere.task.twitter.UpdateStatusTask
import org.mariotaku.twidere.text.util.EmojiEditableFactory
import org.mariotaku.twidere.text.util.EmojiSpannableFactory
import org.mariotaku.twidere.util.AsyncTwitterWrapper
import org.mariotaku.twidere.util.MultiSelectEventHandler
import javax.inject.Singleton
@ -136,4 +135,6 @@ interface GeneralComponent {
fun inject(task: UpdateStatusTask)
fun inject(application: TwidereApplication)
fun inject(fragment: ThemedPreferenceDialogFragmentCompat)
}

View File

@ -25,12 +25,12 @@
android:layout_height="match_parent">
<WebView
android:id="@+id/webview"
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/progress_container"
android:id="@+id/progressContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false">

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB