package com.ponyvillelive.pvlmobile.ui;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.v4.widget.DrawerLayout;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import com.jakewharton.madge.MadgeFrameLayout;
import com.jakewharton.scalpel.ScalpelFrameLayout;
import com.ponyvillelive.pvlmobile.BuildConfig;
import com.ponyvillelive.pvlmobile.PvlApp;
import com.ponyvillelive.pvlmobile.R;
import com.ponyvillelive.pvlmobile.prefs.AnimationSpeed;
import com.ponyvillelive.pvlmobile.prefs.ApiEndpoint;
import com.ponyvillelive.pvlmobile.prefs.BooleanPreference;
import com.ponyvillelive.pvlmobile.prefs.Endpoints;
import com.ponyvillelive.pvlmobile.prefs.IntPreference;
import com.ponyvillelive.pvlmobile.prefs.NetworkProxy;
import com.ponyvillelive.pvlmobile.prefs.PicassoDebugging;
import com.ponyvillelive.pvlmobile.prefs.PixelGridEnabled;
import com.ponyvillelive.pvlmobile.prefs.PixelRatioEnabled;
import com.ponyvillelive.pvlmobile.prefs.ScalpelEnabled;
import com.ponyvillelive.pvlmobile.prefs.ScalpelWireframeEnabled;
import com.ponyvillelive.pvlmobile.prefs.SeenDebugDrawer;
import com.ponyvillelive.pvlmobile.prefs.StringPreference;
import com.ponyvillelive.pvlmobile.util.Strings;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.StatsSnapshot;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Singleton;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import retrofit.MockRestAdapter;
import retrofit.RestAdapter;
import timber.log.Timber;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static java.net.Proxy.Type.HTTP;
import static retrofit.RestAdapter.LogLevel;
/**
* Created by berwyn on 16/08/14.
*/
@Singleton
public class DebugAppContainer implements AppContainer {
private static final DateFormat DATE_DISPLAY_FORMAT = new SimpleDateFormat("yyyy-MM-dd hh:mm a");
private PvlApp app;
private Activity activity;
private Context drawerContext;
private final OkHttpClient client;
private final Picasso picasso;
private final StringPreference networkEndpoint;
private final StringPreference networkProxy;
private final IntPreference animationSpeed;
private final BooleanPreference picassoDebugging;
private final BooleanPreference pixelGridEnabled;
private final BooleanPreference pixelRatioEnabled;
private final BooleanPreference scalpelEnabled;
private final BooleanPreference scalpelWireframeEnabled;
private final BooleanPreference seenDebugDrawer;
private final RestAdapter restAdapter;
private final MockRestAdapter mockRestAdapter;
@InjectView(R.id.debug_drawer_layout)
DrawerLayout drawerLayout;
@InjectView(R.id.debug_content)
ViewGroup content;
@InjectView(R.id.madge_container)
MadgeFrameLayout madgeFrameLayout;
@InjectView(R.id.scalpel_container)
ScalpelFrameLayout scalpelFrameLayout;
@InjectView(R.id.debug_network_endpoint)
Spinner endpointView;
@InjectView(R.id.debug_network_endpoint_edit)
View endpointEditView;
@InjectView(R.id.debug_network_delay)
Spinner networkDelayView;
@InjectView(R.id.debug_network_variance)
Spinner networkVarianceView;
@InjectView(R.id.debug_network_error)
Spinner networkErrorView;
@InjectView(R.id.debug_network_proxy)
Spinner networkProxyView;
@InjectView(R.id.debug_network_logging)
Spinner networkLoggingView;
@InjectView(R.id.debug_ui_animation_speed)
Spinner uiAnimationSpeedView;
@InjectView(R.id.debug_ui_pixel_grid)
Switch uiPixelGridView;
@InjectView(R.id.debug_ui_pixel_ratio)
Switch uiPixelRatioView;
@InjectView(R.id.debug_ui_scalpel)
Switch uiScalpelView;
@InjectView(R.id.debug_ui_scalpel_wireframe)
Switch uiScalpelWireframeView;
@InjectView(R.id.debug_build_name)
TextView buildNameView;
@InjectView(R.id.debug_build_code)
TextView buildCodeView;
@InjectView(R.id.debug_build_sha)
TextView buildShaView;
@InjectView(R.id.debug_build_date)
TextView buildDateView;
@InjectView(R.id.debug_device_make)
TextView deviceMakeView;
@InjectView(R.id.debug_device_model)
TextView deviceModelView;
@InjectView(R.id.debug_device_resolution)
TextView deviceResolutionView;
@InjectView(R.id.debug_device_density)
TextView deviceDensityView;
@InjectView(R.id.debug_device_release)
TextView deviceReleaseView;
@InjectView(R.id.debug_device_api)
TextView deviceApiView;
@InjectView(R.id.debug_picasso_indicators)
Switch picassoIndicatorView;
@InjectView(R.id.debug_picasso_cache_size)
TextView picassoCacheSizeView;
@InjectView(R.id.debug_picasso_cache_hit)
TextView picassoCacheHitView;
@InjectView(R.id.debug_picasso_cache_miss)
TextView picassoCacheMissView;
@InjectView(R.id.debug_picasso_decoded)
TextView picassoDecodedView;
@InjectView(R.id.debug_picasso_decoded_total)
TextView picassoDecodedTotalView;
@InjectView(R.id.debug_picasso_decoded_avg)
TextView picassoDecodedAvgView;
@InjectView(R.id.debug_picasso_transformed)
TextView picassoTransformedView;
@InjectView(R.id.debug_picasso_transformed_total)
TextView picassoTransformedTotalView;
@InjectView(R.id.debug_picasso_transformed_avg)
TextView picassoTransformedAvgView;
@Inject
public DebugAppContainer(OkHttpClient client,
Picasso picasso,
@ApiEndpoint StringPreference networkEndpoint,
@NetworkProxy StringPreference networkProxy,
@AnimationSpeed IntPreference animationSpeed,
@PicassoDebugging BooleanPreference picassoDebugging,
@PixelGridEnabled BooleanPreference pixelGridEnabled,
@PixelRatioEnabled BooleanPreference pixelRatioEnabled,
@ScalpelEnabled BooleanPreference scalpelEnabled,
@ScalpelWireframeEnabled BooleanPreference scalpelWireframeEnabled,
@SeenDebugDrawer BooleanPreference seenDebugDrawer,
RestAdapter restAdapter,
MockRestAdapter mockRestAdapter) {
this.client = client;
this.picasso = picasso;
this.networkEndpoint = networkEndpoint;
this.scalpelEnabled = scalpelEnabled;
this.scalpelWireframeEnabled = scalpelWireframeEnabled;
this.seenDebugDrawer = seenDebugDrawer;
this.networkProxy = networkProxy;
this.animationSpeed = animationSpeed;
this.picassoDebugging = picassoDebugging;
this.pixelGridEnabled = pixelGridEnabled;
this.pixelRatioEnabled = pixelRatioEnabled;
this.restAdapter = restAdapter;
this.mockRestAdapter = mockRestAdapter;
}
@Override
public ViewGroup get(Activity activity, PvlApp app) {
this.app = app;
this.activity = activity;
this.drawerContext = activity;
Timber.d("Starting DebugAppContainer#get");
long start = System.nanoTime();
activity.setContentView(R.layout.debug_activity_frame);
long diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to set content view", diff);
ViewGroup drawer = (ViewGroup) activity.findViewById(R.id.debug_drawer);
LayoutInflater.from(drawerContext).inflate(R.layout.debug_drawer_content, drawer);
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to inflate drawer", diff);
ButterKnife.inject(this, activity);
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to inject DebugAppContainer views", diff);
drawerLayout.setDrawerShadow(R.drawable.debug_drawer_shadow, Gravity.END);
drawerLayout.setDrawerListener(new DrawerLayout.SimpleDrawerListener() {
@Override
public void onDrawerOpened(View drawerView) {
refreshPicassoStats();
}
});
// If you have not seen the debug drawer before, show it with a message
if (!seenDebugDrawer.get()) {
drawerLayout.postDelayed(() -> {
drawerLayout.openDrawer(Gravity.END);
Toast.makeText(activity, R.string.debug_drawer_welcome, Toast.LENGTH_LONG).show();
}, 1000);
seenDebugDrawer.set(true);
}
setupNetworkSection();
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to setup network section", diff);
setupUserInterfaceSection();
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to setup ui section", diff);
setupBuildSection();
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to setup build section", diff);
setupDeviceSection();
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to setup device section", diff);
setupPicassoSection();
diff = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
Timber.d("Took %sms to setup picasso section", diff);
return content;
}
private void setupNetworkSection() {
final Endpoints currentEndpoint = Endpoints.from(networkEndpoint.get());
final ServerEndpointAdapter endpointAdapter = new ServerEndpointAdapter(drawerContext);
endpointView.setAdapter(endpointAdapter);
endpointView.setSelection(currentEndpoint.ordinal());
endpointView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
Endpoints selected = endpointAdapter.getItem(position);
if (selected != currentEndpoint) {
if (selected == Endpoints.CUSTOM) {
Timber.d("Custom network endpoint selected. Prompting for URL.");
showCustomEndpointDialog(currentEndpoint.ordinal(), "http://");
} else {
setEndpointAndRelaunch(selected.url);
}
} else {
Timber.d("Ignoring re-selection of network endpoint %s", selected);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
final NetworkDelayAdapter delayAdapter = new NetworkDelayAdapter(drawerContext);
networkDelayView.setAdapter(delayAdapter);
networkDelayView.setSelection(
NetworkDelayAdapter.getPositionForValue(mockRestAdapter.getDelay()));
networkDelayView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
long selected = delayAdapter.getItem(position);
if (selected != mockRestAdapter.getDelay()) {
Timber.d("Setting network delay to %sms", selected);
mockRestAdapter.setDelay(selected);
} else {
Timber.d("Ignoring re-selection of network delay %sms", selected);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
final NetworkVarianceAdapter varianceAdapter = new NetworkVarianceAdapter(drawerContext);
networkVarianceView.setAdapter(varianceAdapter);
networkVarianceView.setSelection(
NetworkVarianceAdapter.getPositionForValue(mockRestAdapter.getVariancePercentage()));
networkVarianceView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
int selected = varianceAdapter.getItem(position);
if (selected != mockRestAdapter.getVariancePercentage()) {
Timber.d("Setting network variance to %s%%", selected);
mockRestAdapter.setVariancePercentage(selected);
} else {
Timber.d("Ignoring re-selection of network variance %s%%", selected);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
final NetworkErrorAdapter errorAdapter = new NetworkErrorAdapter(drawerContext);
networkErrorView.setAdapter(errorAdapter);
networkErrorView.setSelection(
NetworkErrorAdapter.getPositionForValue(mockRestAdapter.getErrorPercentage()));
networkErrorView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
int selected = errorAdapter.getItem(position);
if (selected != mockRestAdapter.getErrorPercentage()) {
Timber.d("Setting network error to %s%%", selected);
mockRestAdapter.setErrorPercentage(selected);
} else {
Timber.d("Ignoring re-selection of network error %s%%", selected);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
int currentProxyPosition = networkProxy.isSet() ? ProxyAdapter.PROXY : ProxyAdapter.NONE;
final ProxyAdapter proxyAdapter = new ProxyAdapter(activity, networkProxy);
networkProxyView.setAdapter(proxyAdapter);
networkProxyView.setSelection(currentProxyPosition);
networkProxyView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
if (position == ProxyAdapter.NONE) {
Timber.d("Clearing network proxy");
networkProxy.delete();
client.setProxy(null);
} else if (networkProxy.isSet() && position == ProxyAdapter.PROXY) {
Timber.d("Ignoring re-selection of network proxy %s", networkProxy.get());
} else {
Timber.d("New network proxy selected. Prompting for host.");
showNewNetworkProxyDialog(proxyAdapter);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
// Only show the endpoint editor when a custom endpoint is in use.
endpointEditView.setVisibility(currentEndpoint == Endpoints.CUSTOM ? VISIBLE : GONE);
if (currentEndpoint == Endpoints.MOCK_MODE) {
// Disable network proxy if we are in mock mode.
networkProxyView.setEnabled(false);
networkLoggingView.setEnabled(false);
} else {
// Disable network controls if we are not in mock mode.
networkDelayView.setEnabled(false);
networkVarianceView.setEnabled(false);
networkErrorView.setEnabled(false);
}
// We use the JSON rest adapter as the source of truth for the log level.
final EnumAdapter<LogLevel> loggingAdapter = new EnumAdapter<LogLevel>(activity, LogLevel.class);
networkLoggingView.setAdapter(loggingAdapter);
networkLoggingView.setSelection(restAdapter.getLogLevel().ordinal());
networkLoggingView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
RestAdapter.LogLevel selected = loggingAdapter.getItem(position);
if (selected != restAdapter.getLogLevel()) {
Timber.d("Setting logging level to %s", selected);
restAdapter.setLogLevel(selected);
} else {
Timber.d("Ignoring re-selection of logging level " + selected);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
@OnClick(R.id.debug_network_endpoint_edit)
void onEditEndpointClicked() {
Timber.d("Prompting to edit custom endpoint URL.");
// Pass in the currently selected position since we are merely editing.
showCustomEndpointDialog(endpointView.getSelectedItemPosition(), networkEndpoint.get());
}
private void setupUserInterfaceSection() {
final AnimationSpeedAdapter speedAdapter = new AnimationSpeedAdapter(activity);
uiAnimationSpeedView.setAdapter(speedAdapter);
final int animationSpeedValue = animationSpeed.get();
uiAnimationSpeedView.setSelection(
AnimationSpeedAdapter.getPositionForValue(animationSpeedValue));
uiAnimationSpeedView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
int selected = speedAdapter.getItem(position);
if (selected != animationSpeed.get()) {
Timber.d("Setting animation speed to %sx", selected);
animationSpeed.set(selected);
applyAnimationSpeed(selected);
} else {
Timber.d("Ignoring re-selection of animation speed %sx", selected);
}
}
@Override public void onNothingSelected(AdapterView<?> adapterView) {
}
});
// Ensure the animation speed value is always applied across app restarts.
content.post(() -> applyAnimationSpeed(animationSpeedValue));
boolean gridEnabled = pixelGridEnabled.get();
madgeFrameLayout.setOverlayEnabled(gridEnabled);
uiPixelGridView.setChecked(gridEnabled);
uiPixelRatioView.setEnabled(gridEnabled);
uiPixelGridView.setOnCheckedChangeListener((buttonView, isChecked) -> {
Timber.d("Setting pixel grid overlay enabled to " + isChecked);
pixelGridEnabled.set(isChecked);
madgeFrameLayout.setOverlayEnabled(isChecked);
uiPixelRatioView.setEnabled(isChecked);
});
boolean ratioEnabled = pixelRatioEnabled.get();
madgeFrameLayout.setOverlayRatioEnabled(ratioEnabled);
uiPixelRatioView.setChecked(ratioEnabled);
uiPixelRatioView.setOnCheckedChangeListener((buttonView, isChecked) -> {
Timber.d("Setting pixel scale overlay enabled to " + isChecked);
pixelRatioEnabled.set(isChecked);
madgeFrameLayout.setOverlayRatioEnabled(isChecked);
});
boolean scalpel = scalpelEnabled.get();
scalpelFrameLayout.setLayerInteractionEnabled(scalpel);
uiScalpelView.setChecked(scalpel);
uiScalpelWireframeView.setEnabled(scalpel);
uiScalpelView.setOnCheckedChangeListener((buttonView, isChecked) -> {
Timber.d("Setting scalpel interaction enabled to " + isChecked);
scalpelEnabled.set(isChecked);
scalpelFrameLayout.setLayerInteractionEnabled(isChecked);
uiScalpelWireframeView.setEnabled(isChecked);
});
boolean wireframe = scalpelWireframeEnabled.get();
scalpelFrameLayout.setDrawViews(!wireframe);
uiScalpelWireframeView.setChecked(wireframe);
uiScalpelWireframeView.setOnCheckedChangeListener((buttonView, isChecked) -> {
Timber.d("Setting scalpel wireframe enabled to " + isChecked);
scalpelWireframeEnabled.set(isChecked);
scalpelFrameLayout.setDrawViews(!isChecked);
});
}
private void setupBuildSection() {
buildNameView.setText(BuildConfig.VERSION_NAME);
buildCodeView.setText(String.valueOf(BuildConfig.VERSION_CODE));
buildShaView.setText(BuildConfig.GIT_SHA);
try {
// Parse ISO8601-format time into local time.
DateFormat inFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
inFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date buildTime = inFormat.parse(BuildConfig.BUILD_TIME);
buildDateView.setText(DATE_DISPLAY_FORMAT.format(buildTime));
} catch (ParseException e) {
throw new RuntimeException("Unable to decode build time: " + BuildConfig.BUILD_TIME, e);
}
}
private void setupDeviceSection() {
DisplayMetrics displayMetrics = activity.getResources().getDisplayMetrics();
String densityBucket = getDensityString(displayMetrics);
deviceMakeView.setText(Strings.truncateAt(Build.MANUFACTURER, 20));
deviceModelView.setText(Strings.truncateAt(Build.MODEL, 20));
deviceResolutionView.setText(displayMetrics.heightPixels + "x" + displayMetrics.widthPixels);
deviceDensityView.setText(displayMetrics.densityDpi + "dpi (" + densityBucket + ")");
deviceReleaseView.setText(Build.VERSION.RELEASE);
deviceApiView.setText(String.valueOf(Build.VERSION.SDK_INT));
}
private void setupPicassoSection() {
boolean picassoDebuggingValue = picassoDebugging.get();
picasso.setDebugging(picassoDebuggingValue);
picassoIndicatorView.setChecked(picassoDebuggingValue);
picassoIndicatorView.setOnCheckedChangeListener((button, isChecked) -> {
Timber.d("Setting Picasso debugging to " + isChecked);
picasso.setDebugging(isChecked);
picassoDebugging.set(isChecked);
});
refreshPicassoStats();
}
private void refreshPicassoStats() {
StatsSnapshot snapshot = picasso.getSnapshot();
String size = getSizeString(snapshot.size);
String total = getSizeString(snapshot.maxSize);
int percentage = (int) ((1f * snapshot.size / snapshot.maxSize) * 100);
picassoCacheSizeView.setText(size + " / " + total + " (" + percentage + "%)");
picassoCacheHitView.setText(String.valueOf(snapshot.cacheHits));
picassoCacheMissView.setText(String.valueOf(snapshot.cacheMisses));
picassoDecodedView.setText(String.valueOf(snapshot.originalBitmapCount));
picassoDecodedTotalView.setText(getSizeString(snapshot.totalOriginalBitmapSize));
picassoDecodedAvgView.setText(getSizeString(snapshot.averageOriginalBitmapSize));
picassoTransformedView.setText(String.valueOf(snapshot.transformedBitmapCount));
picassoTransformedTotalView.setText(getSizeString(snapshot.totalTransformedBitmapSize));
picassoTransformedAvgView.setText(getSizeString(snapshot.averageTransformedBitmapSize));
}
private void applyAnimationSpeed(int multiplier) {
try {
Method method = ValueAnimator.class.getDeclaredMethod("setDurationScale", float.class);
method.invoke(null, (float) multiplier);
} catch (Exception e) {
throw new RuntimeException("Unable to apply animation speed.", e);
}
}
private static String getDensityString(DisplayMetrics displayMetrics) {
switch (displayMetrics.densityDpi) {
case DisplayMetrics.DENSITY_LOW:
return "ldpi";
case DisplayMetrics.DENSITY_MEDIUM:
return "mdpi";
case DisplayMetrics.DENSITY_HIGH:
return "hdpi";
case DisplayMetrics.DENSITY_XHIGH:
return "xhdpi";
case DisplayMetrics.DENSITY_XXHIGH:
return "xxhdpi";
case DisplayMetrics.DENSITY_XXXHIGH:
return "xxxhdpi";
case DisplayMetrics.DENSITY_TV:
return "tvdpi";
default:
return "unknown";
}
}
private static String getSizeString(long bytes) {
String[] units = new String[] { "B", "KB", "MB", "GB" };
int unit = 0;
while (bytes >= 1024) {
bytes /= 1024;
unit += 1;
}
return bytes + units[unit];
}
private void showNewNetworkProxyDialog(final ProxyAdapter proxyAdapter) {
final int originalSelection = networkProxy.isSet() ? ProxyAdapter.PROXY : ProxyAdapter.NONE;
View view = LayoutInflater.from(app).inflate(R.layout.debug_drawer_network_proxy, null);
final EditText host = (EditText) view.findViewById(R.id.debug_drawer_network_proxy_host);
new AlertDialog.Builder(activity) //
.setTitle("Set Network Proxy")
.setView(view)
.setNegativeButton("Cancel", (dialog, i) -> {
networkProxyView.setSelection(originalSelection);
dialog.cancel();
})
.setPositiveButton("Use", (dialog, i) -> {
String theHost = host.getText().toString();
if (!Strings.isBlank(theHost)) {
String[] parts = theHost.split(":", 2);
SocketAddress address =
InetSocketAddress.createUnresolved(parts[0], Integer.parseInt(parts[1]));
networkProxy.set(theHost); // Persist across restarts.
proxyAdapter.notifyDataSetChanged(); // Tell the spinner to update.
networkProxyView.setSelection(ProxyAdapter.PROXY); // And show the proxy.
client.setProxy(new Proxy(HTTP, address));
} else {
networkProxyView.setSelection(originalSelection);
}
})
.setOnCancelListener(dialogInterface -> networkProxyView.setSelection(originalSelection))
.show();
}
private void showCustomEndpointDialog(final int originalSelection, String defaultUrl) {
View view = LayoutInflater.from(app).inflate(R.layout.debug_drawer_network_endpoint, null);
final EditText url = (EditText) view.findViewById(R.id.debug_drawer_network_endpoint_url);
url.setText(defaultUrl);
url.setSelection(url.length());
new AlertDialog.Builder(activity) //
.setTitle("Set Network Endpoint")
.setView(view)
.setNegativeButton("Cancel", (dialog, i) -> {
endpointView.setSelection(originalSelection);
dialog.cancel();
})
.setPositiveButton("Use", (dialog, i) -> {
String theUrl = url.getText().toString();
if (!Strings.isBlank(theUrl)) {
setEndpointAndRelaunch(theUrl);
} else {
endpointView.setSelection(originalSelection);
}
})
.setOnCancelListener(dialogInterface -> endpointView.setSelection(originalSelection))
.show();
}
private void setEndpointAndRelaunch(String endpoint) {
Timber.d("Setting network endpoint to %s", endpoint);
networkEndpoint.set(endpoint);
Intent newApp = new Intent(app, MainActivity.class);
newApp.setFlags(FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
app.startActivity(newApp);
app.buildObjectGraphAndInject();
}
}