mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-17 04:00:48 +01:00
kotlin migration for network diagnostics
This commit is contained in:
parent
e4193631da
commit
06aa7dc5ad
@ -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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -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++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<org.mariotaku.twidere.view.FixedTextView
|
<org.mariotaku.twidere.view.FixedTextView
|
||||||
android:id="@+id/log_text"
|
android:id="@+id/logText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
android:typeface="monospace"/>
|
android:typeface="monospace"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/start_diagnostics"
|
android:id="@+id/startDiagnostics"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="0"
|
android:layout_weight="0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user