Browse Source

Merge pull request #249 from str4d/i2p-android

Support I2P (and other proxies in future)
master
Anthony Restaino 10 years ago
parent
commit
a2f2fbc82b
  1. 1
      app/build.gradle
  2. 6
      app/proguard-project.txt
  3. 231
      app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java
  4. 125
      app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java
  5. 6
      app/src/main/java/acr/browser/lightning/constant/Constants.java
  6. 2
      app/src/main/java/acr/browser/lightning/controller/BrowserController.java
  7. 37
      app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java
  8. 18
      app/src/main/java/acr/browser/lightning/view/LightningView.java
  9. 43
      app/src/main/res/layout/picker_manual_proxy.xml
  10. 24
      app/src/main/res/layout/settings.xml
  11. 14
      app/src/main/res/values/strings.xml

1
app/build.gradle

@ -39,4 +39,5 @@ dependencies {
compile 'com.android.support:palette-v7:22.1.1' compile 'com.android.support:palette-v7:22.1.1'
compile 'com.android.support:appcompat-v7:22.1.1' compile 'com.android.support:appcompat-v7:22.1.1'
compile 'org.jsoup:jsoup:1.8.1' compile 'org.jsoup:jsoup:1.8.1'
compile 'net.i2p.android:client:0.7@aar'
} }

6
app/proguard-project.txt

@ -77,3 +77,9 @@
# Don't warn about those in case this app is linking against an older # Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe. # platform version. We know about them, and they are safe.
-dontwarn android.support.** -dontwarn android.support.**
# The I2P Java API bundled inside the I2P Android client library contains
# references to javax.naming classes that Android doesn't have. But those
# classes are never used on Android, and it is safe to ignore the warnings.
-dontwarn net.i2p.crypto.CertUtil
-dontwarn org.apache.http.conn.ssl.DefaultHostnameVerifier

231
app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java

