kotlin migration for network diagnostics

This commit is contained in:
Mariotaku Lee 2017-05-21 10:08:02 +08:00
parent e4193631da
commit 06aa7dc5ad
No known key found for this signature in database
GPG Key ID: 99505AEA531814F1
3 changed files with 350 additions and 423 deletions

View File

@ -1,421 +0,0 @@
package org.mariotaku.twidere.fragment;
import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.Selection;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.ScrollingMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.Paging;
import org.mariotaku.restfu.RestAPIFactory;
import org.mariotaku.restfu.RestFuUtils;
import org.mariotaku.restfu.annotation.method.GET;
import org.mariotaku.restfu.http.Endpoint;
import org.mariotaku.restfu.http.HttpRequest;
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.extension.model.CredentialsExtensionsKt;
import org.mariotaku.twidere.model.AccountDetails;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.account.cred.OAuthCredentials;
import org.mariotaku.twidere.model.util.AccountUtils;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.dagger.DependencyHolder;
import org.mariotaku.twidere.util.net.TwidereDns;
import org.xbill.DNS.ResolverConfig;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Locale;
import okhttp3.Dns;
import static org.mariotaku.twidere.Constants.DEFAULT_TWITTER_API_URL_FORMAT;
import static org.mariotaku.twidere.Constants.KEY_BUILTIN_DNS_RESOLVER;
import static org.mariotaku.twidere.Constants.KEY_DNS_SERVER;
import static org.mariotaku.twidere.Constants.KEY_TCP_DNS_QUERY;
/**
* Network diagnostics
* Created by mariotaku on 16/2/9.
*/
public class NetworkDiagnosticsFragment extends BaseFragment {
private TextView mLogTextView;
private Button mStartDiagnosticsButton;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mStartDiagnosticsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mLogTextView.setText(null);
new DiagnosticsTask(NetworkDiagnosticsFragment.this).execute();
}
});
mLogTextView.setMovementMethod(ScrollingMovementMethod.getInstance());
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStartDiagnosticsButton = (Button) view.findViewById(R.id.start_diagnostics);
mLogTextView = (TextView) view.findViewById(R.id.log_text);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_network_diagnostics, container, false);
}
private void appendMessage(LogText message) {
final Activity activity = getActivity();
if (activity == null) return;
SpannableString coloredText = SpannableString.valueOf(message.message);
switch (message.state) {
case LogText.State.OK: {
coloredText.setSpan(new ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_light_green)), 0, coloredText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
}
case LogText.State.ERROR: {
coloredText.setSpan(new ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_red)), 0, coloredText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
}
case LogText.State.WARNING: {
coloredText.setSpan(new ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_amber)), 0, coloredText.length(),
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
break;
}
case LogText.State.DEFAULT:
break;
}
mLogTextView.append(coloredText);
Selection.setSelection(mLogTextView.getEditableText(), mLogTextView.length());
}
static class DiagnosticsTask extends AsyncTask<Object, LogText, Object> {
private final WeakReference<NetworkDiagnosticsFragment> mFragmentRef;
private final Context mContext;
private final ConnectivityManager mConnectivityManager;
DiagnosticsTask(NetworkDiagnosticsFragment fragment) {
mFragmentRef = new WeakReference<>(fragment);
mContext = fragment.getActivity().getApplicationContext();
mConnectivityManager = (ConnectivityManager)
mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
@Override
protected Object doInBackground(Object... params) {
publishProgress(new LogText("**** NOTICE ****", LogText.State.WARNING));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Text below may have personal information, BE CAREFUL TO MAKE IT PUBLIC",
LogText.State.WARNING));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
DependencyHolder holder = DependencyHolder.Companion.get(mContext);
final Dns dns = holder.getDns();
final SharedPreferences prefs = holder.getPreferences();
publishProgress(new LogText("Network preferences"), LogText.LINEBREAK);
publishProgress(new LogText("using_resolver: " + prefs.getBoolean(KEY_BUILTIN_DNS_RESOLVER, false)), LogText.LINEBREAK);
publishProgress(new LogText("tcp_dns_query: " + prefs.getBoolean(KEY_TCP_DNS_QUERY, false)), LogText.LINEBREAK);
publishProgress(new LogText("dns_server: " + prefs.getString(KEY_DNS_SERVER, null)), LogText.LINEBREAK);
publishProgress(LogText.LINEBREAK);
publishProgress(new LogText("System DNS servers"), LogText.LINEBREAK);
final String[] servers = ResolverConfig.getCurrentConfig().servers();
if (servers != null) {
publishProgress(new LogText(Arrays.toString(servers)));
} else {
publishProgress(new LogText("null"));
}
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
for (UserKey accountKey : DataStoreUtils.INSTANCE.getAccountKeys(mContext)) {
final AccountDetails details = AccountUtils.getAccountDetails(AccountManager.get(mContext), accountKey, true);
final MicroBlog twitter = MicroBlogAPIFactory.getInstance(mContext, accountKey);
if (details == null || twitter == null) continue;
publishProgress(new LogText("Testing connection for account " + accountKey));
publishProgress(LogText.LINEBREAK);
publishProgress(new LogText("api_url_format: " + details.credentials.api_url_format), LogText.LINEBREAK);
if (details.credentials instanceof OAuthCredentials) {
publishProgress(new LogText("same_oauth_signing_url: " + ((OAuthCredentials)
details.credentials).same_oauth_signing_url), LogText.LINEBREAK);
}
publishProgress(new LogText("auth_type: " + details.credentials_type));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Testing DNS functionality"));
publishProgress(LogText.LINEBREAK);
final Endpoint endpoint = CredentialsExtensionsKt.getEndpoint(details.credentials, MicroBlog.class);
final Uri uri = Uri.parse(endpoint.getUrl());
final String host = uri.getHost();
if (host != null) {
testDns(dns, host);
testNativeLookup(host);
} else {
publishProgress(new LogText("API URL format is invalid", LogText.State.ERROR));
publishProgress(LogText.LINEBREAK);
}
publishProgress(LogText.LINEBREAK);
publishProgress(new LogText("Testing Network connectivity"));
publishProgress(LogText.LINEBREAK);
final String baseUrl;
if (details.credentials.api_url_format != null) {
baseUrl = MicroBlogAPIFactory.getApiBaseUrl(details.credentials.api_url_format, "api");
} else {
baseUrl = MicroBlogAPIFactory.getApiBaseUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api");
}
RestHttpClient client = RestAPIFactory.getRestClient(twitter).getRestClient();
HttpResponse response = null;
try {
publishProgress(new LogText("Connecting to " + baseUrl + "..."));
HttpRequest.Builder builder = new HttpRequest.Builder();
builder.method(GET.METHOD);
builder.url(baseUrl);
final long start = SystemClock.uptimeMillis();
response = client.newCall(builder.build()).execute();
publishProgress(new LogText(String.format(Locale.US, " OK (%d ms)",
SystemClock.uptimeMillis() - start), LogText.State.OK));
} catch (IOException e) {
publishProgress(new LogText("ERROR: " + e.getMessage(), LogText.State.ERROR));
}
publishProgress(LogText.LINEBREAK);
try {
if (response != null) {
publishProgress(new LogText("Reading response..."));
final long start = SystemClock.uptimeMillis();
final CountOutputStream os = new CountOutputStream();
response.getBody().writeTo(os);
publishProgress(new LogText(String.format(Locale.US, " %d bytes (%d ms)",
os.getTotal(), SystemClock.uptimeMillis() - start), LogText.State.OK));
}
} catch (IOException e) {
publishProgress(new LogText("ERROR: " + e.getMessage(), LogText.State.ERROR));
} finally {
RestFuUtils.closeSilently(response);
}
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Testing API functionality"));
publishProgress(LogText.LINEBREAK);
testTwitter("verify_credentials", twitter, new TwitterTest() {
@Override
public void execute(MicroBlog twitter) throws MicroBlogException {
twitter.verifyCredentials();
}
});
testTwitter("get_home_timeline", twitter, new TwitterTest() {
@Override
public void execute(MicroBlog twitter) throws MicroBlogException {
twitter.getHomeTimeline(new Paging().count(1));
}
});
publishProgress(LogText.LINEBREAK);
}
publishProgress(LogText.LINEBREAK);
publishProgress(new LogText("Testing common host names"));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
testDns(dns, "www.google.com");
testNativeLookup("www.google.com");
publishProgress(LogText.LINEBREAK);
testDns(dns, "github.com");
testNativeLookup("github.com");
publishProgress(LogText.LINEBREAK);
testDns(dns, "twitter.com");
testNativeLookup("twitter.com");
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Build information: "));
publishProgress(new LogText("version_code: " + BuildConfig.VERSION_CODE), LogText.LINEBREAK);
publishProgress(new LogText("version_name: " + BuildConfig.VERSION_NAME), LogText.LINEBREAK);
publishProgress(new LogText("flavor: " + BuildConfig.FLAVOR), LogText.LINEBREAK);
publishProgress(new LogText("debug: " + BuildConfig.DEBUG), LogText.LINEBREAK);
publishProgress(LogText.LINEBREAK);
publishProgress(new LogText("Basic system information: "));
publishProgress(new LogText(String.valueOf(mContext.getResources().getConfiguration())));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Active network info: "));
publishProgress(new LogText(String.valueOf(mConnectivityManager.getActiveNetworkInfo())));
publishProgress(LogText.LINEBREAK, LogText.LINEBREAK);
publishProgress(new LogText("Done. You can send this log to me, and I'll contact you to solve related issue."));
return null;
}
private void testDns(Dns dns, final String host) {
publishProgress(new LogText(String.format("Lookup %s...", host)));
try {
final long start = SystemClock.uptimeMillis();
if (dns instanceof TwidereDns) {
publishProgress(new LogText(String.valueOf(((TwidereDns) dns).lookupResolver(host))));
} else {
publishProgress(new LogText(String.valueOf(dns.lookup(host))));
}
publishProgress(new LogText(String.format(Locale.US, " OK (%d ms)",
SystemClock.uptimeMillis() - start), LogText.State.OK));
} catch (UnknownHostException e) {
publishProgress(new LogText("ERROR: " + e.getMessage(), LogText.State.ERROR));
}
publishProgress(LogText.LINEBREAK);
}
private void testNativeLookup(final String host) {
publishProgress(new LogText(String.format("Native lookup %s...", host)));
try {
final long start = SystemClock.uptimeMillis();
publishProgress(new LogText(Arrays.toString(InetAddress.getAllByName(host))));
publishProgress(new LogText(String.format(Locale.US, " OK (%d ms)",
SystemClock.uptimeMillis() - start), LogText.State.OK));
} catch (UnknownHostException e) {
publishProgress(new LogText("ERROR: " + e.getMessage(), LogText.State.ERROR));
}
publishProgress(LogText.LINEBREAK);
}
private void testTwitter(String name, MicroBlog twitter, TwitterTest test) {
publishProgress(new LogText(String.format("Testing %s...", name)));
try {
final long start = SystemClock.uptimeMillis();
test.execute(twitter);
publishProgress(new LogText(String.format(Locale.US, "OK (%d ms)",
SystemClock.uptimeMillis() - start), LogText.State.OK));
} catch (MicroBlogException e) {
publishProgress(new LogText("ERROR: " + e.getMessage(), LogText.State.ERROR));
}
publishProgress(LogText.LINEBREAK);
}
interface TwitterTest {
void execute(MicroBlog twitter) throws MicroBlogException;
}
@Override
protected void onProgressUpdate(LogText... values) {
NetworkDiagnosticsFragment fragment = mFragmentRef.get();
if (fragment == null) return;
for (LogText value : values) {
fragment.appendMessage(value);
}
}
@Override
protected void onPreExecute() {
NetworkDiagnosticsFragment fragment = mFragmentRef.get();
if (fragment == null) return;
fragment.diagStart();
super.onPreExecute();
}
@Override
protected void onPostExecute(Object o) {
NetworkDiagnosticsFragment fragment = mFragmentRef.get();
if (fragment == null) return;
fragment.logReady();
super.onPostExecute(o);
}
}
private void diagStart() {
mStartDiagnosticsButton.setText(R.string.message_please_wait);
mStartDiagnosticsButton.setEnabled(false);
}
private void logReady() {
mStartDiagnosticsButton.setText(R.string.action_send);
mStartDiagnosticsButton.setEnabled(true);
mStartDiagnosticsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Twidere Network Diagnostics");
intent.putExtra(android.content.Intent.EXTRA_TEXT, mLogTextView.getText());
startActivity(Intent.createChooser(intent, getString(R.string.action_send)));
}
});
}
static class LogText {
static final LogText LINEBREAK = new LogText("\n");
String message;
@State
int state = State.DEFAULT;
LogText(String message, @State int state) {
this.message = message;
this.state = state;
}
LogText(String message) {
this.message = message;
}
@IntDef({State.DEFAULT, State.OK, State.ERROR, State.WARNING})
@interface State {
int DEFAULT = 0;
int OK = 1;
int ERROR = 2;
int WARNING = 3;
}
}
private static class CountOutputStream extends OutputStream {
private long total;
public long getTotal() {
return total;
}
@Override
public void write(int oneByte) throws IOException {
total++;
}
}
}

