updated icon
|
@ -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";
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package org.mariotaku.twidere.model.account.conf;
|
||||
|
||||
/**
|
||||
* Created by mariotaku on 2016/12/2.
|
||||
*/
|
||||
|
||||
public class APIConfiguration {
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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")'
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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>');"
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 8.0 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.1 KiB |
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 20 KiB |