Finishes handshake-test-2
This commit is contained in:
parent
fd472fbe1f
commit
48c9b71f92
6 changed files with 263 additions and 150 deletions
|
@ -38,7 +38,6 @@ dependencies {
|
|||
compile 'com.mikhaellopez:circularfillableloaders:1.2.0'
|
||||
compile 'com.squareup.retrofit2:retrofit:2.2.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
|
||||
compile 'com.squareup.okhttp3:logging-interceptor:3.6.0'
|
||||
compile 'com.github.chrisbanes:PhotoView:1.3.1'
|
||||
compile 'com.mikepenz:google-material-typeface:3.0.1.0.original@aar'
|
||||
compile 'com.github.arimorty:floatingsearchview:2.0.3'
|
||||
|
|
|
@ -34,10 +34,7 @@ import com.google.gson.Gson;
|
|||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.Dispatcher;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
@ -121,9 +118,29 @@ public class BaseActivity extends AppCompatActivity {
|
|||
.registerTypeAdapter(Spanned.class, new SpannedTypeAdapter())
|
||||
.create();
|
||||
|
||||
OkHttpClient okHttpClient = OkHttpUtils.getCompatibleClientBuilder()
|
||||
.addInterceptor(new Interceptor() {
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
|
||||
Request.Builder builder = originalRequest.newBuilder();
|
||||
String accessToken = getAccessToken();
|
||||
if (accessToken != null) {
|
||||
builder.header("Authorization", String.format("Bearer %s",
|
||||
accessToken));
|
||||
}
|
||||
Request newRequest = builder.build();
|
||||
|
||||
return chain.proceed(newRequest);
|
||||
}
|
||||
})
|
||||
.dispatcher(mastodonApiDispatcher)
|
||||
.build();
|
||||
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl(getBaseUrl())
|
||||
.client(OkHttpUtils.getCompatibleClient())
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build();
|
||||
|
||||
|
|
|
@ -18,10 +18,15 @@ package com.keylesspalace.tusky;
|
|||
/**A wrapper for android.util.Log that allows for disabling logging, such as for release builds.*/
|
||||
public class Log {
|
||||
private static final boolean LOGGING_ENABLED = BuildConfig.DEBUG;
|
||||
private static String longBoy;
|
||||
private static String watchedTag;
|
||||
|
||||
public static void i(String tag, String string) {
|
||||
if (LOGGING_ENABLED) {
|
||||
android.util.Log.i(tag, string);
|
||||
if (tag.equals(watchedTag)) {
|
||||
longBoy += string + '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,4 +53,14 @@ public class Log {
|
|||
android.util.Log.w(tag, string);
|
||||
}
|
||||
}
|
||||
|
||||
static void watchTag(String tag) {
|
||||
longBoy = "";
|
||||
watchedTag = tag;
|
||||
}
|
||||
|
||||
static String getWatchedMessages() {
|
||||
watchedTag = null;
|
||||
return longBoy;
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import android.content.pm.PackageManager;
|
|||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.View;
|
||||
|
@ -58,126 +59,7 @@ public class LoginActivity extends AppCompatActivity {
|
|||
@BindView(R.id.edit_text_domain) EditText editText;
|
||||
@BindView(R.id.button_login) Button button;
|
||||
@BindView(R.id.whats_an_instance) TextView whatsAnInstance;
|
||||
|
||||
/**
|
||||
* Chain together the key-value pairs into a query string, for either appending to a URL or
|
||||
* as the content of an HTTP request.
|
||||
*/
|
||||
private static String toQueryString(Map<String, String> parameters) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
String between = "";
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
s.append(between);
|
||||
s.append(Uri.encode(entry.getKey()));
|
||||
s.append("=");
|
||||
s.append(Uri.encode(entry.getValue()));
|
||||
between = "&";
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
/** Make sure the user-entered text is just a fully-qualified domain name. */
|
||||
private static String validateDomain(String s) {
|
||||
// Strip any schemes out.
|
||||
s = s.replaceFirst("http://", "");
|
||||
s = s.replaceFirst("https://", "");
|
||||
// If a username was included (e.g. username@example.com), just take what's after the '@'.
|
||||
int at = s.indexOf('@');
|
||||
if (at != -1) {
|
||||
s = s.substring(at + 1);
|
||||
}
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
private String getOauthRedirectUri() {
|
||||
String scheme = getString(R.string.oauth_scheme);
|
||||
String host = getString(R.string.oauth_redirect_host);
|
||||
return scheme + "://" + host + "/";
|
||||
}
|
||||
|
||||
private void redirectUserToAuthorizeAndLogin(EditText editText) {
|
||||
/* To authorize this app and log in it's necessary to redirect to the domain given,
|
||||
* activity_login there, and the server will redirect back to the app with its response. */
|
||||
String endpoint = MastodonAPI.ENDPOINT_AUTHORIZE;
|
||||
String redirectUri = getOauthRedirectUri();
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put("client_id", clientId);
|
||||
parameters.put("redirect_uri", redirectUri);
|
||||
parameters.put("response_type", "code");
|
||||
parameters.put("scope", OAUTH_SCOPES);
|
||||
String url = "https://" + domain + endpoint + "?" + toQueryString(parameters);
|
||||
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
if (viewIntent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(viewIntent);
|
||||
} else {
|
||||
editText.setError(getString(R.string.error_no_web_browser_found));
|
||||
}
|
||||
}
|
||||
|
||||
private MastodonAPI getApiFor(String domain) {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://" + domain)
|
||||
.client(OkHttpUtils.getCompatibleClient())
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
|
||||
return retrofit.create(MastodonAPI.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the oauth client credentials for this app. This is only necessary the first time the
|
||||
* app is run on a given server instance. So, after the first authentication, they are
|
||||
* saved in SharedPreferences and every subsequent run they are simply fetched from there.
|
||||
*/
|
||||
private void onButtonClick(final EditText editText) {
|
||||
domain = validateDomain(editText.getText().toString());
|
||||
/* Attempt to get client credentials from SharedPreferences, and if not present
|
||||
* (such as in the case that the domain has never been accessed before)
|
||||
* authenticate with the server and store the received credentials to use next
|
||||
* time. */
|
||||
String prefClientId = preferences.getString(domain + "/client_id", null);
|
||||
String prefClientSecret = preferences.getString(domain + "/client_secret", null);
|
||||
|
||||
if (prefClientId != null && prefClientSecret != null) {
|
||||
clientId = prefClientId;
|
||||
clientSecret = prefClientSecret;
|
||||
redirectUserToAuthorizeAndLogin(editText);
|
||||
} else {
|
||||
Callback<AppCredentials> callback = new Callback<AppCredentials>() {
|
||||
@Override
|
||||
public void onResponse(Call<AppCredentials> call, Response<AppCredentials> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
editText.setError(getString(R.string.error_failed_app_registration));
|
||||
Log.e(TAG, "App authentication failed. " + response.message());
|
||||
return;
|
||||
}
|
||||
AppCredentials credentials = response.body();
|
||||
clientId = credentials.clientId;
|
||||
clientSecret = credentials.clientSecret;
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString(domain + "/client_id", clientId);
|
||||
editor.putString(domain + "/client_secret", clientSecret);
|
||||
editor.apply();
|
||||
redirectUserToAuthorizeAndLogin(editText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<AppCredentials> call, Throwable t) {
|
||||
editText.setError(getString(R.string.error_failed_app_registration));
|
||||
t.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
getApiFor(domain)
|
||||
.authenticateApp(getString(R.string.app_name), getOauthRedirectUri(),
|
||||
OAUTH_SCOPES, getString(R.string.app_website))
|
||||
.enqueue(callback);
|
||||
} catch (IllegalArgumentException e) {
|
||||
editText.setError(getString(R.string.error_invalid_domain));
|
||||
}
|
||||
}
|
||||
}
|
||||
@BindView(R.id.debug_log_display) TextView debugLogDisplay;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -259,14 +141,133 @@ public class LoginActivity extends AppCompatActivity {
|
|||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
private void onLoginSuccess(String accessToken) {
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString("domain", domain);
|
||||
editor.putString("accessToken", accessToken);
|
||||
editor.commit();
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
/** Make sure the user-entered text is just a fully-qualified domain name. */
|
||||
@NonNull
|
||||
private static String validateDomain(String s) {
|
||||
// Strip any schemes out.
|
||||
s = s.replaceFirst("http://", "");
|
||||
s = s.replaceFirst("https://", "");
|
||||
// If a username was included (e.g. username@example.com), just take what's after the '@'.
|
||||
int at = s.indexOf('@');
|
||||
if (at != -1) {
|
||||
s = s.substring(at + 1);
|
||||
}
|
||||
return s.trim();
|
||||
}
|
||||
|
||||
private String getOauthRedirectUri() {
|
||||
String scheme = getString(R.string.oauth_scheme);
|
||||
String host = getString(R.string.oauth_redirect_host);
|
||||
return scheme + "://" + host + "/";
|
||||
}
|
||||
|
||||
private MastodonAPI getApiFor(String domain) {
|
||||
Retrofit retrofit = new Retrofit.Builder()
|
||||
.baseUrl("https://" + domain)
|
||||
.client(OkHttpUtils.getCompatibleClient())
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
|
||||
return retrofit.create(MastodonAPI.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the oauth client credentials for this app. This is only necessary the first time the
|
||||
* app is run on a given server instance. So, after the first authentication, they are
|
||||
* saved in SharedPreferences and every subsequent run they are simply fetched from there.
|
||||
*/
|
||||
private void onButtonClick(final EditText editText) {
|
||||
domain = validateDomain(editText.getText().toString());
|
||||
/* Attempt to get client credentials from SharedPreferences, and if not present
|
||||
* (such as in the case that the domain has never been accessed before)
|
||||
* authenticate with the server and store the received credentials to use next
|
||||
* time. */
|
||||
String prefClientId = preferences.getString(domain + "/client_id", null);
|
||||
String prefClientSecret = preferences.getString(domain + "/client_secret", null);
|
||||
|
||||
if (prefClientId != null && prefClientSecret != null) {
|
||||
clientId = prefClientId;
|
||||
clientSecret = prefClientSecret;
|
||||
redirectUserToAuthorizeAndLogin(editText);
|
||||
} else {
|
||||
Log.watchTag(OkHttpUtils.TAG);
|
||||
|
||||
Callback<AppCredentials> callback = new Callback<AppCredentials>() {
|
||||
@Override
|
||||
public void onResponse(Call<AppCredentials> call,
|
||||
Response<AppCredentials> response) {
|
||||
if (!response.isSuccessful()) {
|
||||
debugLogDisplay.setText(Log.getWatchedMessages());
|
||||
editText.setError(getString(R.string.error_failed_app_registration));
|
||||
Log.e(TAG, "App authentication failed. " + response.message());
|
||||
return;
|
||||
}
|
||||
AppCredentials credentials = response.body();
|
||||
clientId = credentials.clientId;
|
||||
clientSecret = credentials.clientSecret;
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString(domain + "/client_id", clientId);
|
||||
editor.putString(domain + "/client_secret", clientSecret);
|
||||
editor.apply();
|
||||
Log.watchTag(null);
|
||||
redirectUserToAuthorizeAndLogin(editText);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<AppCredentials> call, Throwable t) {
|
||||
debugLogDisplay.setText(Log.getWatchedMessages());
|
||||
editText.setError(getString(R.string.error_failed_app_registration));
|
||||
t.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
getApiFor(domain)
|
||||
.authenticateApp(getString(R.string.app_name), getOauthRedirectUri(),
|
||||
OAUTH_SCOPES, getString(R.string.app_website))
|
||||
.enqueue(callback);
|
||||
} catch (IllegalArgumentException e) {
|
||||
editText.setError(getString(R.string.error_invalid_domain));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Chain together the key-value pairs into a query string, for either appending to a URL or
|
||||
* as the content of an HTTP request.
|
||||
*/
|
||||
@NonNull
|
||||
private static String toQueryString(Map<String, String> parameters) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
String between = "";
|
||||
for (Map.Entry<String, String> entry : parameters.entrySet()) {
|
||||
s.append(between);
|
||||
s.append(Uri.encode(entry.getKey()));
|
||||
s.append("=");
|
||||
s.append(Uri.encode(entry.getValue()));
|
||||
between = "&";
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private void redirectUserToAuthorizeAndLogin(EditText editText) {
|
||||
/* To authorize this app and log in it's necessary to redirect to the domain given,
|
||||
* activity_login there, and the server will redirect back to the app with its response. */
|
||||
String endpoint = MastodonAPI.ENDPOINT_AUTHORIZE;
|
||||
String redirectUri = getOauthRedirectUri();
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put("client_id", clientId);
|
||||
parameters.put("redirect_uri", redirectUri);
|
||||
parameters.put("response_type", "code");
|
||||
parameters.put("scope", OAUTH_SCOPES);
|
||||
String url = "https://" + domain + endpoint + "?" + toQueryString(parameters);
|
||||
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
if (viewIntent.resolveActivity(getPackageManager()) != null) {
|
||||
startActivity(viewIntent);
|
||||
} else {
|
||||
editText.setError(getString(R.string.error_no_web_browser_found));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -350,4 +351,14 @@ public class LoginActivity extends AppCompatActivity {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onLoginSuccess(String accessToken) {
|
||||
SharedPreferences.Editor editor = preferences.edit();
|
||||
editor.putString("domain", domain);
|
||||
editor.putString("accessToken", accessToken);
|
||||
editor.commit();
|
||||
Intent intent = new Intent(this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import android.support.annotation.NonNull;
|
|||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
|
@ -30,6 +29,8 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.net.ssl.HandshakeCompletedEvent;
|
||||
import javax.net.ssl.HandshakeCompletedListener;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
@ -39,11 +40,19 @@ import javax.net.ssl.X509TrustManager;
|
|||
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
|
||||
class OkHttpUtils {
|
||||
private static final String TAG = "OkHttpUtils"; // logging tag
|
||||
static final String TAG = "OkHttpUtils"; // logging tag
|
||||
|
||||
/**
|
||||
* Makes a Builder with the maximum range of TLS versions and cipher suites enabled.
|
||||
*
|
||||
* It first tries the "approved" list of cipher suites given in OkHttp (the default in
|
||||
* ConnectionSpec.MODERN_TLS) and if that doesn't work falls back to the set of ALL enabled,
|
||||
* then falls back to plain http.
|
||||
*
|
||||
* TLS 1.1 and 1.2 have to be manually enabled on API levels 16-20.
|
||||
*/
|
||||
@NonNull
|
||||
static OkHttpClient.Builder getCompatibleClientBuilder() {
|
||||
ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
|
||||
|
@ -56,12 +65,8 @@ class OkHttpUtils {
|
|||
specList.add(fallback);
|
||||
specList.add(ConnectionSpec.CLEARTEXT);
|
||||
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
OkHttpClient.Builder builder = new OkHttpClient.Builder()
|
||||
.connectionSpecs(specList)
|
||||
.addInterceptor(loggingInterceptor);
|
||||
.connectionSpecs(specList);
|
||||
|
||||
return enableHigherTlsOnPreLollipop(builder);
|
||||
}
|
||||
|
@ -72,7 +77,7 @@ class OkHttpUtils {
|
|||
}
|
||||
|
||||
private static OkHttpClient.Builder enableHigherTlsOnPreLollipop(OkHttpClient.Builder builder) {
|
||||
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
|
||||
// if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
|
||||
try {
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
|
||||
TrustManagerFactory.getDefaultAlgorithm());
|
||||
|
@ -89,22 +94,23 @@ class OkHttpUtils {
|
|||
sslContext.init(null, new TrustManager[] { trustManager }, null);
|
||||
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
builder.sslSocketFactory(new Tls11And12SocketFactory(sslSocketFactory),
|
||||
builder.sslSocketFactory(new SSLSocketFactoryCompat(sslSocketFactory),
|
||||
trustManager);
|
||||
} catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e) {
|
||||
Log.e(TAG, "Failed enabling TLS 1.1 & 1.2. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
// }
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static class Tls11And12SocketFactory extends SSLSocketFactory {
|
||||
private static final String[] TLS_VERSIONS = { "TLSv1.1", "TLSv1.2" };
|
||||
private static class SSLSocketFactoryCompat extends SSLSocketFactory {
|
||||
private static final String[] DESIRED_TLS_VERSIONS = { "TLSv1", "TLSv1.1", "TLSv1.2",
|
||||
"TLSv1.3" };
|
||||
|
||||
final SSLSocketFactory delegate;
|
||||
|
||||
Tls11And12SocketFactory(SSLSocketFactory base) {
|
||||
SSLSocketFactoryCompat(SSLSocketFactory base) {
|
||||
this.delegate = base;
|
||||
}
|
||||
|
||||
|
@ -125,13 +131,13 @@ class OkHttpUtils {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
|
||||
public Socket createSocket(String host, int port) throws IOException {
|
||||
return patch(delegate.createSocket(host, port));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket(String host, int port, InetAddress localHost, int localPort)
|
||||
throws IOException, UnknownHostException {
|
||||
throws IOException {
|
||||
return patch(delegate.createSocket(host, port, localHost, localPort));
|
||||
}
|
||||
|
||||
|
@ -146,10 +152,52 @@ class OkHttpUtils {
|
|||
return patch(delegate.createSocket(address, port, localAddress, localPort));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static String[] getMatches(String[] wanted, String[] have) {
|
||||
List<String> a = new ArrayList<>(Arrays.asList(wanted));
|
||||
List<String> b = Arrays.asList(have);
|
||||
a.retainAll(b);
|
||||
return a.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<String> getDifferences(String[] wanted, String[] have) {
|
||||
List<String> a = new ArrayList<>(Arrays.asList(wanted));
|
||||
List<String> b = Arrays.asList(have);
|
||||
a.removeAll(b);
|
||||
return a;
|
||||
}
|
||||
|
||||
private Socket patch(Socket socket) {
|
||||
if (socket instanceof SSLSocket) {
|
||||
SSLSocket sslSocket = (SSLSocket) socket;
|
||||
sslSocket.setEnabledProtocols(TLS_VERSIONS);
|
||||
String[] protocols = getMatches(DESIRED_TLS_VERSIONS,
|
||||
sslSocket.getSupportedProtocols());
|
||||
sslSocket.setEnabledProtocols(protocols);
|
||||
|
||||
// Add a debug listener.
|
||||
String[] enabledProtocols = sslSocket.getEnabledProtocols();
|
||||
List<String> disabledProtocols = getDifferences(sslSocket.getSupportedProtocols(),
|
||||
enabledProtocols);
|
||||
String[] enabledSuites = sslSocket.getEnabledCipherSuites();
|
||||
List<String> disabledSuites = getDifferences(sslSocket.getSupportedCipherSuites(),
|
||||
enabledSuites);
|
||||
Log.i(TAG, "Socket Created-----");
|
||||
Log.i(TAG, "enabled protocols: " + Arrays.toString(enabledProtocols));
|
||||
Log.i(TAG, "disabled protocols: " + disabledProtocols.toString());
|
||||
Log.i(TAG, "enabled cipher suites: " + Arrays.toString(enabledSuites));
|
||||
Log.i(TAG, "disabled cipher suites: " + disabledSuites.toString());
|
||||
|
||||
sslSocket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
|
||||
@Override
|
||||
public void handshakeCompleted(HandshakeCompletedEvent event) {
|
||||
String host = event.getSession().getPeerHost();
|
||||
String protocol = event.getSession().getProtocol();
|
||||
String cipherSuite = event.getCipherSuite();
|
||||
Log.i(TAG, String.format("Handshake: %s %s %s", host, protocol,
|
||||
cipherSuite));
|
||||
}
|
||||
});
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
|
|
@ -13,11 +13,34 @@
|
|||
android:gravity="center"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#000000"
|
||||
android:layout_marginBottom="25dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/debug_log_display"
|
||||
android:textIsSelectable="true"
|
||||
android:textColor="#ffffff" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
<!--
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="50dp"
|
||||
android:src="@drawable/elephant_friend"/>
|
||||
-->
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
android:layout_height="wrap_content"
|
||||
|
|
Loading…
Reference in a new issue