View File

@ -0,0 +1,348 @@
package org.mariotaku.twidere.fragment
import android.accounts.AccountManager
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.net.Uri
import android.os.AsyncTask
import android.os.Bundle
import android.os.SystemClock
import android.support.v4.content.ContextCompat
import android.text.Selection
import android.text.SpannableString
import android.text.Spanned
import android.text.method.ScrollingMovementMethod
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_network_diagnostics.*
import okhttp3.Dns
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.mastodon.Mastodon
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.restfu.RestFuUtils
import org.mariotaku.restfu.annotation.method.GET
import org.mariotaku.restfu.http.HttpRequest
import org.mariotaku.restfu.http.HttpResponse
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.DEFAULT_TWITTER_API_URL_FORMAT
import org.mariotaku.twidere.R
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.SharedPreferenceConstants.*
import org.mariotaku.twidere.extension.model.getEndpoint
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.account.cred.OAuthCredentials
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.dagger.DependencyHolder
import org.mariotaku.twidere.util.net.TwidereDns
import org.xbill.DNS.ResolverConfig
import java.io.IOException
import java.io.OutputStream
import java.lang.ref.WeakReference
import java.net.InetAddress
import java.util.*
/**
* Network diagnostics
* Created by mariotaku on 16/2/9.
*/
class NetworkDiagnosticsFragment : BaseFragment() {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
startDiagnostics.setOnClickListener {
logText.text = null
DiagnosticsTask(this@NetworkDiagnosticsFragment).execute()
}
logText.movementMethod = ScrollingMovementMethod.getInstance()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_network_diagnostics, container, false)
}
private fun appendMessage(message: LogText) {
val activity = activity ?: return
val coloredText = SpannableString.valueOf(message.message)
when (message.state) {
LogText.State.OK -> {
coloredText.setSpan(ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_light_green)), 0, coloredText.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
LogText.State.ERROR -> {
coloredText.setSpan(ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_red)), 0, coloredText.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
LogText.State.WARNING -> {
coloredText.setSpan(ForegroundColorSpan(ContextCompat.getColor(activity,
R.color.material_amber)), 0, coloredText.length,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
}
LogText.State.DEFAULT -> {
}
}
logText.append(coloredText)
Selection.setSelection(logText.editableText, logText.length())
}
internal class DiagnosticsTask(fragment: NetworkDiagnosticsFragment) : AsyncTask<Any, LogText, Unit>() {
private val fragmentRef = WeakReference(fragment)
private val context = fragment.activity.applicationContext
private val connectivityManager: ConnectivityManager
init {
connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
}
override fun doInBackground(vararg params: Any) {
logPrintln("**** NOTICE ****", LogText.State.WARNING)
logPrintln()
logPrintln("Text below may have personal information, BE CAREFUL TO MAKE IT PUBLIC",
LogText.State.WARNING)
logPrintln()
val holder = DependencyHolder.get(context)
val dns = holder.dns
val prefs = holder.preferences
logPrintln(("Network preferences"))
logPrintln(("using_resolver: ${prefs.getBoolean(KEY_BUILTIN_DNS_RESOLVER, false)}"))
logPrintln(("tcp_dns_query: ${prefs.getBoolean(KEY_TCP_DNS_QUERY, false)}"))
logPrintln(("dns_server: ${prefs.getString(KEY_DNS_SERVER, null)}"))
logPrintln()
logPrintln(("System DNS servers"))
val servers = ResolverConfig.getCurrentConfig().servers()
if (servers != null) {
logPrintln(Arrays.toString(servers))
} else {
logPrintln("null")
}
logPrintln()
for (accountKey in DataStoreUtils.getAccountKeys(context)) {
val details = AccountUtils.getAccountDetails(AccountManager.get(context),
accountKey, true) ?: continue
logPrintln(("Testing connection for account $accountKey"))
logPrintln()
logPrintln(("api_url_format: ${details.credentials.api_url_format}"))
(details.credentials as? OAuthCredentials)?.let { creds ->
logPrintln(("same_oauth_signing_url: ${creds.same_oauth_signing_url}"))
}
logPrintln(("auth_type: " + details.credentials_type))
logPrintln()
logPrintln(("Testing DNS functionality"))
logPrintln()
val endpoint = details.credentials.getEndpoint(MicroBlog::class.java)
val uri = Uri.parse(endpoint.url)
val host = uri.host
if (host != null) {
testDns(dns, host)
testNativeLookup(host)
} else {
logPrintln("API URL format is invalid", LogText.State.ERROR)
logPrintln()
}
logPrintln()
logPrintln(("Testing Network connectivity"))
logPrintln()
val baseUrl: String
if (details.credentials.api_url_format != null) {
baseUrl = MicroBlogAPIFactory.getApiBaseUrl(details.credentials.api_url_format, "api")
} else {
baseUrl = MicroBlogAPIFactory.getApiBaseUrl(DEFAULT_TWITTER_API_URL_FORMAT, "api")
}
val client = DependencyHolder.get(context).restHttpClient
var response: HttpResponse? = null
try {
logPrint("Connecting to $baseUrl...")
val builder = HttpRequest.Builder()
builder.method(GET.METHOD)
builder.url(baseUrl)
val start = SystemClock.uptimeMillis()
response = client.newCall(builder.build()).execute()
logPrint(" OK (${SystemClock.uptimeMillis() - start} ms)")
} catch (e: IOException) {
logPrint("ERROR: ${e.message}", LogText.State.ERROR)
}
logPrintln()
try {
if (response != null) {
logPrintln("Reading response...")
val start = SystemClock.uptimeMillis()
val os = CountOutputStream()
response.body.writeTo(os)
logPrintln(" ${os.total} bytes (${SystemClock.uptimeMillis() - start} ms)", LogText.State.OK)
}
} catch (e: IOException) {
logPrintln("ERROR: ${e.message}", LogText.State.ERROR)
} finally {
RestFuUtils.closeSilently(response)
}
logPrintln()
logPrintln(("Testing API functionality"))
logPrintln()
when (details.type) {
AccountType.MASTODON -> {
val mastodon = details.newMicroBlogInstance(context, Mastodon::class.java)
testAPICall("verify_credentials", mastodon) {
verifyCredentials()
}
testAPICall("get_home_timeline", mastodon) {
getHomeTimeline(Paging().count(1))
}
}
else -> {
val microBlog = details.newMicroBlogInstance(context, MicroBlog::class.java)
testAPICall("verify_credentials", microBlog) {
verifyCredentials()
}
testAPICall("get_home_timeline", microBlog) {
getHomeTimeline(Paging().count(1))
}
}
}
logPrintln()
}
logPrintln()
logPrintln(("Testing common host names"))
logPrintln()
testDns(dns, "www.google.com")
testNativeLookup("www.google.com")
logPrintln()
testDns(dns, "github.com")
testNativeLookup("github.com")
logPrintln()
testDns(dns, "twitter.com")
testNativeLookup("twitter.com")
logPrintln()
logPrintln("Build information: ")
logPrintln("version_code: ${BuildConfig.VERSION_CODE}")
logPrintln("version_name: ${BuildConfig.VERSION_NAME}")
logPrintln("flavor: ${BuildConfig.FLAVOR}")
logPrintln("debug: ${BuildConfig.DEBUG}")
logPrintln()
logPrintln(("Basic system information: "))
logPrintln(context.resources.configuration.toString())
logPrintln()
logPrintln(("Active network info: "))
logPrintln((connectivityManager.activeNetworkInfo.toString()))
}
override fun onProgressUpdate(vararg values: LogText) {
val fragment = fragmentRef.get() ?: return
for (value in values) {
fragment.appendMessage(value)
}
}
override fun onPreExecute() {
val fragment = fragmentRef.get() ?: return
fragment.diagStart()
super.onPreExecute()
}
override fun onPostExecute(u: Unit) {
val fragment = fragmentRef.get() ?: return
logPrintln()
logPrintln(("Done. You can send this log to me, and I'll contact you to solve related issue."))
fragment.logReady()
}
private fun testDns(dns: Dns, host: String) {
testCall("builtin lookup $host") {
if (dns is TwidereDns) {
logPrint((dns.lookupResolver(host).toString()))
} else {
logPrint((dns.lookup(host).toString()))
}
}
}
private fun testNativeLookup(host: String) {
testCall("native lookup $host") {
logPrint(Arrays.toString(InetAddress.getAllByName(host)))
}
}
private fun <T> testAPICall(name: String, api: T, test: T.() -> Unit) {
testCall(name) { test(api) }
}
private inline fun testCall(name: String, test: () -> Unit) {
logPrint("Testing $name...")
try {
val start = SystemClock.uptimeMillis()
test()
logPrint("OK (${SystemClock.uptimeMillis() - start} ms)", LogText.State.OK)
} catch (e: Exception) {
logPrint("ERROR: ${e.message}", LogText.State.ERROR)
}
logPrintln()
}
private fun logPrint(text: CharSequence, state: LogText.State = LogText.State.DEFAULT) {
publishProgress(LogText(text, state))
}
private fun logPrintln(text: CharSequence = "", state: LogText.State = LogText.State.DEFAULT) {
logPrint("$text\n", state)
}
}
private fun diagStart() {
startDiagnostics.setText(R.string.message_please_wait)
startDiagnostics.isEnabled = false
}
private fun logReady() {
startDiagnostics.setText(R.string.action_send)
startDiagnostics.isEnabled = true
startDiagnostics.setOnClickListener {
val intent = Intent(Intent.ACTION_SEND)
intent.type = "text/plain"
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Twidere Network Diagnostics")
intent.putExtra(android.content.Intent.EXTRA_TEXT, logText.text)
startActivity(Intent.createChooser(intent, getString(R.string.action_send)))
}
}
internal data class LogText(val message: CharSequence, var state: State = State.DEFAULT) {
internal enum class State {
DEFAULT, OK, ERROR, WARNING
}
}
private class CountOutputStream : OutputStream() {
var total: Long = 0
private set
@Throws(IOException::class)
override fun write(oneByte: Int) {
total++
}
}
}

View File

@ -6,7 +6,7 @@
android:orientation="vertical">
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/log_text"
android:id="@+id/logText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
@ -18,7 +18,7 @@
android:typeface="monospace"/>
<Button
android:id="@+id/start_diagnostics"
android:id="@+id/startDiagnostics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="0"