diff --git a/app/build.gradle b/app/build.gradle index b8f4727..ddbae40 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,4 +39,5 @@ dependencies { compile 'com.android.support:palette-v7:22.1.1' compile 'com.android.support:appcompat-v7:22.1.1' compile 'org.jsoup:jsoup:1.8.1' + compile 'net.i2p.android:client:0.7@aar' } diff --git a/app/proguard-project.txt b/app/proguard-project.txt index 4bab3ea..c821b33 100644 --- a/app/proguard-project.txt +++ b/app/proguard-project.txt @@ -77,3 +77,9 @@ # Don't warn about those in case this app is linking against an older # platform version. We know about them, and they are safe. -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 diff --git a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java b/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java index bde3d06..8190213 100644 --- a/app/src/main/java/acr/browser/lightning/activity/BrowserActivity.java +++ b/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.app.Activity; 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.Resources.Theme; import android.database.Cursor; @@ -36,15 +40,26 @@ import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; 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.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.OnFocusChangeListener; import android.view.View.OnKeyListener; import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; +import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.Window; +import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; @@ -52,42 +67,69 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Transformation; import android.view.inputmethod.EditorInfo; 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.WebIconDatabase; +import android.webkit.WebStorage; +import android.webkit.WebView; import android.webkit.WebView.HitTestResult; -import android.widget.*; -import android.support.v7.app.ActionBar; -import android.support.v7.graphics.Palette; -import android.support.v7.widget.Toolbar; +import android.webkit.WebViewDatabase; +import android.widget.AdapterView; 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.Toast; +import android.widget.VideoView; -import acr.browser.lightning.view.AnimatedProgressBar; -import acr.browser.lightning.database.BookmarkManager; +import net.i2p.android.ui.I2PAndroidHelper; + +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.controller.BrowserController; -import acr.browser.lightning.object.ClickHandler; 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.HistoryItem; -import acr.browser.lightning.constant.HistoryPage; -import acr.browser.lightning.view.LightningView; -import acr.browser.lightning.preference.PreferenceManager; -import acr.browser.lightning.R; +import acr.browser.lightning.object.ClickHandler; +import acr.browser.lightning.object.DrawerArrowDrawable; import acr.browser.lightning.object.SearchAdapter; +import acr.browser.lightning.preference.PreferenceManager; 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.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 { // Layout @@ -140,6 +182,11 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll private Drawable mDeleteIcon, mRefreshIcon, mCopyIcon, mIcon; private DrawerArrowDrawable mArrowDrawable; + // Helper + private I2PAndroidHelper mI2PHelper; + private boolean mI2PHelperBound; + private boolean mI2PProxyInitialized; + // Constant private static final int API = android.os.Build.VERSION.SDK_INT; 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); arrowButton.setOnClickListener(this); + mI2PHelper = new I2PAndroidHelper(this); + RelativeLayout back = (RelativeLayout) findViewById(R.id.action_back); back.setOnClickListener(this); @@ -304,7 +353,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll WebIconDatabase.getInstance().open(getDir("icons", MODE_PRIVATE).getPath()); } - checkForTor(); + checkForProxy(); } 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 */ - private boolean checkForTor() { + private void checkForProxy() { boolean useProxy = mPreferences.getUseProxy(); OrbotHelper oh = new OrbotHelper(this); - if (oh.isOrbotInstalled() && !mPreferences.getCheckedForTor()) { - mPreferences.setCheckedForTor(true); - DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - switch (which) { - case DialogInterface.BUTTON_POSITIVE: - mPreferences.setUseProxy(true); - initializeTor(); - break; - case DialogInterface.BUTTON_NEGATIVE: - mPreferences.setUseProxy(false); - break; - } - } - }; - + final boolean orbotInstalled = oh.isOrbotInstalled(); + 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); - builder.setMessage(R.string.use_tor_prompt) - .setPositiveButton(R.string.yes, dialogClickListener) - .setNegativeButton(R.string.no, dialogClickListener).show(); - return true; - } else if (oh.isOrbotInstalled() & useProxy) { - return true; - } else { - mPreferences.setUseProxy(false); - return false; + 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() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which) { + case DialogInterface.BUTTON_POSITIVE: + mPreferences.setProxyChoice(orbotInstalled ? + Constants.PROXY_ORBOT : Constants.PROXY_I2P); + initializeProxy(); + break; + case DialogInterface.BUTTON_NEGATIVE: + mPreferences.setProxyChoice(Constants.NO_PROXY); + break; + } + } + }; + + builder.setMessage(orbotInstalled ? R.string.use_tor_prompt : R.string.use_i2p_prompt) + .setPositiveButton(R.string.yes, dialogClickListener) + .setNegativeButton(R.string.no, dialogClickListener); + } + builder.show(); } } /* - * 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); + if (!oh.isOrbotRunning()) { + oh.requestOrbotStart(this); + } + host = "localhost"; + port = 8118; + break; - OrbotHelper oh = new OrbotHelper(this); - if (!oh.isOrbotRunning()) { - oh.requestOrbotStart(this); + 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 { - String host = mPreferences.getProxyHost(); - int port = mPreferences.getProxyPort(); WebkitProxy.setProxy(BrowserApp.class.getName(), getApplicationContext(), host, port); } 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() { return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE; } @@ -704,7 +817,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll updateCookiePreference(); if (mPreferences.getUseProxy()) { - initializeTor(); + initializeProxy(); } else { try { WebkitProxy.resetProxy(BrowserApp.class.getName(), @@ -712,6 +825,7 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll } catch (Exception e) { 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 protected void onDestroy() { Log.d(Constants.TAG, "onDestroy"); @@ -1378,6 +1499,22 @@ public class BrowserActivity extends ThemableActivity implements BrowserControll 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 protected void onResume() { super.onResume(); diff --git a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java b/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java index c5a8b92..9ee017a 100644 --- a/app/src/main/java/acr/browser/lightning/activity/SettingsActivity.java +++ b/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.OnCheckedChangeListener; import android.widget.EditText; +import android.widget.LinearLayout; 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.preference.PreferenceManager; -import acr.browser.lightning.R; import acr.browser.lightning.utils.Utils; import info.guardianproject.onionkit.ui.OrbotHelper; @@ -36,6 +40,8 @@ public class SettingsActivity extends ThemableSettingsActivity { private PreferenceManager mPreferences; private Context mContext; private Activity mActivity; + private CharSequence[] mProxyChoices; + private TextView mProxyChoiceName; @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,7 +75,7 @@ public class SettingsActivity extends ThemableSettingsActivity { layoutBlockAds.setEnabled(Constants.FULL_VERSION); RelativeLayout layoutImages = (RelativeLayout) findViewById(R.id.layoutImages); 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 layoutBookmarks = (RelativeLayout) findViewById(R.id.layoutBookmarks); @@ -89,12 +95,19 @@ public class SettingsActivity extends ThemableSettingsActivity { boolean imagesBool = mPreferences.getBlockImagesEnabled(); 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 adblock = (CheckBox) findViewById(R.id.cbAdblock); adblock.setEnabled(Constants.FULL_VERSION); CheckBox images = (CheckBox) findViewById(R.id.cbImageBlock); CheckBox enablejs = (CheckBox) findViewById(R.id.cbJavascript); - CheckBox orbot = (CheckBox) findViewById(R.id.cbOrbot); CheckBox color = (CheckBox) findViewById(R.id.cbColorMode); images.setChecked(imagesBool); @@ -105,12 +118,11 @@ public class SettingsActivity extends ThemableSettingsActivity { flash.setChecked(false); } adblock.setChecked(mPreferences.getAdBlockEnabled()); - orbot.setChecked(mPreferences.getUseProxy()); color.setChecked(mPreferences.getColorModeEnabled()); - initCheckBox(flash, adblock, images, enablejs, orbot, color); + initCheckBox(flash, adblock, images, enablejs, color); 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 display = (RelativeLayout) findViewById(R.id.layoutDisplay); @@ -127,9 +139,9 @@ public class SettingsActivity extends ThemableSettingsActivity { public void clickListenerForCheckBoxes(RelativeLayout layoutFlash, 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 enablejs, final CheckBox orbot, final CheckBox color) { + final CheckBox enablejs, final CheckBox color) { layoutFlash.setOnClickListener(new OnClickListener() { @Override @@ -168,17 +180,12 @@ public class SettingsActivity extends ThemableSettingsActivity { } }); - layoutOrbot.setOnClickListener(new OnClickListener() { + layoutProxyChoice.setOnClickListener(new OnClickListener() { @Override - public void onClick(View v) { - if (orbot.isEnabled()) { - orbot.setChecked(!orbot.isChecked()); - } else { - Utils.showToast(mContext, getResources().getString(R.string.install_orbot)); - } + public void onClick(View view) { + proxyChoicePicker(); } - }); layoutColor.setOnClickListener(new OnClickListener() { @@ -191,7 +198,7 @@ public class SettingsActivity extends ThemableSettingsActivity { } public void initCheckBox(CheckBox flash, CheckBox adblock, CheckBox images, CheckBox enablejs, - CheckBox orbot, CheckBox color) { + CheckBox color) { flash.setEnabled(API < 19); 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() { @Override @@ -309,6 +303,77 @@ public class SettingsActivity extends ThemableSettingsActivity { 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() { final AlertDialog.Builder agentStringPicker = new AlertDialog.Builder(mActivity); diff --git a/app/src/main/java/acr/browser/lightning/constant/Constants.java b/app/src/main/java/acr/browser/lightning/constant/Constants.java index f8a25ce..c3ba14c 100644 --- a/app/src/main/java/acr/browser/lightning/constant/Constants.java +++ b/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 FOLDER = "folder://"; 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; } diff --git a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java index 8fdbd53..c48e805 100644 --- a/app/src/main/java/acr/browser/lightning/controller/BrowserController.java +++ b/app/src/main/java/acr/browser/lightning/controller/BrowserController.java @@ -54,5 +54,7 @@ public interface BrowserController { boolean isIncognito(); + boolean isProxyReady(); + int getMenu(); } diff --git a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java b/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java index 275e93d..c6d1316 100644 --- a/app/src/main/java/acr/browser/lightning/preference/PreferenceManager.java +++ b/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 USE_PROXY = "useProxy"; + public static final String PROXY_CHOICE = "proxyChoice"; public static final String USE_PROXY_HOST = "useProxyHost"; public static final String USE_PROXY_PORT = "useProxyPort"; public static final String INITIAL_CHECK_FOR_TOR = "checkForTor"; + public static final String INITIAL_CHECK_FOR_I2P = "checkForI2P"; } private static PreferenceManager mInstance; @@ -86,6 +88,10 @@ public class PreferenceManager { return mPrefs.getBoolean(Name.INITIAL_CHECK_FOR_TOR, false); } + public boolean getCheckedForI2P() { + return mPrefs.getBoolean(Name.INITIAL_CHECK_FOR_I2P, false); + } + public boolean getClearCacheExit() { return mPrefs.getBoolean(Name.CLEAR_CACHE_EXIT, false); } @@ -226,6 +232,10 @@ public class PreferenceManager { return mPrefs.getBoolean(Name.USE_PROXY, false); } + public int getProxyChoice() { + return mPrefs.getInt(Name.PROXY_CHOICE, Constants.NO_PROXY); + } + public int getUserAgentChoice() { return mPrefs.getInt(Name.USER_AGENT, 1); } @@ -266,6 +276,10 @@ public class PreferenceManager { putBoolean(Name.INITIAL_CHECK_FOR_TOR, check); } + public void setCheckedForI2P(boolean check) { + putBoolean(Name.INITIAL_CHECK_FOR_I2P, check); + } + public void setClearCacheExit(boolean enable) { putBoolean(Name.CLEAR_CACHE_EXIT, enable); } @@ -394,8 +408,27 @@ public class PreferenceManager { putBoolean(Name.DARK_THEME, use); } - public void setUseProxy(boolean enable) { - putBoolean(Name.USE_PROXY, enable); + /** + * Valid choices: + * + * + * @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) { diff --git a/app/src/main/java/acr/browser/lightning/view/LightningView.java b/app/src/main/java/acr/browser/lightning/view/LightningView.java index f50b87d..d65fc4c 100644 --- a/app/src/main/java/acr/browser/lightning/view/LightningView.java +++ b/app/src/main/java/acr/browser/lightning/view/LightningView.java @@ -526,6 +526,12 @@ public class LightningView { } public synchronized void reload() { + // Check if configured proxy is available + if (!mBrowserController.isProxyReady()) { + // User has been notified + return; + } + if (mWebView != null) { mWebView.reload(); } @@ -611,6 +617,12 @@ public class LightningView { } public synchronized void loadUrl(String url) { + // Check if configured proxy is available + if (!mBrowserController.isProxyReady()) { + // User has been notified + return; + } + if (mWebView != null) { mWebView.loadUrl(url); } @@ -815,6 +827,12 @@ public class LightningView { @Override 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()) { return super.shouldOverrideUrlLoading(view, url); } diff --git a/app/src/main/res/layout/picker_manual_proxy.xml b/app/src/main/res/layout/picker_manual_proxy.xml new file mode 100644 index 0000000..f8568ac --- /dev/null +++ b/app/src/main/res/layout/picker_manual_proxy.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/settings.xml b/app/src/main/res/layout/settings.xml index 3a04e18..4ba9191 100644 --- a/app/src/main/res/layout/settings.xml +++ b/app/src/main/res/layout/settings.xml @@ -1,8 +1,9 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> @@ -159,33 +160,32 @@ android:layout_marginRight="10dp" android:background="?attr/dividerColor" /> - - - + android:paddingLeft="16dp" + android:textAppearance="?android:attr/textAppearanceSmall" + android:textColor="@color/light" + tools:text="Disabled" /> + Powered by Google AdBlock AdBlock is currently only available in the browser\'s paid version, Lightning Browser+. It is available to download on Google Play. - Enable Orbot + HTTP proxy + + None + Orbot + I2P + Manual + + Manual proxy + Host: + Port: It looks like you have Orbot installed. Do you want to use Tor? + It looks like you have I2P installed. Do you want to use I2P? Please install Orbot in order to proxy with Tor. + I2P is not running. + I2P tunnels are not ready yet. Yes No Clear cookies on exit