@ -10,7 +10,11 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity; import android.app.Activity;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.*; import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources.Theme; import android.content.res.Resources.Theme;
import android.database.Cursor; import android.database.Cursor;
@ -36,15 +40,26 @@ import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.DrawerLayout.DrawerListener; import android.support.v4.widget.DrawerLayout.DrawerListener;
import android.support.v7.app.ActionBar;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.*; import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener; import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener; import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener; import android.view.View.OnLongClickListener;
import android.view.View.OnTouchListener; import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener; import android.view.animation.Animation.AnimationListener;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
@ -52,42 +67,69 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation; import android.view.animation.Transformation;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import android.webkit.*; import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient.CustomViewCallback; import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebIconDatabase;
import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebView.HitTestResult; import android.webkit.WebView.HitTestResult;
import android.widget.*; import android.webkit.WebViewDatabase;
import android.support.v7.app.ActionBar; import android.widget.AdapterView;
import android.support.v7.graphics.Palette;
import android.support.v7.widget.Toolbar;
import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import android.widget.VideoView;
import acr.browser.lightning.view.AnimatedProgressBar; import net.i2p.android.ui.I2PAndroidHelper;
import acr.browser.lightning.database.BookmarkManager;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import acr.browser.lightning.R;
import acr.browser.lightning.constant.BookmarkPage; import acr.browser.lightning.constant.BookmarkPage;
import acr.browser.lightning.controller.BrowserController;
import acr.browser.lightning.object.ClickHandler;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.object.DrawerArrowDrawable; import acr.browser.lightning.constant.HistoryPage;
import acr.browser.lightning.controller.BrowserController;
import acr.browser.lightning.database.BookmarkManager;
import acr.browser.lightning.database.HistoryDatabase; import acr.browser.lightning.database.HistoryDatabase;
import acr.browser.lightning.database.HistoryItem; import acr.browser.lightning.database.HistoryItem;
import acr.browser.lightning.constant.HistoryPage; import acr.browser.lightning.object.ClickHandler;
import acr.browser.lightning.view.LightningView; import acr.browser.lightning.object.DrawerArrowDrawable;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.R;
import acr.browser.lightning.object.SearchAdapter; import acr.browser.lightning.object.SearchAdapter;
import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
import acr.browser.lightning.view.AnimatedProgressBar;
import acr.browser.lightning.view.LightningView;
import info.guardianproject.onionkit.ui.OrbotHelper; import info.guardianproject.onionkit.ui.OrbotHelper;
import info.guardianproject.onionkit.web.WebkitProxy; import info.guardianproject.onionkit.web.WebkitProxy;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;
public class BrowserActivity extends ThemableActivity implements BrowserController, OnClickListener { public class BrowserActivity extends ThemableActivity implements BrowserController, OnClickListener {
// Layout // Layout
@ -140,6 +182,11 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
private Drawable mDeleteIcon, mRefreshIcon, mCopyIcon, mIcon; private Drawable mDeleteIcon, mRefreshIcon, mCopyIcon, mIcon;
private DrawerArrowDrawable mArrowDrawable; private DrawerArrowDrawable mArrowDrawable;
// Helper
private I2PAndroidHelper mI2PHelper;
private boolean mI2PHelperBound;
private boolean mI2PProxyInitialized;
// Constant // Constant
private static final int API = android.os.Build.VERSION.SDK_INT; private static final int API = android.os.Build.VERSION.SDK_INT;
private static final LayoutParams MATCH_PARENT = new LayoutParams(LayoutParams.MATCH_PARENT, private static final LayoutParams MATCH_PARENT = new LayoutParams(LayoutParams.MATCH_PARENT,
@ -221,6 +268,8 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
R.id.arrow_button); R.id.arrow_button);
arrowButton.setOnClickListener(this); arrowButton.setOnClickListener(this);
mI2PHelper = new I2PAndroidHelper(this);
RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back); RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back);
back.setOnClickListener(this); back.setOnClickListener(this);
@ -304,7 +353,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath());
} }
checkForTor(); checkForProxy();
} }
private class SearchClass { private class SearchClass {
@ -494,56 +543,106 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
} }
/* /*
* If Orbot/Tor is installed, prompt the user if they want to enable * If Orbot/Tor or I2P is installed, prompt the user if they want to enable
* proxying for this session * proxying for this session
*/ */
private boolean checkForTor() { private void checkForProxy() {
boolean useProxy = mPreferences.getUseProxy(); boolean useProxy = mPreferences.getUseProxy();
OrbotHelper oh = new OrbotHelper(this); OrbotHelper oh = new OrbotHelper(this);
if (oh.isOrbotInstalled() && !mPreferences.getCheckedForTor()) { final boolean orbotInstalled = oh.isOrbotInstalled();
mPreferences.setCheckedForTor(true); boolean orbotChecked = mPreferences.getCheckedForTor();
boolean orbot = orbotInstalled && !orbotChecked;
boolean i2pInstalled = mI2PHelper.isI2PAndroidInstalled();
boolean i2pChecked = mPreferences.getCheckedForI2P();
boolean i2p = i2pInstalled && !i2pChecked;
// TODO Is the idea to show this per-session, or only once?
if (!useProxy && (orbot || i2p)) {
if (orbot) mPreferences.setCheckedForTor(true);
if (i2p) mPreferences.setCheckedForI2P(true);
AlertDialog.Builder builder = new AlertDialog.Builder(this);
if (orbotInstalled && i2pInstalled) {
String[] proxyChoices = this.getResources().getStringArray(R.array.proxy_choices_array);
builder.setTitle(getResources().getString(R.string.http_proxy))
.setSingleChoiceItems(proxyChoices, mPreferences.getProxyChoice(),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mPreferences.setProxyChoice(which);
}
})
.setNeutralButton(getResources().getString(R.string.action_ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (mPreferences.getUseProxy())
initializeProxy();
}
});
} else {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
case DialogInterface.BUTTON_POSITIVE: case DialogInterface.BUTTON_POSITIVE:
mPreferences.setUseProxy(true); mPreferences.setProxyChoice(orbotInstalled ?
initializeTor(); Constants.PROXY_ORBOT : Constants.PROXY_I2P);
initializeProxy();
break; break;
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
mPreferences.setUseProxy(false); mPreferences.setProxyChoice(Constants.NO_PROXY);
break; break;
} }
} }
}; };
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage(orbotInstalled ? R.string.use_tor_prompt : R.string.use_i2p_prompt)
builder.setMessage(R.string.use_tor_prompt)
.setPositiveButton(R.string.yes, dialogClickListener) .setPositiveButton(R.string.yes, dialogClickListener)
.setNegativeButton(R.string.no, dialogClickListener).show(); .setNegativeButton(R.string.no, dialogClickListener);
}
return true; builder.show();
} else if (oh.isOrbotInstalled() & useProxy) {
return true;
} else {
mPreferences.setUseProxy(false);
return false;
} }
} }
/* /*
* Initialize WebKit Proxying for Tor * Initialize WebKit Proxying
*/ */
private void initializeTor() { private void initializeProxy() {
String host;
int port;
switch (mPreferences.getProxyChoice()) {
case Constants.NO_PROXY:
// We shouldn't be here
return;
case Constants.PROXY_ORBOT:
OrbotHelper oh = new OrbotHelper(this); OrbotHelper oh = new OrbotHelper(this);
if (!oh.isOrbotRunning()) { if (!oh.isOrbotRunning()) {
oh.requestOrbotStart(this); oh.requestOrbotStart(this);
} }
host = "localhost";
port = 8118;
break;
case Constants.PROXY_I2P:
mI2PProxyInitialized = true;
if (mI2PHelperBound && !mI2PHelper.isI2PAndroidRunning()) {
mI2PHelper.requestI2PAndroidStart(this);
}
host = "localhost";
port = 4444;
break;
default:
host = mPreferences.getProxyHost();
port = mPreferences.getProxyPort();
}
try { try {
String host = mPreferences.getProxyHost();
int port = mPreferences.getProxyPort();
WebkitProxy.setProxy(BrowserApp.class.getName(), getApplicationContext(), WebkitProxy.setProxy(BrowserApp.class.getName(), getApplicationContext(),
host, port); host, port);
} catch (Exception e) { } catch (Exception e) {
@ -552,6 +651,20 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
} }
public boolean isProxyReady() {
if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) {
if (!mI2PHelper.isI2PAndroidRunning()) {
Utils.showToast(this, getString(R.string.i2p_not_running));
return false;
} else if (!mI2PHelper.areTunnelsActive()) {
Utils.showToast(this, getString(R.string.i2p_tunnels_not_ready));
return false;
}
}
return true;
}
private boolean isTablet() { private boolean isTablet() {
return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
} }
@ -704,7 +817,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
updateCookiePreference(); updateCookiePreference();
if (mPreferences.getUseProxy()) { if (mPreferences.getUseProxy()) {
initializeTor(); initializeProxy();
} else { } else {
try { try {
WebkitProxy.resetProxy(BrowserApp.class.getName(), WebkitProxy.resetProxy(BrowserApp.class.getName(),
@ -712,6 +825,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
mI2PProxyInitialized = false;
} }
} }
@ -1369,6 +1483,13 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
} }
} }
@Override
protected void onStop() {
super.onStop();
mI2PHelper.unbind();
mI2PHelperBound = false;
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
Log.d(Constants.TAG, "onDestroy"); Log.d(Constants.TAG, "onDestroy");
@ -1378,6 +1499,22 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll
super.onDestroy(); super.onDestroy();
} }
@Override
protected void onStart() {
super.onStart();
if (mPreferences.getProxyChoice() == Constants.PROXY_I2P) {
// Try to bind to I2P Android
mI2PHelper.bind(new I2PAndroidHelper.Callback() {
@Override
public void onI2PAndroidBound() {
mI2PHelperBound = true;
if (mI2PProxyInitialized && !mI2PHelper.isI2PAndroidRunning())
mI2PHelper.requestI2PAndroidStart(BrowserActivity.this);
}
});
}
}
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();

