mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-08 07:48:45 +01:00
improved combined emoji
improved user agent
This commit is contained in:
parent
d74ffe2814
commit
8c8bd746dc
@ -119,6 +119,7 @@ import org.mariotaku.twidere.util.MathUtils;
|
||||
import org.mariotaku.twidere.util.MediaLoaderWrapper;
|
||||
import org.mariotaku.twidere.util.MediaLoadingHandler;
|
||||
import org.mariotaku.twidere.util.MenuUtils;
|
||||
import org.mariotaku.twidere.util.MultiSelectManager;
|
||||
import org.mariotaku.twidere.util.Nullables;
|
||||
import org.mariotaku.twidere.util.RecyclerViewNavigationHelper;
|
||||
import org.mariotaku.twidere.util.RecyclerViewUtils;
|
||||
@ -947,11 +948,11 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
private final TextView translateResultView;
|
||||
private final RecyclerView interactUsersView;
|
||||
|
||||
public DetailStatusViewHolder(StatusAdapter adapter, View itemView) {
|
||||
public DetailStatusViewHolder(final StatusAdapter adapter, View itemView) {
|
||||
super(itemView);
|
||||
this.linkClickHandler = new StatusLinkClickHandler(adapter.getContext(), null);
|
||||
this.linkify = new TwidereLinkify(linkClickHandler);
|
||||
this.adapter = adapter;
|
||||
this.linkClickHandler = new DetailStatusLinkClickHandler(adapter.getContext(), null, adapter);
|
||||
this.linkify = new TwidereLinkify(linkClickHandler);
|
||||
menuBar = (ActionMenuView) itemView.findViewById(R.id.menu_bar);
|
||||
nameView = (TextView) itemView.findViewById(R.id.name);
|
||||
screenNameView = (TextView) itemView.findViewById(R.id.screen_name);
|
||||
@ -1328,7 +1329,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
interactUsersView.setLayoutManager(layoutManager);
|
||||
|
||||
if (adapter.isProfileImageEnabled()) {
|
||||
interactUsersView.setAdapter(new UserProfileImagesAdapter(adapter.getContext()));
|
||||
interactUsersView.setAdapter(new UserProfileImagesAdapter(fragment, adapter.getContext()));
|
||||
} else {
|
||||
interactUsersView.setAdapter(null);
|
||||
}
|
||||
@ -1341,9 +1342,11 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
|
||||
private static class UserProfileImagesAdapter extends ArrayRecyclerAdapter<ParcelableUser, ViewHolder> {
|
||||
private final LayoutInflater mInflater;
|
||||
private final StatusFragment mFragment;
|
||||
|
||||
public UserProfileImagesAdapter(Context context) {
|
||||
public UserProfileImagesAdapter(StatusFragment fragment, Context context) {
|
||||
super(context);
|
||||
mFragment = fragment;
|
||||
mInflater = LayoutInflater.from(context);
|
||||
}
|
||||
|
||||
@ -1357,7 +1360,7 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
return new ProfileImageViewHolder(this, mInflater.inflate(R.layout.adapter_item_status_interact_user, parent, false));
|
||||
}
|
||||
|
||||
static class ProfileImageViewHolder extends ViewHolder {
|
||||
static class ProfileImageViewHolder extends ViewHolder implements OnClickListener {
|
||||
|
||||
private final UserProfileImagesAdapter adapter;
|
||||
private final ImageView profileImageView;
|
||||
@ -1365,14 +1368,52 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
|
||||
public ProfileImageViewHolder(UserProfileImagesAdapter adapter, View itemView) {
|
||||
super(itemView);
|
||||
profileImageView = (ImageView) itemView.findViewById(R.id.profile_image);
|
||||
itemView.setOnClickListener(this);
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
public void displayUser(ParcelableUser item) {
|
||||
adapter.getMediaLoader().displayProfileImage(profileImageView, item.profile_image_url);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
adapter.notifyItemClick(getLayoutPosition());
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyItemClick(int position) {
|
||||
mFragment.onUserClick(getItem(position));
|
||||
}
|
||||
}
|
||||
|
||||
private static class DetailStatusLinkClickHandler extends StatusLinkClickHandler {
|
||||
private final StatusAdapter adapter;
|
||||
|
||||
public DetailStatusLinkClickHandler(Context context, MultiSelectManager manager, StatusAdapter adapter) {
|
||||
super(context, manager);
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLinkClick(String link, String orig, long accountId, long extraId, int type, boolean sensitive, int start, int end) {
|
||||
final ParcelableStatus status = adapter.getStatus();
|
||||
if (status.media != null) {
|
||||
for (final ParcelableMedia media : status.media) {
|
||||
if (media.start == start && media.end == end) {
|
||||
adapter.setDetailMediaExpanded(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.onLinkClick(link, orig, accountId, extraId, type, sensitive, start, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onUserClick(ParcelableUser user) {
|
||||
Utils.openUserProfile(getContext(), user, null);
|
||||
}
|
||||
|
||||
private static class SpaceViewHolder extends ViewHolder {
|
||||
|
@ -29,10 +29,12 @@ import android.text.style.DynamicDrawableSpan;
|
||||
*/
|
||||
public class EmojiSpan extends DynamicDrawableSpan {
|
||||
private final Drawable drawable;
|
||||
private Paint.FontMetrics fontMetrics;
|
||||
|
||||
public EmojiSpan(Drawable drawable) {
|
||||
super(ALIGN_BOTTOM);
|
||||
this.drawable = drawable;
|
||||
this.fontMetrics = new Paint.FontMetrics();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -44,7 +46,8 @@ public class EmojiSpan extends DynamicDrawableSpan {
|
||||
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
|
||||
final Drawable drawable = getDrawable();
|
||||
if (drawable == null) return 0;
|
||||
final int textHeightPx = Math.round(paint.descent() - paint.ascent());
|
||||
paint.getFontMetrics(fontMetrics);
|
||||
final int textHeightPx = Math.round(fontMetrics.descent - fontMetrics.ascent);
|
||||
final float intrinsicWidth = drawable.getIntrinsicWidth(),
|
||||
intrinsicHeight = drawable.getIntrinsicHeight();
|
||||
final int scaledWidth;
|
||||
@ -53,10 +56,7 @@ public class EmojiSpan extends DynamicDrawableSpan {
|
||||
} else {
|
||||
scaledWidth = Math.round(intrinsicWidth * (textHeightPx / intrinsicHeight));
|
||||
}
|
||||
if (fm == null) {
|
||||
fm = paint.getFontMetricsInt();
|
||||
}
|
||||
final int top = fm.bottom - textHeightPx, left = 0;
|
||||
final int top = Math.round(fontMetrics.bottom) - textHeightPx, left = 0;
|
||||
drawable.setBounds(left, top, left + scaledWidth, top + textHeightPx);
|
||||
return scaledWidth;
|
||||
}
|
||||
|
@ -49,41 +49,54 @@ public class EmojiSupportUtils {
|
||||
final ExternalThemeManager.Emoji emoji = manager.getEmoji();
|
||||
if (!emoji.isSupported()) return;
|
||||
final CodePointArray array = new CodePointArray(text);
|
||||
for (int i = array.length() - 1; i >= 0; i--) {
|
||||
final int codePoint = array.get(i);
|
||||
for (int arrayIdx = array.length() - 1; arrayIdx >= 0; arrayIdx--) {
|
||||
final int codePoint = array.get(arrayIdx);
|
||||
if (isEmoji(codePoint)) {
|
||||
int arrayIdx = i, arrayEnd = i + 1, arrayIdxOffset = 0;
|
||||
int textIdx = array.indexOfText(codePoint, i), textIdxOffset = 0;
|
||||
int indexOffset = 0;
|
||||
int arrayEnd = arrayIdx + 1, arrayIdxOffset = 0;
|
||||
int textIdx = array.indexOfText(codePoint, arrayIdx), textIdxOffset = 0;
|
||||
int skippedIndex = 0;
|
||||
if (textIdx == -1 || textIdx < textStart) {
|
||||
continue;
|
||||
}
|
||||
final int textEnd = textIdx + Character.charCount(codePoint);
|
||||
if (isRegionalIndicatorSymbol(codePoint)) {
|
||||
if (i > 0) {
|
||||
int prev = array.get(i - 1);
|
||||
if (isRegionalIndicatorSymbol(prev)) {
|
||||
if (arrayIdx > 0) {
|
||||
final int prevCodePoint = array.get(arrayIdx - 1);
|
||||
if (isRegionalIndicatorSymbol(codePoint)) {
|
||||
if (isRegionalIndicatorSymbol(prevCodePoint)) {
|
||||
arrayIdxOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prev);
|
||||
indexOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prevCodePoint);
|
||||
skippedIndex = -1;
|
||||
}
|
||||
}
|
||||
} else if (isModifier(codePoint)) {
|
||||
if (i > 0) {
|
||||
int prev = array.get(i - 1);
|
||||
if (isEmoji(prev)) {
|
||||
} else if (isModifier(codePoint)) {
|
||||
if (isEmoji(prevCodePoint)) {
|
||||
arrayIdxOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prev);
|
||||
indexOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prevCodePoint);
|
||||
skippedIndex = -1;
|
||||
}
|
||||
}
|
||||
} else if (isKeyCap(codePoint)) {
|
||||
if (i > 0) {
|
||||
int prev = array.get(i - 1);
|
||||
if (isPhoneNumberSymbol(prev)) {
|
||||
} else if (isKeyCap(codePoint)) {
|
||||
if (isPhoneNumberSymbol(prevCodePoint)) {
|
||||
arrayIdxOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prev);
|
||||
indexOffset = -1;
|
||||
textIdxOffset = -Character.charCount(prevCodePoint);
|
||||
skippedIndex = -1;
|
||||
}
|
||||
} else if (isZeroWidthJoin(prevCodePoint)) {
|
||||
int notValidControlCount = 0;
|
||||
int charCount = 0;
|
||||
for (int i = arrayIdx - 1; i >= 0; i--) {
|
||||
final int cp = array.get(i);
|
||||
charCount += Character.charCount(cp);
|
||||
if (isZeroWidthJoin(cp) || isVariationSelector(cp)) {
|
||||
// Ignore
|
||||
notValidControlCount = 0;
|
||||
continue;
|
||||
}
|
||||
notValidControlCount++;
|
||||
if (notValidControlCount > 1 || i == 0) {
|
||||
arrayIdxOffset = i - arrayIdx + 1;
|
||||
textIdxOffset = -charCount + Character.charCount(cp);
|
||||
skippedIndex = i - arrayIdx + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -96,7 +109,7 @@ public class EmojiSupportUtils {
|
||||
// Not emoji combination, just use fallback
|
||||
textIdxOffset = 0;
|
||||
arrayIdxOffset = 0;
|
||||
indexOffset = 0;
|
||||
skippedIndex = 0;
|
||||
spans = text.getSpans(textIdx + textIdxOffset, textEnd, EmojiSpan.class);
|
||||
if (spans.length == 0) {
|
||||
drawable = emoji.getEmojiDrawableFor(array.subarray(arrayIdx + arrayIdxOffset,
|
||||
@ -108,11 +121,19 @@ public class EmojiSupportUtils {
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
}
|
||||
i += indexOffset;
|
||||
arrayIdx += skippedIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isVariationSelector(int codePoint) {
|
||||
return codePoint == 0xfe0f;
|
||||
}
|
||||
|
||||
private static boolean isZeroWidthJoin(int codePoint) {
|
||||
return codePoint == 0x200d;
|
||||
}
|
||||
|
||||
private static boolean isPhoneNumberSymbol(int codePoint) {
|
||||
return codePoint == 0x0023 || codePoint == 0x002a || inRange(codePoint, 0x0030, 0x0039);
|
||||
}
|
||||
|
@ -6,8 +6,11 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.SSLCertificateSocketFactory;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.text.TextUtils;
|
||||
import android.webkit.URLUtil;
|
||||
|
||||
@ -33,6 +36,7 @@ import org.mariotaku.restfu.http.RestHttpResponse;
|
||||
import org.mariotaku.restfu.http.mime.StringTypedData;
|
||||
import org.mariotaku.restfu.http.mime.TypedData;
|
||||
import org.mariotaku.restfu.okhttp.OkHttpRestClient;
|
||||
import org.mariotaku.twidere.BuildConfig;
|
||||
import org.mariotaku.twidere.TwidereConstants;
|
||||
import org.mariotaku.twidere.api.twitter.Twitter;
|
||||
import org.mariotaku.twidere.api.twitter.TwitterException;
|
||||
@ -59,6 +63,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
@ -73,23 +78,27 @@ import static android.text.TextUtils.isEmpty;
|
||||
*/
|
||||
public class TwitterAPIFactory implements TwidereConstants {
|
||||
|
||||
@WorkerThread
|
||||
public static Twitter getDefaultTwitterInstance(final Context context, final boolean includeEntities) {
|
||||
if (context == null) return null;
|
||||
return getDefaultTwitterInstance(context, includeEntities, true);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static Twitter getDefaultTwitterInstance(final Context context, final boolean includeEntities,
|
||||
final boolean includeRetweets) {
|
||||
if (context == null) return null;
|
||||
return getTwitterInstance(context, Utils.getDefaultAccountId(context), includeEntities, includeRetweets);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static Twitter getTwitterInstance(final Context context, final long accountId,
|
||||
final boolean includeEntities) {
|
||||
return getTwitterInstance(context, accountId, includeEntities, true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@WorkerThread
|
||||
public static Twitter getTwitterInstance(final Context context, final long accountId,
|
||||
final boolean includeEntities,
|
||||
final boolean includeRetweets) {
|
||||
@ -97,6 +106,7 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@WorkerThread
|
||||
public static <T> T getTwitterInstance(final Context context, final long accountId,
|
||||
final boolean includeEntities,
|
||||
final boolean includeRetweets, Class<T> cls) {
|
||||
@ -164,6 +174,7 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
return Proxy.NO_PROXY;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint,
|
||||
final Authorization auth, final Map<String, String> extraRequestParams,
|
||||
final Class<T> cls) {
|
||||
@ -174,7 +185,7 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
final String consumerSecret = ((OAuthAuthorization) auth).getConsumerSecret();
|
||||
final ConsumerKeyType officialKeyType = TwitterContentUtils.getOfficialKeyType(context, consumerKey, consumerSecret);
|
||||
if (officialKeyType != ConsumerKeyType.UNKNOWN) {
|
||||
userAgent = getUserAgentName(officialKeyType);
|
||||
userAgent = getUserAgentName(context, officialKeyType);
|
||||
} else {
|
||||
userAgent = getTwidereUserAgent(context);
|
||||
}
|
||||
@ -191,18 +202,20 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
return factory.build(cls);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint,
|
||||
final Authorization auth, final Class<T> cls) {
|
||||
return getInstance(context, endpoint, auth, null, cls);
|
||||
}
|
||||
|
||||
|
||||
@WorkerThread
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint,
|
||||
final ParcelableCredentials credentials,
|
||||
final Class<T> cls) {
|
||||
return getInstance(context, endpoint, credentials, null, cls);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static <T> T getInstance(final Context context, final Endpoint endpoint,
|
||||
final ParcelableCredentials credentials,
|
||||
final Map<String, String> extraRequestParams, final Class<T> cls) {
|
||||
@ -210,11 +223,13 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
extraRequestParams, cls);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
static <T> T getInstance(final Context context, final ParcelableCredentials credentials,
|
||||
final Class<T> cls) {
|
||||
return getInstance(context, credentials, null, cls);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
static <T> T getInstance(final Context context, final ParcelableCredentials credentials,
|
||||
final Map<String, String> extraRequestParams, final Class<T> cls) {
|
||||
if (credentials == null) return null;
|
||||
@ -327,7 +342,6 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
|
||||
public static String getApiUrl(final String pattern, final String domain, final String appendPath) {
|
||||
final String urlBase = getApiBaseUrl(pattern, domain);
|
||||
if (urlBase == null) return null;
|
||||
if (appendPath == null) return urlBase.endsWith("/") ? urlBase : urlBase + "/";
|
||||
final StringBuilder sb = new StringBuilder(urlBase);
|
||||
if (urlBase.endsWith("/")) {
|
||||
@ -343,10 +357,22 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String getUserAgentName(ConsumerKeyType type) {
|
||||
@WorkerThread
|
||||
public static String getUserAgentName(Context context, ConsumerKeyType type) {
|
||||
switch (type) {
|
||||
case TWITTER_FOR_ANDROID: {
|
||||
return "TwitterAndroid";
|
||||
final String versionName = "5.2.4";
|
||||
final String internalVersionName = "524-r1";
|
||||
final String model = Build.MODEL;
|
||||
final String manufacturer = Build.MANUFACTURER;
|
||||
final int sdkInt = Build.VERSION.SDK_INT;
|
||||
final String device = Build.DEVICE;
|
||||
final String brand = Build.BRAND;
|
||||
final String product = Build.PRODUCT;
|
||||
final int debug = BuildConfig.DEBUG ? 1 : 0;
|
||||
return String.format(Locale.ROOT, "TwitterAndroid/%s (%s) %s/%d (%s;%s;%s;%s;%d)",
|
||||
versionName, internalVersionName, model, sdkInt, manufacturer, device, brand,
|
||||
product, debug);
|
||||
}
|
||||
case TWITTER_FOR_IPHONE: {
|
||||
return "Twitter-iPhone";
|
||||
@ -358,7 +384,7 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
return "Twitter-Mac";
|
||||
}
|
||||
case TWEETDECK: {
|
||||
return "TweetDeck";
|
||||
return UserAgentUtils.getDefaultUserAgentStringSafe(context);
|
||||
}
|
||||
}
|
||||
return "Twitter";
|
||||
@ -367,10 +393,10 @@ public class TwitterAPIFactory implements TwidereConstants {
|
||||
public static String getTwidereUserAgent(final Context context) {
|
||||
final PackageManager pm = context.getPackageManager();
|
||||
try {
|
||||
final PackageInfo pi = pm.getPackageInfo(TWIDERE_PACKAGE_NAME, 0);
|
||||
return TWIDERE_APP_NAME + " " + TWIDERE_PROJECT_URL + " / " + pi.versionName;
|
||||
final PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
|
||||
return String.format("%s %s / %s", TWIDERE_APP_NAME, TWIDERE_PROJECT_URL, pi.versionName);
|
||||
} catch (final PackageManager.NameNotFoundException e) {
|
||||
return TWIDERE_APP_NAME + " " + TWIDERE_PROJECT_URL;
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,11 @@ package org.mariotaku.twidere.util;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.annotation.WorkerThread;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
||||
@ -33,8 +37,10 @@ import java.lang.reflect.Constructor;
|
||||
*/
|
||||
public class UserAgentUtils {
|
||||
// You may uncomment next line if using Android Annotations library, otherwise just be sure to run it in on the UI thread
|
||||
@UiThread
|
||||
public static String getDefaultUserAgentString(Context context) {
|
||||
if (Looper.myLooper() != Looper.getMainLooper()) throw new IllegalStateException();
|
||||
if (Looper.myLooper() != Looper.getMainLooper())
|
||||
throw new IllegalStateException("User-Agent cannot be fetched from worker thread");
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
return NewApiWrapper.getDefaultUserAgent(context);
|
||||
@ -62,8 +68,49 @@ public class UserAgentUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@Nullable
|
||||
public static String getDefaultUserAgentStringSafe(Context context) {
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
final FetchUserAgentRunnable runnable = new FetchUserAgentRunnable(context);
|
||||
handler.post(runnable);
|
||||
runnable.waitForExecution();
|
||||
try {
|
||||
return runnable.getUserAgent();
|
||||
} finally {
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FetchUserAgentRunnable implements Runnable {
|
||||
|
||||
private final Context context;
|
||||
private String userAgent;
|
||||
private boolean userAgentSet;
|
||||
|
||||
public FetchUserAgentRunnable(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
userAgent = getDefaultUserAgentString(context);
|
||||
userAgentSet = true;
|
||||
}
|
||||
|
||||
public String getUserAgent() {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public void waitForExecution() {
|
||||
//noinspection StatementWithEmptyBody
|
||||
while (!userAgentSet) ;
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
static class NewApiWrapper {
|
||||
@UiThread
|
||||
static String getDefaultUserAgent(Context context) {
|
||||
return WebSettings.getDefaultUserAgent(context);
|
||||
}
|
||||
|
@ -92,19 +92,15 @@ public class TwidereDns implements Constants, Dns {
|
||||
return cachedHostAddr;
|
||||
}
|
||||
}
|
||||
// Then I'll try to load from custom host mapping.
|
||||
// Stupid way to find top domain, but really fast.
|
||||
if (mHostMapping.contains(host)) {
|
||||
final String mappedAddr = mHostMapping.getString(host, null);
|
||||
if (mappedAddr != null) {
|
||||
final InetAddress[] hostAddr = fromAddressString(originalHost, mappedAddr);
|
||||
putCache(originalHost, hostAddr);
|
||||
if (BuildConfig.DEBUG) {
|
||||
Log.v(RESOLVER_LOGTAG, "Got mapped " + Arrays.toString(hostAddr));
|
||||
}
|
||||
if (hostAddr != null) {
|
||||
return hostAddr;
|
||||
}
|
||||
final String customMappedHost = findHost(host);
|
||||
if (customMappedHost != null) {
|
||||
final InetAddress[] hostAddr = fromAddressString(originalHost, customMappedHost);
|
||||
putCache(originalHost, hostAddr);
|
||||
if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
|
||||
Log.v(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host);
|
||||
}
|
||||
if (hostAddr != null) {
|
||||
return hostAddr;
|
||||
}
|
||||
}
|
||||
try {
|
||||
@ -117,55 +113,12 @@ public class TwidereDns implements Constants, Dns {
|
||||
} catch (UnknownHostException e) {
|
||||
// Ignore
|
||||
}
|
||||
final String customMappedHost = findHost(host);
|
||||
if (customMappedHost != null) {
|
||||
final InetAddress[] hostAddr = fromAddressString(originalHost, customMappedHost);
|
||||
putCache(originalHost, hostAddr);
|
||||
if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
|
||||
Log.v(RESOLVER_LOGTAG, "Got mapped address " + customMappedHost + " for host " + host);
|
||||
}
|
||||
if (hostAddr != null) {
|
||||
return hostAddr;
|
||||
}
|
||||
}
|
||||
// Use TCP DNS Query if enabled.
|
||||
final Resolver dns = getResolver();
|
||||
if (dns != null && mPreferences.getBoolean(KEY_TCP_DNS_QUERY, false)) {
|
||||
final Lookup lookup = new Lookup(new Name(host), Type.A, DClass.IN);
|
||||
final Record[] records;
|
||||
lookup.setResolver(dns);
|
||||
lookup.run();
|
||||
final int result = lookup.getResult();
|
||||
if (result != Lookup.SUCCESSFUL) {
|
||||
throw new UnknownHostException("Unable to resolve " + host + ", " + lookup.getErrorString());
|
||||
}
|
||||
records = lookup.getAnswers();
|
||||
final ArrayList<InetAddress> resolvedAddresses = new ArrayList<>();
|
||||
// Test each IP address resolved.
|
||||
for (final Record record : records) {
|
||||
if (record instanceof ARecord) {
|
||||
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
|
||||
resolvedAddresses.add(InetAddress.getByAddress(originalHost, ipv4Addr.getAddress()));
|
||||
} else if (record instanceof AAAARecord) {
|
||||
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
|
||||
resolvedAddresses.add(InetAddress.getByAddress(originalHost, ipv6Addr.getAddress()));
|
||||
}
|
||||
}
|
||||
if (!resolvedAddresses.isEmpty()) {
|
||||
final InetAddress[] hostAddr = resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]);
|
||||
putCache(originalHost, hostAddr);
|
||||
if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
|
||||
Log.v(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr));
|
||||
}
|
||||
return hostAddr;
|
||||
}
|
||||
// No address is reachable, but I believe the IP is correct.
|
||||
|
||||
for (final Record record : records) {
|
||||
if (record instanceof CNAMERecord)
|
||||
return resolveInternal(originalHost, ((CNAMERecord) record).getTarget().toString());
|
||||
}
|
||||
}
|
||||
// final Resolver dns = getResolver();
|
||||
// if (dns != null && mPreferences.getBoolean(KEY_TCP_DNS_QUERY, false)) {
|
||||
// final InetAddress[] hostAddr = resolveDns(originalHost, host, dns);
|
||||
// if (hostAddr != null) return hostAddr;
|
||||
// }
|
||||
if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
|
||||
Log.v(RESOLVER_LOGTAG, "Resolve address " + host + " failed, using original host");
|
||||
}
|
||||
@ -174,6 +127,44 @@ public class TwidereDns implements Constants, Dns {
|
||||
return defaultAddresses;
|
||||
}
|
||||
|
||||
private InetAddress[] resolveDns(String originalHost, String host, Resolver dns) throws IOException {
|
||||
final Lookup lookup = new Lookup(new Name(host), Type.A, DClass.IN);
|
||||
final Record[] records;
|
||||
lookup.setResolver(dns);
|
||||
lookup.run();
|
||||
final int result = lookup.getResult();
|
||||
if (result != Lookup.SUCCESSFUL) {
|
||||
throw new UnknownHostException("Unable to resolve " + host + ", " + lookup.getErrorString());
|
||||
}
|
||||
records = lookup.getAnswers();
|
||||
final ArrayList<InetAddress> resolvedAddresses = new ArrayList<>();
|
||||
// Test each IP address resolved.
|
||||
for (final Record record : records) {
|
||||
if (record instanceof ARecord) {
|
||||
final InetAddress ipv4Addr = ((ARecord) record).getAddress();
|
||||
resolvedAddresses.add(InetAddress.getByAddress(originalHost, ipv4Addr.getAddress()));
|
||||
} else if (record instanceof AAAARecord) {
|
||||
final InetAddress ipv6Addr = ((AAAARecord) record).getAddress();
|
||||
resolvedAddresses.add(InetAddress.getByAddress(originalHost, ipv6Addr.getAddress()));
|
||||
}
|
||||
}
|
||||
if (!resolvedAddresses.isEmpty()) {
|
||||
final InetAddress[] hostAddr = resolvedAddresses.toArray(new InetAddress[resolvedAddresses.size()]);
|
||||
putCache(originalHost, hostAddr);
|
||||
if (BuildConfig.DEBUG && Log.isLoggable(RESOLVER_LOGTAG, Log.VERBOSE)) {
|
||||
Log.v(RESOLVER_LOGTAG, "Resolved " + Arrays.toString(hostAddr));
|
||||
}
|
||||
return hostAddr;
|
||||
}
|
||||
// No address is reachable, but I believe the IP is correct.
|
||||
|
||||
for (final Record record : records) {
|
||||
if (record instanceof CNAMERecord)
|
||||
return resolveInternal(originalHost, ((CNAMERecord) record).getTarget().toString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void putCache(String host, InetAddress[] addresses) {
|
||||
if (ArrayUtils.isEmpty(addresses) || ArrayUtils.contains(addresses, null)) return;
|
||||
mHostCache.put(host, addresses);
|
||||
@ -194,9 +185,7 @@ public class TwidereDns implements Constants, Dns {
|
||||
|
||||
private Resolver getResolver() throws IOException {
|
||||
if (mDns != null) return mDns;
|
||||
mDns = new SimpleResolver(mDnsAddress);
|
||||
mDns.setTCP(mPreferences.getBoolean(KEY_TCP_DNS_QUERY, false));
|
||||
return mDns;
|
||||
return mDns = new SimpleResolver(mDnsAddress);
|
||||
}
|
||||
|
||||
private static boolean hostMatches(final String host, final String rule) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user