From 8c8bd746dc06fb51c335eac17d4891138e462143 Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sat, 26 Dec 2015 17:58:07 +0800 Subject: [PATCH] improved combined emoji improved user agent --- .../fragment/support/StatusFragment.java | 53 +++++++- .../twidere/text/style/EmojiSpan.java | 10 +- .../twidere/util/EmojiSupportUtils.java | 75 +++++++---- .../twidere/util/TwitterAPIFactory.java | 44 +++++-- .../twidere/util/UserAgentUtils.java | 49 +++++++- .../twidere/util/net/TwidereDns.java | 117 ++++++++---------- 6 files changed, 236 insertions(+), 112 deletions(-) diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java index 051d15663..64a7b32ec 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java +++ b/twidere/src/main/java/org/mariotaku/twidere/fragment/support/StatusFragment.java @@ -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 { 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 { diff --git a/twidere/src/main/java/org/mariotaku/twidere/text/style/EmojiSpan.java b/twidere/src/main/java/org/mariotaku/twidere/text/style/EmojiSpan.java index f3527ebda..56834c843 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/text/style/EmojiSpan.java +++ b/twidere/src/main/java/org/mariotaku/twidere/text/style/EmojiSpan.java @@ -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; } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java index 25fedede6..4a368b139 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/EmojiSupportUtils.java @@ -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); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java index 071ce02c4..44bdb7ec2 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/TwitterAPIFactory.java @@ -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 getTwitterInstance(final Context context, final long accountId, final boolean includeEntities, final boolean includeRetweets, Class cls) { @@ -164,6 +174,7 @@ public class TwitterAPIFactory implements TwidereConstants { return Proxy.NO_PROXY; } + @WorkerThread public static T getInstance(final Context context, final Endpoint endpoint, final Authorization auth, final Map extraRequestParams, final Class 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 getInstance(final Context context, final Endpoint endpoint, final Authorization auth, final Class cls) { return getInstance(context, endpoint, auth, null, cls); } - + @WorkerThread public static T getInstance(final Context context, final Endpoint endpoint, final ParcelableCredentials credentials, final Class cls) { return getInstance(context, endpoint, credentials, null, cls); } + @WorkerThread public static T getInstance(final Context context, final Endpoint endpoint, final ParcelableCredentials credentials, final Map extraRequestParams, final Class cls) { @@ -210,11 +223,13 @@ public class TwitterAPIFactory implements TwidereConstants { extraRequestParams, cls); } + @WorkerThread static T getInstance(final Context context, final ParcelableCredentials credentials, final Class cls) { return getInstance(context, credentials, null, cls); } + @WorkerThread static T getInstance(final Context context, final ParcelableCredentials credentials, final Map extraRequestParams, final Class 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); } } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/UserAgentUtils.java b/twidere/src/main/java/org/mariotaku/twidere/util/UserAgentUtils.java index c965b9970..c3bbb7986 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/UserAgentUtils.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/UserAgentUtils.java @@ -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); } diff --git a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java index 52b2bba8a..568f0792e 100644 --- a/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java +++ b/twidere/src/main/java/org/mariotaku/twidere/util/net/TwidereDns.java @@ -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 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 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) {