From 06aa7dc5ad9ab7c5ddc5ace94bcaaf4d1b9b070d Mon Sep 17 00:00:00 2001 From: Mariotaku Lee Date: Sun, 21 May 2017 10:08:02 +0800 Subject: [PATCH] kotlin migration for network diagnostics --- .../fragment/NetworkDiagnosticsFragment.java | 421 ------------------ .../fragment/NetworkDiagnosticsFragment.kt | 348 +++++++++++++++ .../layout/fragment_network_diagnostics.xml | 4 +- 3 files changed, 350 insertions(+), 423 deletions(-) delete mode 100644 twidere/src/main/java/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.java create mode 100644 twidere/src/main/kotlin/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.kt diff --git a/twidere/src/main/java/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.java b/twidere/src/main/java/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.java deleted file mode 100644 index 210924404..000000000 --- a/twidere/src/main/java/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.java +++ /dev/null @@ -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 { - - private final WeakReference 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++; - } - } -} diff --git a/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.kt b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.kt new file mode 100644 index 000000000..f23ac1601 --- /dev/null +++ b/twidere/src/main/kotlin/org/mariotaku/twidere/fragment/NetworkDiagnosticsFragment.kt @@ -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() { + + 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 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++ + } + } +} diff --git a/twidere/src/main/res/layout/fragment_network_diagnostics.xml b/twidere/src/main/res/layout/fragment_network_diagnostics.xml index 11e096752..62bfa0866 100644 --- a/twidere/src/main/res/layout/fragment_network_diagnostics.xml +++ b/twidere/src/main/res/layout/fragment_network_diagnostics.xml @@ -6,7 +6,7 @@ android:orientation="vertical">