125
app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java

@ -22,11 +22,15 @@ import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText; import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
import android.widget.TextView;
import net.i2p.android.ui.I2PAndroidHelper;
import acr.browser.lightning.R;
import acr.browser.lightning.constant.Constants; import acr.browser.lightning.constant.Constants;
import acr.browser.lightning.preference.PreferenceManager; import acr.browser.lightning.preference.PreferenceManager;
import acr.browser.lightning.R;
import acr.browser.lightning.utils.Utils; import acr.browser.lightning.utils.Utils;
import info.guardianproject.onionkit.ui.OrbotHelper; import info.guardianproject.onionkit.ui.OrbotHelper;
@ -36,6 +40,8 @@ public class SettingsActivity extends ThemableSettingsActivity {
private PreferenceManager mPreferences; private PreferenceManager mPreferences;
private Context mContext; private Context mContext;
private Activity mActivity; private Activity mActivity;
private CharSequence[] mProxyChoices;
private TextView mProxyChoiceName;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -69,7 +75,7 @@ public class SettingsActivity extends ThemableSettingsActivity {
layoutBlockAds.setEnabled(Constants.FULL_VERSION); layoutBlockAds.setEnabled(Constants.FULL_VERSION);
RelativeLayout layoutImages = (RelativeLayout) findViewById(R.id.layoutImages); RelativeLayout layoutImages = (RelativeLayout) findViewById(R.id.layoutImages);
RelativeLayout layoutEnableJS = (RelativeLayout) findViewById(R.id.layoutEnableJS); RelativeLayout layoutEnableJS = (RelativeLayout) findViewById(R.id.layoutEnableJS);
RelativeLayout layoutOrbot = (RelativeLayout) findViewById(R.id.layoutUseOrbot); LinearLayout layoutProxyChoice = (LinearLayout) findViewById(R.id.layoutProxyChoice);
RelativeLayout layoutColor = (RelativeLayout) findViewById(R.id.layoutColorMode); RelativeLayout layoutColor = (RelativeLayout) findViewById(R.id.layoutColorMode);
RelativeLayout layoutBookmarks = (RelativeLayout) findViewById(R.id.layoutBookmarks); RelativeLayout layoutBookmarks = (RelativeLayout) findViewById(R.id.layoutBookmarks);
@ -89,12 +95,19 @@ public class SettingsActivity extends ThemableSettingsActivity {
boolean imagesBool = mPreferences.getBlockImagesEnabled(); boolean imagesBool = mPreferences.getBlockImagesEnabled();
boolean enableJSBool = mPreferences.getJavaScriptEnabled(); boolean enableJSBool = mPreferences.getJavaScriptEnabled();
mProxyChoiceName = (TextView) findViewById(R.id.proxyChoiceName);
mProxyChoices = this.getResources().getStringArray(R.array.proxy_choices_array);
int choice = mPreferences.getProxyChoice();
if (choice == Constants.PROXY_MANUAL)
mProxyChoiceName.setText(mPreferences.getProxyHost() + ":" + mPreferences.getProxyPort());
else
mProxyChoiceName.setText(mProxyChoices[choice]);
CheckBox flash = (CheckBox) findViewById(R.id.cbFlash); CheckBox flash = (CheckBox) findViewById(R.id.cbFlash);
CheckBox adblock = (CheckBox) findViewById(R.id.cbAdblock); CheckBox adblock = (CheckBox) findViewById(R.id.cbAdblock);
adblock.setEnabled(Constants.FULL_VERSION); adblock.setEnabled(Constants.FULL_VERSION);
CheckBox images = (CheckBox) findViewById(R.id.cbImageBlock); CheckBox images = (CheckBox) findViewById(R.id.cbImageBlock);
CheckBox enablejs = (CheckBox) findViewById(R.id.cbJavascript); CheckBox enablejs = (CheckBox) findViewById(R.id.cbJavascript);
CheckBox orbot = (CheckBox) findViewById(R.id.cbOrbot);
CheckBox color = (CheckBox) findViewById(R.id.cbColorMode); CheckBox color = (CheckBox) findViewById(R.id.cbColorMode);
images.setChecked(imagesBool); images.setChecked(imagesBool);
@ -105,12 +118,11 @@ public class SettingsActivity extends ThemableSettingsActivity {
flash.setChecked(false); flash.setChecked(false);
} }
adblock.setChecked(mPreferences.getAdBlockEnabled()); adblock.setChecked(mPreferences.getAdBlockEnabled());
orbot.setChecked(mPreferences.getUseProxy());
color.setChecked(mPreferences.getColorModeEnabled()); color.setChecked(mPreferences.getColorModeEnabled());
initCheckBox(flash, adblock, images, enablejs, orbot, color); initCheckBox(flash, adblock, images, enablejs, color);
clickListenerForCheckBoxes(layoutFlash, layoutBlockAds, layoutImages, layoutEnableJS, clickListenerForCheckBoxes(layoutFlash, layoutBlockAds, layoutImages, layoutEnableJS,
layoutOrbot, layoutColor, flash, adblock, images, enablejs, orbot, color); layoutProxyChoice, layoutColor, flash, adblock, images, enablejs, color);
RelativeLayout general = (RelativeLayout) findViewById(R.id.layoutGeneral); RelativeLayout general = (RelativeLayout) findViewById(R.id.layoutGeneral);
RelativeLayout display = (RelativeLayout) findViewById(R.id.layoutDisplay); RelativeLayout display = (RelativeLayout) findViewById(R.id.layoutDisplay);
@ -127,9 +139,9 @@ public class SettingsActivity extends ThemableSettingsActivity {
public void clickListenerForCheckBoxes(RelativeLayout layoutFlash, public void clickListenerForCheckBoxes(RelativeLayout layoutFlash,
RelativeLayout layoutBlockAds, RelativeLayout layoutImages, RelativeLayout layoutBlockAds, RelativeLayout layoutImages,
RelativeLayout layoutEnableJS, RelativeLayout layoutOrbot, RelativeLayout layoutColor, RelativeLayout layoutEnableJS, LinearLayout layoutProxyChoice, RelativeLayout layoutColor,
final CheckBox flash, final CheckBox adblock, final CheckBox images, final CheckBox flash, final CheckBox adblock, final CheckBox images,
final CheckBox enablejs, final CheckBox orbot, final CheckBox color) { final CheckBox enablejs, final CheckBox color) {
layoutFlash.setOnClickListener(new OnClickListener() { layoutFlash.setOnClickListener(new OnClickListener() {
@Override @Override
@ -168,17 +180,12 @@ public class SettingsActivity extends ThemableSettingsActivity {
} }
}); });
layoutOrbot.setOnClickListener(new OnClickListener() { layoutProxyChoice.setOnClickListener(new OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View view) {
if (orbot.isEnabled()) { proxyChoicePicker();
orbot.setChecked(!orbot.isChecked());
} else {
Utils.showToast(mContext, getResources().getString(R.string.install_orbot));
} }
}
}); });
layoutColor.setOnClickListener(new OnClickListener() { layoutColor.setOnClickListener(new OnClickListener() {
@ -191,7 +198,7 @@ public class SettingsActivity extends ThemableSettingsActivity {
} }
public void initCheckBox(CheckBox flash, CheckBox adblock, CheckBox images, CheckBox enablejs, public void initCheckBox(CheckBox flash, CheckBox adblock, CheckBox images, CheckBox enablejs,
CheckBox orbot, CheckBox color) { CheckBox color) {
flash.setEnabled(API < 19); flash.setEnabled(API < 19);
flash.setOnCheckedChangeListener(new OnCheckedChangeListener() { flash.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@ -253,20 +260,7 @@ public class SettingsActivity extends ThemableSettingsActivity {
} }
}); });
OrbotHelper oh = new OrbotHelper(this);
if (!oh.isOrbotInstalled()) {
orbot.setEnabled(false);
}
orbot.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
mPreferences.setUseProxy(isChecked);
}
});
color.setOnCheckedChangeListener(new OnCheckedChangeListener() { color.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override @Override
@ -309,6 +303,77 @@ public class SettingsActivity extends ThemableSettingsActivity {
alert.show(); alert.show();
} }
private void proxyChoicePicker() {
AlertDialog.Builder picker = new AlertDialog.Builder(mContext);
picker.setTitle(getResources().getString(R.string.http_proxy));
picker.setSingleChoiceItems(mProxyChoices, mPreferences.getProxyChoice(),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
setProxyChoice(which);
}
});
picker.setNeutralButton(getResources().getString(R.string.action_ok),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}
});
picker.show();
}
private void setProxyChoice(int choice) {
switch (choice) {
case Constants.PROXY_ORBOT:
OrbotHelper oh = new OrbotHelper(this);
if (!oh.isOrbotInstalled()) {
choice = Constants.NO_PROXY;
Utils.showToast(mContext, getResources().getString(R.string.install_orbot));
}
break;
case Constants.PROXY_I2P:
I2PAndroidHelper ih = new I2PAndroidHelper(this);
if (!ih.isI2PAndroidInstalled()) {
choice = Constants.NO_PROXY;
ih.promptToInstall(this);
}
break;
case Constants.PROXY_MANUAL:
manualProxyPicker();
break;
}
mPreferences.setProxyChoice(choice);
if (choice < mProxyChoices.length)
mProxyChoiceName.setText(mProxyChoices[choice]);
}
public void manualProxyPicker() {
View v = getLayoutInflater().inflate(R.layout.picker_manual_proxy, null);
final EditText eProxyHost = (EditText) v.findViewById(R.id.proxyHost);
final EditText eProxyPort = (EditText) v.findViewById(R.id.proxyPort);
eProxyHost.setText(mPreferences.getProxyHost());
eProxyPort.setText(Integer.toString(mPreferences.getProxyPort()));
new AlertDialog.Builder(mActivity)
.setTitle(R.string.manual_proxy)
.setView(v)
.setPositiveButton(R.string.action_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
String proxyHost = eProxyHost.getText().toString();
int proxyPort = Integer.parseInt(eProxyPort.getText().toString());
mPreferences.setProxyHost(proxyHost);
mPreferences.setProxyPort(proxyPort);
mProxyChoiceName.setText(proxyHost + ":" + proxyPort);
}
})
.show();
}
public void agentPicker() { public void agentPicker() {
final AlertDialog.Builder agentStringPicker = new AlertDialog.Builder(mActivity); final AlertDialog.Builder agentStringPicker = new AlertDialog.Builder(mActivity);

6
app/src/main/java/acr/browser/lightning/constant/Constants.java

@ -38,4 +38,10 @@ public final class Constants {
public static final String FILE = "file://"; public static final String FILE = "file://";
public static final String FOLDER = "folder://"; public static final String FOLDER = "folder://";
public static final String TAG = "Lightning"; public static final String TAG = "Lightning";
// These should match the order of @array/proxy_choices_array
public static final int NO_PROXY = 0;
public static final int PROXY_ORBOT = 1;
public static final int PROXY_I2P = 2;
public static final int PROXY_MANUAL = 3;
} }

2
app/src/main/java/acr/browser/lightning/controller/BrowserController.java

@ -54,5 +54,7 @@ public interface BrowserController {
boolean isIncognito(); boolean isIncognito();
boolean isProxyReady();
int getMenu(); int getMenu();
} }

