1
0
mirror of https://github.com/TwidereProject/Twidere-Android synced 2025-01-31 08:54:57 +01:00

improved oauth browser sign in

This commit is contained in:
Mariotaku Lee 2016-03-11 22:23:09 +08:00
parent 25714f06b9
commit af2ff44328
16 changed files with 181 additions and 119 deletions

View File

@ -45,4 +45,8 @@ public interface TwitterOAuth {
OAuthToken getAccessToken(@Extra({"oauth_token", "oauth_token_secret"}) OAuthToken requestToken,
@Param("oauth_verifier") String oauthVerifier) throws TwitterException;
@POST("/oauth/access_token")
OAuthToken getAccessToken(@Extra({"oauth_token", "oauth_token_secret"}) OAuthToken requestToken)
throws TwitterException;
}

View File

@ -0,0 +1,34 @@
package org.mariotaku.twidere.util;
import org.junit.Test;
import org.mariotaku.twidere.util.UriUtils;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
/**
* Created by mariotaku on 16/2/9.
*/
public class UriUtilsTest {
@Test
public void testGetAuthority() throws Exception {
assertEquals("www.google.com", UriUtils.getAuthority("http://www.google.com/"));
assertEquals("twitter.com", UriUtils.getAuthority("https://twitter.com"));
assertNull(UriUtils.getAuthority("www.google.com/"));
}
@Test
public void testGetPath() throws Exception {
assertEquals("/", UriUtils.getPath("http://www.example.com/"));
assertEquals("", UriUtils.getPath("http://www.example.com"));
assertEquals("/test/path", UriUtils.getPath("https://example.com/test/path"));
assertEquals("/test/path", UriUtils.getPath("https://example.com/test/path?with=query"));
assertEquals("/test/path/", UriUtils.getPath("https://example.com/test/path/?with=query"));
assertEquals("/test/path", UriUtils.getPath("https://example.com/test/path?with=query#fragment"));
assertEquals("/test/path/", UriUtils.getPath("https://example.com/test/path/?with=query#fragment"));
assertEquals("/test/path", UriUtils.getPath("https://example.com/test/path#fragment"));
assertEquals("/test/path/", UriUtils.getPath("https://example.com/test/path/#fragment"));
}
}

View File

@ -1,33 +0,0 @@
package org.mariotaku.twidere.util.media.preview;
import org.junit.Test;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
/**
* Created by mariotaku on 16/2/9.
*/
public class PreviewMediaExtractorTest {
@Test
public void testGetAuthority() throws Exception {
assertEquals("www.google.com", PreviewMediaExtractor.getAuthority("http://www.google.com/"));
assertEquals("twitter.com", PreviewMediaExtractor.getAuthority("https://twitter.com"));
assertNull(PreviewMediaExtractor.getAuthority("www.google.com/"));
}
@Test
public void testGetPath() throws Exception {
assertEquals("/", PreviewMediaExtractor.getPath("http://www.example.com/"));
assertEquals("", PreviewMediaExtractor.getPath("http://www.example.com"));
assertEquals("/test/path", PreviewMediaExtractor.getPath("https://example.com/test/path"));
assertEquals("/test/path", PreviewMediaExtractor.getPath("https://example.com/test/path?with=query"));
assertEquals("/test/path/", PreviewMediaExtractor.getPath("https://example.com/test/path/?with=query"));
assertEquals("/test/path", PreviewMediaExtractor.getPath("https://example.com/test/path?with=query#fragment"));
assertEquals("/test/path/", PreviewMediaExtractor.getPath("https://example.com/test/path/?with=query#fragment"));
assertEquals("/test/path", PreviewMediaExtractor.getPath("https://example.com/test/path#fragment"));
assertEquals("/test/path/", PreviewMediaExtractor.getPath("https://example.com/test/path/#fragment"));
}
}

View File