37
app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java

@ -49,9 +49,11 @@ public class PreferenceManager {
public static final String DEFAULT_BOOKMARKS = "defaultBookmarks"; public static final String DEFAULT_BOOKMARKS = "defaultBookmarks";
public static final String USE_PROXY = "useProxy"; public static final String USE_PROXY = "useProxy";
public static final String PROXY_CHOICE = "proxyChoice";
public static final String USE_PROXY_HOST = "useProxyHost"; public static final String USE_PROXY_HOST = "useProxyHost";
public static final String USE_PROXY_PORT = "useProxyPort"; public static final String USE_PROXY_PORT = "useProxyPort";
public static final String INITIAL_CHECK_FOR_TOR = "checkForTor"; public static final String INITIAL_CHECK_FOR_TOR = "checkForTor";
public static final String INITIAL_CHECK_FOR_I2P = "checkForI2P";
} }
private static PreferenceManager mInstance; private static PreferenceManager mInstance;
@ -86,6 +88,10 @@ public class PreferenceManager {
return mPrefs.getBoolean(Name.INITIAL_CHECK_FOR_TOR, false); return mPrefs.getBoolean(Name.INITIAL_CHECK_FOR_TOR, false);
} }
public boolean getCheckedForI2P() {
return mPrefs.getBoolean(Name.INITIAL_CHECK_FOR_I2P, false);
}
public boolean getClearCacheExit() { public boolean getClearCacheExit() {
return mPrefs.getBoolean(Name.CLEAR_CACHE_EXIT, false); return mPrefs.getBoolean(Name.CLEAR_CACHE_EXIT, false);
} }
@ -226,6 +232,10 @@ public class PreferenceManager {
return mPrefs.getBoolean(Name.USE_PROXY, false); return mPrefs.getBoolean(Name.USE_PROXY, false);
} }
public int getProxyChoice() {
return mPrefs.getInt(Name.PROXY_CHOICE, Constants.NO_PROXY);
}
public int getUserAgentChoice() { public int getUserAgentChoice() {
return mPrefs.getInt(Name.USER_AGENT, 1); return mPrefs.getInt(Name.USER_AGENT, 1);
} }
@ -266,6 +276,10 @@ public class PreferenceManager {
putBoolean(Name.INITIAL_CHECK_FOR_TOR, check); putBoolean(Name.INITIAL_CHECK_FOR_TOR, check);
} }
public void setCheckedForI2P(boolean check) {
putBoolean(Name.INITIAL_CHECK_FOR_I2P, check);
}
public void setClearCacheExit(boolean enable) { public void setClearCacheExit(boolean enable) {
putBoolean(Name.CLEAR_CACHE_EXIT, enable); putBoolean(Name.CLEAR_CACHE_EXIT, enable);
} }
@ -394,8 +408,27 @@ public class PreferenceManager {
putBoolean(Name.DARK_THEME, use); putBoolean(Name.DARK_THEME, use);
} }
public void setUseProxy(boolean enable) { /**
putBoolean(Name.USE_PROXY, enable); * Valid choices:
* <ul>
* <li>{@link Constants#NO_PROXY}</li>
* <li>{@link Constants#PROXY_ORBOT}</li>
* <li>{@link Constants#PROXY_I2P}</li>
* </ul>
*
* @param choice the proxy to use.
*/
public void setProxyChoice(int choice) {
putBoolean(Name.USE_PROXY, choice != Constants.NO_PROXY);
putInt(Name.PROXY_CHOICE, choice);
}
public void setProxyHost(String proxyHost) {
putString(Name.USE_PROXY_HOST, proxyHost);
}
public void setProxyPort(int proxyPort) {
putInt(Name.USE_PROXY_PORT, proxyPort);
} }
public void setUserAgentChoice(int choice) { public void setUserAgentChoice(int choice) {

18
app/src/main/java/acr/browser/lightning/view/LightningView.java

@ -526,6 +526,12 @@ public class LightningView {
} }
public synchronized void reload() { public synchronized void reload() {
// Check if configured proxy is available
if (!mBrowserController.isProxyReady()) {
// User has been notified
return;
}
if (mWebView != null) { if (mWebView != null) {
mWebView.reload(); mWebView.reload();
} }
@ -611,6 +617,12 @@ public class LightningView {
} }
public synchronized void loadUrl(String url) { public synchronized void loadUrl(String url) {
// Check if configured proxy is available
if (!mBrowserController.isProxyReady()) {
// User has been notified
return;
}
if (mWebView != null) { if (mWebView != null) {
mWebView.loadUrl(url); mWebView.loadUrl(url);
} }
@ -815,6 +827,12 @@ public class LightningView {
@Override @Override
public boolean shouldOverrideUrlLoading(WebView view, String url) { public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Check if configured proxy is available
if (!mBrowserController.isProxyReady()) {
// User has been notified
return true;
}
if (mBrowserController.isIncognito()) { if (mBrowserController.isIncognito()) {
return super.shouldOverrideUrlLoading(view, url); return super.shouldOverrideUrlLoading(view, url);
} }

43
app/src/main/res/layout/picker_manual_proxy.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="4dp"
android:paddingStart="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/host"/>
<EditText
android:id="@+id/proxyHost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="text"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/port"/>
<EditText
android:id="@+id/proxyPort"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number"/>
</LinearLayout>
</LinearLayout>

24
app/src/main/res/layout/settings.xml

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -159,33 +160,32 @@
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:background="?attr/dividerColor" /> android:background="?attr/dividerColor" />
<RelativeLayout <LinearLayout
android:id="@+id/layoutUseOrbot" android:id="@+id/layoutProxyChoice"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/listChoiceBackgroundIndicator" android:background="?attr/listChoiceBackgroundIndicator"
android:minHeight="60dp" android:minHeight="60dp"
android:orientation="vertical"
android:paddingBottom="10dp" android:paddingBottom="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp" > android:paddingTop="10dp" >
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:paddingLeft="16dp" android:paddingLeft="16dp"
android:paddingRight="60dp" android:text="@string/http_proxy"
android:text="@string/enable_orbot"
android:textAppearance="?android:attr/textAppearanceMedium" /> android:textAppearance="?android:attr/textAppearanceMedium" />
<CheckBox <TextView
android:id="@+id/cbOrbot" android:id="@+id/proxyChoiceName"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentRight="true" android:paddingLeft="16dp"
android:layout_centerVertical="true" /> android:textAppearance="?android:attr/textAppearanceSmall"
</RelativeLayout> android:textColor="@color/light"
tools:text="Disabled" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

14
app/src/main/res/values/strings.xml

@ -158,9 +158,21 @@
<string name="powered_by_google">Powered by Google</string> <string name="powered_by_google">Powered by Google</string>
<string name="title_adblock">AdBlock</string> <string name="title_adblock">AdBlock</string>
<string name="message_adblock">AdBlock is currently only available in the browser\'s paid version, Lightning Browser+. It is available to download on Google Play.</string> <string name="message_adblock">AdBlock is currently only available in the browser\'s paid version, Lightning Browser+. It is available to download on Google Play.</string>
<string name="enable_orbot">Enable Orbot</string> <string name="http_proxy">HTTP proxy</string>
<string-array name="proxy_choices_array">
<item>None</item>
<item>Orbot</item>
<item>I2P</item>
<item>Manual</item>
</string-array>
<string name="manual_proxy">Manual proxy</string>
<string name="host">Host:</string>
<string name="port">Port:</string>
<string name="use_tor_prompt">It looks like you have Orbot installed. Do you want to use Tor?</string> <string name="use_tor_prompt">It looks like you have Orbot installed. Do you want to use Tor?</string>
<string name="use_i2p_prompt">It looks like you have I2P installed. Do you want to use I2P?</string>
<string name="install_orbot">Please install Orbot in order to proxy with Tor.</string> <string name="install_orbot">Please install Orbot in order to proxy with Tor.</string>
<string name="i2p_not_running">I2P is not running.</string>
<string name="i2p_tunnels_not_ready">I2P tunnels are not ready yet.</string>
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="no">No</string> <string name="no">No</string>
<string name="clear_cookies_exit">Clear cookies on exit</string> <string name="clear_cookies_exit">Clear cookies on exit</string>

Loading…
Cancel
Save