@ -22,7 +22,6 @@ package org.mariotaku.twidere.activity.support;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
@ -30,7 +29,6 @@ import android.os.Bundle;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
@ -38,12 +36,11 @@ import android.widget.Toast;
import org.attoparser.AttoParseException;
import org.mariotaku.restfu.http.Authorization;
import org.mariotaku.restfu.http.Endpoint;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.api.twitter.TwitterOAuth;
import org.mariotaku.twidere.api.twitter.auth.OAuthAuthorization;
import org.mariotaku.twidere.api.twitter.auth.OAuthEndpoint;
import org.mariotaku.twidere.api.twitter.auth.OAuthToken;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator;
@ -52,9 +49,9 @@ 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;
import static org.mariotaku.twidere.util.Utils.getNonEmptyString;
@SuppressLint("SetJavaScriptEnabled")
public class BrowserSignInActivity extends BaseAppCompatActivity {
@ -97,7 +94,6 @@ public class BrowserSignInActivity extends BaseAppCompatActivity {
@SuppressLint("AddJavascriptInterface")
@Override
protected void onCreate(final Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_browser_sign_in);
mWebView.setWebViewClient(new AuthorizationWebViewClient(this));
@ -152,7 +148,25 @@ public class BrowserSignInActivity extends BaseAppCompatActivity {
public void onPageFinished(final WebView view, final String url) {
super.onPageFinished(view, url);
view.loadUrl(INJECT_CONTENT);
((BrowserSignInActivity) getActivity()).setLoadProgressShown(false);
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
@ -167,7 +181,7 @@ public class BrowserSignInActivity extends BaseAppCompatActivity {
final String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
final Activity activity = getActivity();
Toast.makeText(activity, R.string.error_occurred, Toast.LENGTH_SHORT).show();
Toast.makeText(activity, description, Toast.LENGTH_SHORT).show();
activity.finish();
}
@ -196,36 +210,27 @@ public class BrowserSignInActivity extends BaseAppCompatActivity {
static class GetRequestTokenTask extends AsyncTask<Object, Object, OAuthToken> {
private final String mConsumerKey, mConsumerSecret;
private final TwidereApplication mApplication;
private final SharedPreferences mPreferences;
private final BrowserSignInActivity mActivity;
private final String mAPIUrlFormat;
public GetRequestTokenTask(final BrowserSignInActivity activity) {
mActivity = activity;
mApplication = TwidereApplication.getInstance(activity);
mPreferences = activity.getSharedPreferences(SHARED_PREFERENCES_NAME, MODE_PRIVATE);
final Intent intent = activity.getIntent();
mConsumerKey = intent.getStringExtra(Accounts.CONSUMER_KEY);
mConsumerSecret = intent.getStringExtra(Accounts.CONSUMER_SECRET);
mAPIUrlFormat = intent.getStringExtra(Accounts.API_URL_FORMAT);
}
@Override
protected OAuthToken doInBackground(final Object... params) {
final String defConsumerKey = getNonEmptyString(mPreferences, KEY_CONSUMER_KEY, TWITTER_CONSUMER_KEY);
final String defConsumerSecret = getNonEmptyString(mPreferences, KEY_CONSUMER_SECRET,
TWITTER_CONSUMER_SECRET);
final String consumerKey, consumerSecret;
if (!isEmpty(mConsumerKey) && !isEmpty(mConsumerSecret)) {
consumerKey = mConsumerKey;
consumerSecret = mConsumerSecret;
} else {
consumerKey = defConsumerKey;
consumerSecret = defConsumerSecret;
if (isEmpty(mConsumerKey) || isEmpty(mConsumerSecret)) {
return null;
}
try {
final OAuthEndpoint endpoint = new OAuthEndpoint(TwitterAPIFactory.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", null));
final Authorization auth = new OAuthAuthorization(consumerKey, consumerSecret);
final TwitterOAuth twitter = TwitterAPIFactory.getInstance(mActivity, endpoint, auth, TwitterOAuth.class);
final Endpoint endpoint = TwitterAPIFactory.getOAuthSignInEndpoint(mAPIUrlFormat, true);
final Authorization auth = new OAuthAuthorization(mConsumerKey, mConsumerSecret);
final TwitterOAuth twitter = TwitterAPIFactory.getInstance(mActivity, endpoint,
auth, TwitterOAuth.class);
return twitter.getRequestToken(OAUTH_CALLBACK_OOB);
} catch (final Exception e) {
Log.w(LOGTAG, e);
@ -244,7 +249,7 @@ public class BrowserSignInActivity extends BaseAppCompatActivity {
}
return;
}
final OAuthEndpoint endpoint = new OAuthEndpoint(TwitterAPIFactory.getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api", null));
final Endpoint endpoint = TwitterAPIFactory.getOAuthSignInEndpoint(mAPIUrlFormat, true);
mActivity.loadUrl(endpoint.construct("/oauth/authorize", new String[]{"oauth_token", data.getOauthToken()}));
}

View File

@ -267,6 +267,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
final Intent intent = new Intent(this, BrowserSignInActivity.class);
intent.putExtra(Accounts.CONSUMER_KEY, mConsumerKey);
intent.putExtra(Accounts.CONSUMER_SECRET, mConsumerSecret);
intent.putExtra(Accounts.API_URL_FORMAT, mAPIUrlFormat);
startActivityForResult(intent, REQUEST_BROWSER_SIGN_IN);
break;
}
@ -640,17 +641,20 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
public static class BrowserSignInTask extends AbstractSignInTask {
private final String oauthVerifier;
private final Context context;
@NonNull
private final String apiUrlFormat;
private final boolean sameOauthSigningUrl, noVersionSuffix;
@Nullable
private final String oauthVerifier;
private final OAuthToken consumerKey, requestToken;
private final boolean sameOauthSigningUrl, noVersionSuffix;
public BrowserSignInTask(final SignInActivity context, OAuthToken consumerKey,
final OAuthToken requestToken,
final String oauthVerifier, @NonNull final String apiUrlFormat,
public BrowserSignInTask(@NonNull final SignInActivity context,
@NonNull final OAuthToken consumerKey,
@NonNull final OAuthToken requestToken,
@Nullable final String oauthVerifier,
@NonNull final String apiUrlFormat,
final boolean sameOauthSigningUrl, final boolean noVersionSuffix) {
super(context);
this.context = context;
@ -666,23 +670,26 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
protected SignInResponse doInBackground(final Object... params) {
try {
final String versionSuffix = noVersionSuffix ? null : "1.1";
Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null,
Endpoint endpoint = TwitterAPIFactory.getOAuthSignInEndpoint(apiUrlFormat,
sameOauthSigningUrl);
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(context, endpoint,
new OAuthAuthorization(consumerKey.getOauthToken(),
consumerKey.getOauthTokenSecret()), TwitterOAuth.class);
final OAuthToken accessToken = oauth.getAccessToken(requestToken, oauthVerifier);
final String userId = accessToken.getUserId();
if (userId == null) return new SignInResponse(false, false, null);
final OAuthToken accessToken;
if (oauthVerifier != null) {
accessToken = oauth.getAccessToken(requestToken, oauthVerifier);
} else {
accessToken = oauth.getAccessToken(requestToken);
}
final OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(),
consumerKey.getOauthTokenSecret(), accessToken);
endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", versionSuffix,
sameOauthSigningUrl);
final Twitter twitter = TwitterAPIFactory.getInstance(context, endpoint,
auth, Twitter.class);
final Twitter twitter = TwitterAPIFactory.getInstance(context, endpoint, auth,
Twitter.class);
final User user = twitter.verifyCredentials();
final int color = analyseUserProfileColor(user);
return new SignInResponse(isUserLoggedIn(context, userId), auth, user,
return new SignInResponse(isUserLoggedIn(context, user.getId()), auth, user,
ParcelableCredentials.AUTH_TYPE_OAUTH, color, apiUrlFormat, sameOauthSigningUrl,
noVersionSuffix, detectAccountType(twitter, user));
} catch (final TwitterException e) {
@ -759,7 +766,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
private SignInResponse authOAuth() throws AuthenticationException, TwitterException {
final SignInActivity activity = activityRef.get();
if (activity == null) return new SignInResponse(false, false, null);
Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl);
Endpoint endpoint = TwitterAPIFactory.getOAuthSignInEndpoint(apiUrlFormat,
sameOAuthSigningUrl);
OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret());
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class);
final OAuthPasswordAuthenticator authenticator = new OAuthPasswordAuthenticator(oauth, verificationCallback, userAgent);
@ -773,7 +781,8 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
private SignInResponse authxAuth() throws TwitterException {
final SignInActivity activity = activityRef.get();
if (activity == null) return new SignInResponse(false, false, null);
Endpoint endpoint = TwitterAPIFactory.getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl);
Endpoint endpoint = TwitterAPIFactory.getOAuthSignInEndpoint(apiUrlFormat,
sameOAuthSigningUrl);
OAuthAuthorization auth = new OAuthAuthorization(consumerKey.getOauthToken(), consumerKey.getOauthTokenSecret());
final TwitterOAuth oauth = TwitterAPIFactory.getInstance(activity, endpoint, auth, TwitterOAuth.class);
final OAuthToken accessToken = oauth.getAccessToken(username, password);
@ -963,6 +972,7 @@ public class SignInActivity extends BaseAppCompatActivity implements OnClickList
final AlertDialog alertDialog = (AlertDialog) dialog;
final TextView verificationHint = (TextView) alertDialog.findViewById(R.id.verification_hint);
final EditText editVerification = (EditText) alertDialog.findViewById(R.id.edit_verification_code);
if (verificationHint == null || editVerification == null) return;
if ("Push".equalsIgnoreCase(challengeType)) {
verificationHint.setText(R.string.login_verification_push_hint);
editVerification.setVisibility(View.GONE);

View File

@ -11,7 +11,7 @@ import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
import org.mariotaku.twidere.util.UriUtils;
import java.util.ArrayList;
@ -86,7 +86,7 @@ public class UserKeyUtils {
def = TwidereConstants.USER_TYPE_TWITTER_COM;
}
if (uri == null) return def;
final String authority = PreviewMediaExtractor.getAuthority(uri);
final String authority = UriUtils.getAuthority(uri);
if (authority == null) return def;
return authority.replaceAll("[^\\w\\d\\.]", "-");
}

View File

@ -21,7 +21,6 @@ import org.mariotaku.twidere.api.twitter.model.User;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
import java.util.ArrayList;
import java.util.List;
@ -160,7 +159,7 @@ public class InternalTwitterContentUtils {
public static String getBestBannerUrl(@Nullable final String baseUrl, final int width) {
if (baseUrl == null) return null;
final String type = getBestBannerType(width);
final String authority = PreviewMediaExtractor.getAuthority(baseUrl);
final String authority = UriUtils.getAuthority(baseUrl);
return authority != null && authority.endsWith(".twimg.com") ? baseUrl + "/" + type : baseUrl;
}

View File

@ -335,7 +335,7 @@ public class TwitterAPIFactory implements TwidereConstants {
}
@NonNull
public static String getApiUrl(final String pattern, final String domain, String appendPath) {
public static String getApiUrl(@NonNull final String pattern, final String domain, String appendPath) {
String urlBase = getApiBaseUrl(pattern, domain);
if (urlBase.endsWith("/")) {
urlBase = urlBase.substring(0, urlBase.length() - 1);
@ -390,14 +390,33 @@ public class TwitterAPIFactory implements TwidereConstants {
}
}
public static Endpoint getOAuthRestEndpoint(String apiUrlFormat, boolean sameOAuthSigningUrl, boolean noVersionSuffix) {
public static Endpoint getOAuthRestEndpoint(@NonNull String apiUrlFormat, boolean sameOAuthSigningUrl, boolean noVersionSuffix) {
return getOAuthEndpoint(apiUrlFormat, "api", noVersionSuffix ? null : "1.1", sameOAuthSigningUrl);
}
public static Endpoint getOAuthSignInEndpoint(@NonNull String apiUrlFormat, boolean sameOAuthSigningUrl) {
return getOAuthEndpoint(apiUrlFormat, "api", null, sameOAuthSigningUrl, true);
}
public static Endpoint getOAuthEndpoint(String apiUrlFormat, @Nullable String domain,
@Nullable String versionSuffix, boolean sameOAuthSigningUrl) {
@Nullable String versionSuffix,
boolean sameOAuthSigningUrl) {
return getOAuthEndpoint(apiUrlFormat, domain, versionSuffix, sameOAuthSigningUrl, false);
}
public static Endpoint getOAuthEndpoint(@NonNull String apiUrlFormat, @Nullable String domain,
@Nullable String versionSuffix,
boolean sameOAuthSigningUrl, boolean fixUrl) {
String endpointUrl, signEndpointUrl;
endpointUrl = getApiUrl(apiUrlFormat, domain, versionSuffix);
if (fixUrl) {
int[] authorityRange = UriUtils.getAuthorityRange(endpointUrl);
if (authorityRange != null && endpointUrl.regionMatches(authorityRange[0],
"api.fanfou.com", 0, authorityRange[1] - authorityRange[0])) {
endpointUrl = endpointUrl.substring(0, authorityRange[0]) + "fanfou.com" +
endpointUrl.substring(authorityRange[1]);
}
}
if (!sameOAuthSigningUrl) {
signEndpointUrl = getApiUrl(DEFAULT_TWITTER_API_URL_FORMAT, domain, versionSuffix);
} else {

View File

@ -20,6 +20,8 @@
package org.mariotaku.twidere.util;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by mariotaku on 15/3/23.
@ -39,4 +41,45 @@ public class UriUtils {
public static Uri appendQueryParameters(Uri uri, String key, boolean value) {
return appendQueryParameters(uri, key, ParseUtils.parseString(value));
}
@Nullable
public static String getAuthority(@NonNull String link) {
int start = link.indexOf("://");
if (start < 0) return null;
int end = link.indexOf('/', start + 3);
if (end < 0) {
end = link.length();
}
return link.substring(start + 3, end);
}
@Nullable
public static int[] getAuthorityRange(@NonNull String link) {
int start = link.indexOf("://");
if (start < 0) return null;
int end = link.indexOf('/', start + 3);
if (end < 0) {
end = link.length();
}
return new int[]{start + 3, end};
}
@Nullable
public static String getPath(@NonNull String link) {
int start = link.indexOf("://");
if (start < 0) return null;
start = link.indexOf('/', start + 3);
if (start < 0) {
return "";
}
int end = link.indexOf('?', start);
if (end < 0) {
end = link.indexOf('#', start);
if (end < 0) {
end = link.length();
}
}
return link.substring(start, end);
}
}

View File

@ -71,32 +71,4 @@ public class PreviewMediaExtractor {
return links;
}
@Nullable
public static String getAuthority(@NonNull String link) {
int start = link.indexOf("://");
if (start < 0) return null;
int end = link.indexOf('/', start + 3);
if (end < 0) {
end = link.length();
}
return link.substring(start + 3, end);
}
@Nullable
public static String getPath(@NonNull String link) {
int start = link.indexOf("://");
if (start < 0) return null;
start = link.indexOf('/', start + 3);
if (start < 0) {
return "";
}
int end = link.indexOf('?', start);
if (end < 0) {
end = link.indexOf('#', start);
if (end < 0) {
end = link.length();
}
}
return link.substring(start, end);
}
}

View File

@ -6,7 +6,7 @@ import android.support.annotation.WorkerThread;
import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
import org.mariotaku.twidere.util.UriUtils;
import java.util.Locale;
@ -16,14 +16,14 @@ import java.util.Locale;
public class InstagramProvider implements Provider {
@Override
public boolean supports(@NonNull String link) {
final String authority = PreviewMediaExtractor.getAuthority(link);
final String authority = UriUtils.getAuthority(link);
if (authority == null) return false;
switch (authority) {
//noinspection SpellCheckingInspection
case "instagr.am":
case "instagram.com":
case "www.instagram.com": {
final String path = PreviewMediaExtractor.getPath(link);
final String path = UriUtils.getPath(link);
return path != null && path.startsWith("/p/");
}
}
@ -33,7 +33,7 @@ public class InstagramProvider implements Provider {
@Override
@Nullable
public ParcelableMedia from(@NonNull String link) {
final String path = PreviewMediaExtractor.getPath(link);
final String path = UriUtils.getPath(link);
final String prefix = "/p/";
if (path == null || !path.startsWith(prefix)) {
return null;

View File

@ -5,7 +5,7 @@ import android.support.annotation.Nullable;
import org.mariotaku.restfu.http.RestHttpClient;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.util.media.preview.PreviewMediaExtractor;
import org.mariotaku.twidere.util.UriUtils;
import java.util.Locale;
@ -21,7 +21,7 @@ public class TwitterMediaProvider implements Provider {
@Nullable
@Override
public ParcelableMedia from(@NonNull String link) {
final String path = PreviewMediaExtractor.getPath(link);
final String path = UriUtils.getPath(link);
if (path == null) return null;
final ParcelableMedia media = new ParcelableMedia();
media.url = link;
@ -47,11 +47,11 @@ public class TwitterMediaProvider implements Provider {
}
public static boolean isSupported(@NonNull String link) {
final String authority = PreviewMediaExtractor.getAuthority(link);
final String authority = UriUtils.getAuthority(link);
if (authority == null || !authority.endsWith(".twimg.com")) {
return false;
}
final String path = PreviewMediaExtractor.getPath(link);
final String path = UriUtils.getPath(link);
if (path == null) return false;
if (path.startsWith("/media/")) {
return true;

View File

@ -107,6 +107,11 @@ public class ActivityTitleSummaryViewHolder extends ViewHolder implements View.O
summaryView.setText(message.getSummary());
summaryView.setVisibility(summaryView.length() > 0 ? View.VISIBLE : View.GONE);
timeView.setTime(activity.timestamp);
if (adapter.shouldShowAccountsColor()) {
itemContent.drawEnd(activity.account_color);
} else {
itemContent.drawEnd();
}
displayUserProfileImages(sources);
}

View File

@ -2,13 +2,15 @@
<org.mariotaku.twidere.view.ColorLabelFrameLayout
android:id="@+id/item_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:focusable="true"
android:focusableInTouchMode="false"
android:paddingLeft="@dimen/element_spacing_normal"
android:paddingRight="@dimen/element_spacing_normal">
android:paddingRight="@dimen/element_spacing_normal"
app:ignorePadding="true">
<include layout="@layout/card_item_activity_summary_common"/>
</org.mariotaku.twidere.view.ColorLabelFrameLayout>

View File

@ -2,11 +2,13 @@
<org.mariotaku.twidere.view.ColorLabelFrameLayout
android:id="@+id/item_content"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:focusable="true"
android:focusableInTouchMode="false">
android:focusableInTouchMode="false"
app:ignorePadding="true">
<include layout="@layout/card_item_activity_summary_common"/>
</org.mariotaku.twidere.view.ColorLabelFrameLayout>