From 7aa201a94da727c1d8784e5bc2437fa234d09040 Mon Sep 17 00:00:00 2001 From: Daniel Velazco Date: Sat, 4 Jan 2014 01:03:16 -0800 Subject: [PATCH] Add ability to set a proxy. If Orbot is installed, a request to start its connection will be made if the proxy is enabled and Orbot is not connected. --- .../com/danvelazco/fbwrapper/FbWrapper.java | 23 +- .../activity/BaseFacebookWebViewActivity.java | 40 ++- .../preferences/FacebookPreferences.java | 46 +++- .../fbwrapper/util/OrbotHelper.java | 91 +++++++ .../fbwrapper/util/TorServiceUtils.java | 201 ++++++++++++++++ .../fbwrapper/util/WebViewProxyUtil.java | 227 ++++++++++++++++++ .../src/main/res/values/strings.xml | 12 + .../src/main/res/xml/main_preferences.xml | 23 ++ 8 files changed, 649 insertions(+), 14 deletions(-) create mode 100644 Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/OrbotHelper.java create mode 100644 Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/TorServiceUtils.java create mode 100644 Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/WebViewProxyUtil.java diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/FbWrapper.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/FbWrapper.java index 3298653..15094e2 100644 --- a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/FbWrapper.java +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/FbWrapper.java @@ -11,12 +11,14 @@ import android.preference.PreferenceManager; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; +import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.widget.RelativeLayout; import com.danvelazco.fbwrapper.activity.BaseFacebookWebViewActivity; import com.danvelazco.fbwrapper.preferences.FacebookPreferences; import com.danvelazco.fbwrapper.util.Logger; +import com.danvelazco.fbwrapper.util.OrbotHelper; /** * Facebook web wrapper activity. @@ -194,14 +196,33 @@ private void loadPreferences() { mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); } - // Get the URL load and check-in settings + // Get the URL to load, check-in and proxy settings boolean anyDomain = mSharedPreferences.getBoolean(FacebookPreferences.OPEN_LINKS_INSIDE, false); boolean allowCheckins = mSharedPreferences.getBoolean(FacebookPreferences.ALLOW_CHECKINS, false); + boolean enableProxy = mSharedPreferences.getBoolean(FacebookPreferences.KEY_PROXY_ENABLED, false); + String proxyHost = mSharedPreferences.getString(FacebookPreferences.KEY_PROXY_HOST, null); + String proxyPort = mSharedPreferences.getString(FacebookPreferences.KEY_PROXY_PORT, null); // Set the flags for loading URLs and allowing geolocation setAllowCheckins(allowCheckins); setAllowAnyDomain(anyDomain); + if (enableProxy && !TextUtils.isEmpty(proxyHost) && !TextUtils.isEmpty(proxyPort)) { + int proxyPortInt = -1; + try { + proxyPortInt = Integer.parseInt(proxyPort); + } catch (Exception e) { + e.printStackTrace(); + } + setProxy(proxyHost, proxyPortInt); + + // If Orbot is installed and not running, request to start it + OrbotHelper orbotHelper = new OrbotHelper(this); + if (orbotHelper.isOrbotInstalled() && !orbotHelper.isOrbotRunning()) { + orbotHelper.requestOrbotStart(this); + } + } + // Whether the site should be loaded as the mobile or desktop version String mode = mSharedPreferences.getString(FacebookPreferences.SITE_MODE, FacebookPreferences.SITE_MODE_AUTO); diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/activity/BaseFacebookWebViewActivity.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/activity/BaseFacebookWebViewActivity.java index 07f35c4..2650f33 100644 --- a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/activity/BaseFacebookWebViewActivity.java +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/activity/BaseFacebookWebViewActivity.java @@ -29,6 +29,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.text.TextUtils; import android.view.KeyEvent; import android.view.View; import android.webkit.CookieSyncManager; @@ -39,6 +40,8 @@ import android.widget.ProgressBar; import com.danvelazco.fbwrapper.R; import com.danvelazco.fbwrapper.util.Logger; +import com.danvelazco.fbwrapper.util.OrbotHelper; +import com.danvelazco.fbwrapper.util.WebViewProxyUtil; import com.danvelazco.fbwrapper.webview.FacebookWebChromeClient; import com.danvelazco.fbwrapper.webview.FacebookWebView; import com.danvelazco.fbwrapper.webview.FacebookWebViewClient; @@ -221,6 +224,18 @@ public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); } + /** + * Set a proxy for the {@link com.danvelazco.fbwrapper.webview.FacebookWebView} + * + * @param host {@link String} + * @param port {@link int} + */ + protected final void setProxy(String host, int port) { + if (mWebView != null && !TextUtils.isEmpty(host) && port > 0) { + WebViewProxyUtil.setProxy(getApplicationContext(), mWebView, host, port); + } + } + /** * Restore the state of the {@link FacebookWebView} * @@ -379,17 +394,20 @@ private void updateCacheMode() { * {@inheritDoc} */ @Override - protected void onActivityResult(int requestCode, int resultCode, - Intent intent) { - // Handle file uploads - if (requestCode == RESULT_CODE_FILE_UPLOAD) { - if (null == mUploadMessage) { - return; - } - Uri result = intent == null || resultCode != RESULT_OK ? null - : intent.getData(); - mUploadMessage.onReceiveValue(result); - mUploadMessage = null; + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + switch (requestCode) { + case OrbotHelper.REQUEST_CODE_START_ORBOT: + mWebView.reload(); + break; + case RESULT_CODE_FILE_UPLOAD: + if (null == mUploadMessage) { + return; + } + Uri result = intent == null || resultCode != RESULT_OK ? null + : intent.getData(); + mUploadMessage.onReceiveValue(result); + mUploadMessage = null; + break; } } diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/preferences/FacebookPreferences.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/preferences/FacebookPreferences.java index e05f093..6e90490 100644 --- a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/preferences/FacebookPreferences.java +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/preferences/FacebookPreferences.java @@ -18,8 +18,8 @@ import android.app.AlertDialog; import android.content.DialogInterface; -import android.os.Build; import android.os.Bundle; +import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceScreen; @@ -37,16 +37,23 @@ public class FacebookPreferences extends PreferenceActivity { // Custom preferences public final static String MENU_DRAWER_SHOWED_OPENED = "drawer_shown_opened"; - // Shared preferences + // Shared preference keys public final static String CAT_GENERAL = "pref_cat_general"; public final static String ALLOW_CHECKINS = "prefs_allow_checkins"; public final static String OPEN_LINKS_INSIDE = "prefs_open_links_inside"; + public final static String KEY_PROXY_ENABLED = "prefs_enable_proxy"; + public final static String KEY_PROXY_HOST = "prefs_proxy_host"; + public final static String KEY_PROXY_PORT = "prefs_proxy_port"; public final static String SITE_MODE = "prefs_mobile_site"; public final static String SITE_MODE_AUTO = "auto"; public final static String SITE_MODE_MOBILE = "mobile"; public final static String SITE_MODE_DESKTOP = "desktop"; public final static String ABOUT = "pref_about"; + // Preferences + private EditTextPreference mPrefProxyHost = null; + private EditTextPreference mPrefProxyPort = null; + /** * {@inheritDoc} */ @@ -55,6 +62,41 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getActionBar().setDisplayHomeAsUpEnabled(true); addPreferencesFromResource(R.xml.main_preferences); + + mPrefProxyHost = (EditTextPreference) findPreference(KEY_PROXY_HOST); + mPrefProxyHost.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String newProxyHostValue = (String) newValue; + mPrefProxyHost.setSummary(newProxyHostValue); + return true; + } + }); + + mPrefProxyPort = (EditTextPreference) findPreference(KEY_PROXY_PORT); + mPrefProxyPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + String newProxyPortValue = (String) newValue; + mPrefProxyPort.setSummary(newProxyPortValue); + return true; + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + protected void onResume() { + super.onResume(); + + if (mPrefProxyHost != null) { + mPrefProxyHost.setSummary(mPrefProxyHost.getText()); + } + if (mPrefProxyPort != null) { + mPrefProxyPort.setSummary(mPrefProxyPort.getText()); + } } /** diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/OrbotHelper.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/OrbotHelper.java new file mode 100644 index 0000000..f3ac18f --- /dev/null +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/OrbotHelper.java @@ -0,0 +1,91 @@ +package com.danvelazco.fbwrapper.util; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.PackageManager; +import com.danvelazco.fbwrapper.R; + +/** + * Modified utility class from OnionKit library + */ +public class OrbotHelper { + + // Constants + public final static String URI_ORBOT = "org.torproject.android"; + public final static String TOR_BIN_PATH = "/data/data/org.torproject.android/app_bin/tor"; + public final static String ACTION_START_TOR = "org.torproject.android.START_TOR"; + public final static int REQUEST_CODE_START_ORBOT = 80010; + + // Members + private Context mContext = null; + + /** + * Constructor + * + * @param context {@link Context} + */ + public OrbotHelper(Context context) { + mContext = context; + } + + /** + * Check whether or not Orbot is running. + * + * @return {@link boolean} + */ + public boolean isOrbotRunning() { + int procId = TorServiceUtils.findProcessId(TOR_BIN_PATH); + return (procId != -1); + } + + /** + * Check whether or not Orbot is installed. + * + * @return {@link boolean} + */ + public boolean isOrbotInstalled() { + return isAppInstalled(URI_ORBOT); + } + + private boolean isAppInstalled(String uri) { + PackageManager pm = mContext.getPackageManager(); + boolean installed = false; + try { + pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES); + installed = true; + } catch (PackageManager.NameNotFoundException e) { + installed = false; + } + return installed; + } + + /** + * Request Orbot to start and connect + * + * @param activity {@link Activity} + */ + public void requestOrbotStart(final Activity activity) { + AlertDialog.Builder downloadDialog = new AlertDialog.Builder(activity); + downloadDialog.setTitle(R.string.start_orbot_); + downloadDialog.setMessage(R.string.orbot_not_running_start_it_question); + downloadDialog.setPositiveButton(R.string.lbl_yes, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(URI_ORBOT); + intent.setAction(ACTION_START_TOR); + activity.startActivityForResult(intent, REQUEST_CODE_START_ORBOT); + } + }); + downloadDialog.setNegativeButton(R.string.lbl_no, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }); + downloadDialog.show(); + + } + +} diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/TorServiceUtils.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/TorServiceUtils.java new file mode 100644 index 0000000..cc8cfcd --- /dev/null +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/TorServiceUtils.java @@ -0,0 +1,201 @@ +package com.danvelazco.fbwrapper.util; + +import android.util.Log; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.URLEncoder; +import java.util.StringTokenizer; + +/** + * Utility class from OnionKit library + */ +public class TorServiceUtils { + + // various console cmds + public final static String SHELL_CMD_CHMOD = "chmod"; + public final static String SHELL_CMD_KILL = "kill -9"; + public final static String SHELL_CMD_RM = "rm"; + public final static String SHELL_CMD_PS = "ps"; + public final static String SHELL_CMD_PIDOF = "pidof"; + public final static String CHMOD_EXE_VALUE = "700"; + private final static String TAG = "TorUtils"; + + public static boolean isRootPossible() { + StringBuilder log = new StringBuilder(); + + try { + // Check if Superuser.apk exists + File fileSU = new File("/system/app/Superuser.apk"); + if (fileSU.exists()) + return true; + + fileSU = new File("/system/app/superuser.apk"); + if (fileSU.exists()) + return true; + + fileSU = new File("/system/bin/su"); + if (fileSU.exists()) { + String[] cmd = { + "su" + }; + int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true); + if (exitCode != 0) + return false; + else + return true; + } + + // Check for 'su' binary + String[] cmd = { + "which su" + }; + int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true); + + if (exitCode == 0) { + Log.d(TAG, "root exists, but not sure about permissions"); + return true; + + } + + } catch (IOException e) { + // this means that there is no root to be had (normally) so we won't + // log anything + Log.e(TAG, "Error checking for root access", e); + + } catch (Exception e) { + Log.e(TAG, "Error checking for root access", e); + // this means that there is no root to be had (normally) + } + + Log.e(TAG, "Could not acquire root permissions"); + + return false; + } + + public static int findProcessId(String command) { + int procId = -1; + + try { + procId = findProcessIdWithPidOf(command); + + if (procId == -1) + procId = findProcessIdWithPS(command); + } catch (Exception e) { + try { + procId = findProcessIdWithPS(command); + } catch (Exception e2) { + Log.e(TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2); + } + } + + return procId; + } + + // use 'pidof' command + public static int findProcessIdWithPidOf(String command) throws Exception { + int procId = -1; + Runtime r = Runtime.getRuntime(); + Process procPs = null; + + String baseName = new File(command).getName(); + // fix contributed my mikos on 2010.12.10 + procPs = r.exec(new String[]{ + SHELL_CMD_PIDOF, baseName + }); + // procPs = r.exec(SHELL_CMD_PIDOF); + + BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream())); + String line = null; + + while ((line = reader.readLine()) != null) { + + try { + // this line should just be the process id + procId = Integer.parseInt(line.trim()); + break; + } catch (NumberFormatException e) { + Log.e("TorServiceUtils", "unable to parse process pid: " + line, e); + } + } + + return procId; + + } + + // use 'ps' command + public static int findProcessIdWithPS(String command) throws Exception { + int procId = -1; + Runtime r = Runtime.getRuntime(); + Process procPs = null; + procPs = r.exec(SHELL_CMD_PS); + + BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream())); + String line = null; + + while ((line = reader.readLine()) != null) { + if (line.indexOf(' ' + command) != -1) { + StringTokenizer st = new StringTokenizer(line, " "); + st.nextToken(); // proc owner + procId = Integer.parseInt(st.nextToken().trim()); + break; + } + } + + return procId; + } + + public static int doShellCommand(String[] cmds, StringBuilder log, boolean runAsRoot, + boolean waitFor) throws Exception { + Process proc = null; + int exitCode = -1; + + if (runAsRoot) { + proc = Runtime.getRuntime().exec("su"); + } else { + proc = Runtime.getRuntime().exec("sh"); + } + + OutputStreamWriter out = new OutputStreamWriter(proc.getOutputStream()); + + for (int i = 0; i < cmds.length; i++) { + // TorService.logMessage("executing shell cmd: " + cmds[i] + + // "; runAsRoot=" + runAsRoot + ";waitFor=" + waitFor); + + out.write(cmds[i]); + out.write("\n"); + } + + out.flush(); + out.write("exit\n"); + out.flush(); + + if (waitFor) { + final char buf[] = new char[10]; + + // Consume the "stdout" + InputStreamReader reader = new InputStreamReader(proc.getInputStream()); + int read = 0; + while ((read = reader.read(buf)) != -1) { + if (log != null) + log.append(buf, 0, read); + } + + // Consume the "stderr" + reader = new InputStreamReader(proc.getErrorStream()); + read = 0; + while ((read = reader.read(buf)) != -1) { + if (log != null) + log.append(buf, 0, read); + } + + exitCode = proc.waitFor(); + } + + return exitCode; + } + +} diff --git a/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/WebViewProxyUtil.java b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/WebViewProxyUtil.java new file mode 100644 index 0000000..896c7d4 --- /dev/null +++ b/Tinfoil-for-Facebook/src/main/java/com/danvelazco/fbwrapper/util/WebViewProxyUtil.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2013 Daniel Velazco + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.danvelazco.fbwrapper.util; + +import android.content.Context; +import android.content.Intent; +import android.net.Proxy; +import android.os.Build; +import android.os.Parcelable; +import android.util.ArrayMap; +import android.util.Log; +import android.webkit.WebView; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class WebViewProxyUtil { + + // Constants + private static final String LOG_TAG = "WebViewProxyUtil"; + + /** + * Helper method to set the proxy to a {@link android.webkit.WebView} + * + * @param context {@link Context} + * @param webview {@link android.webkit.WebView} + * @param host {@link String} + * @param port {@link int} + * + * @return {@link boolean} + */ + public static boolean setProxy(Context context, WebView webview, String host, int port) { + // ICS: 4.0.3 + if (Build.VERSION.SDK_INT <= 15) { + return setProxyICS(webview, host, port); + } + // 4.1 or higher (JB) + else if (Build.VERSION.SDK_INT <= 16) { + return setProxyJBPlus(webview, host, port); + } + // 4.4 or higher (KK) + else { + return setKitKatWebViewProxy(context, host, port); + } + } + + /** + * Set Proxy for Android 4.0.3 and above. + */ + @SuppressWarnings("all") + private static boolean setProxyICS(WebView webview, String host, int port) { + try { + Log.d(LOG_TAG, "Setting proxy with 4.0 API."); + + Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge"); + Class params[] = new Class[1]; + params[0] = Class.forName("android.net.ProxyProperties"); + Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params); + + Class wv = Class.forName("android.webkit.WebView"); + Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore"); + Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webview); + + Class wvc = Class.forName("android.webkit.WebViewCore"); + Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame"); + Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance); + + Class bf = Class.forName("android.webkit.BrowserFrame"); + Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge"); + Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame); + + Class ppclass = Class.forName("android.net.ProxyProperties"); + Class pparams[] = new Class[3]; + pparams[0] = String.class; + pparams[1] = int.class; + pparams[2] = String.class; + Constructor ppcont = ppclass.getConstructor(pparams); + + updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null)); + + Log.d(LOG_TAG, "Setting proxy with 4.0 API successful!"); + return true; + } catch (Exception ex) { + Log.e(LOG_TAG, "failed to set HTTP proxy: " + ex); + return false; + } + } + + /** + * Set Proxy for Android 4.1 and above. + */ + @SuppressWarnings("all") + private static boolean setProxyJBPlus(WebView webview, String host, int port) { + Log.d(LOG_TAG, "Setting proxy with >= 4.1 API."); + + try { + Class wvcClass = Class.forName("android.webkit.WebViewClassic"); + Class wvParams[] = new Class[1]; + wvParams[0] = Class.forName("android.webkit.WebView"); + Method fromWebView = wvcClass.getDeclaredMethod("fromWebView", wvParams); + Object webViewClassic = fromWebView.invoke(null, webview); + + Class wv = Class.forName("android.webkit.WebViewClassic"); + Field mWebViewCoreField = wv.getDeclaredField("mWebViewCore"); + Object mWebViewCoreFieldInstance = getFieldValueSafely(mWebViewCoreField, webViewClassic); + + Class wvc = Class.forName("android.webkit.WebViewCore"); + Field mBrowserFrameField = wvc.getDeclaredField("mBrowserFrame"); + Object mBrowserFrame = getFieldValueSafely(mBrowserFrameField, mWebViewCoreFieldInstance); + + Class bf = Class.forName("android.webkit.BrowserFrame"); + Field sJavaBridgeField = bf.getDeclaredField("sJavaBridge"); + Object sJavaBridge = getFieldValueSafely(sJavaBridgeField, mBrowserFrame); + + Class ppclass = Class.forName("android.net.ProxyProperties"); + Class pparams[] = new Class[3]; + pparams[0] = String.class; + pparams[1] = int.class; + pparams[2] = String.class; + Constructor ppcont = ppclass.getConstructor(pparams); + + Class jwcjb = Class.forName("android.webkit.JWebCoreJavaBridge"); + Class params[] = new Class[1]; + params[0] = Class.forName("android.net.ProxyProperties"); + Method updateProxyInstance = jwcjb.getDeclaredMethod("updateProxy", params); + + updateProxyInstance.invoke(sJavaBridge, ppcont.newInstance(host, port, null)); + } catch (Exception ex) { + Log.e(LOG_TAG, "Setting proxy with >= 4.1 API failed with error: " + ex.getMessage()); + return false; + } + + Log.d(LOG_TAG, "Setting proxy with >= 4.1 API successful!"); + return true; + } + + /** + * Set Proxy for Android 4.4 and above. + */ + @SuppressWarnings("all") + private static boolean setKitKatWebViewProxy(Context appContext, String host, int port) { + System.setProperty("http.proxyHost", host); + System.setProperty("http.proxyPort", port + ""); + System.setProperty("https.proxyHost", host); + System.setProperty("https.proxyPort", port + ""); + try { + Class applictionCls = Class.forName("android.app.Application"); + Field loadedApkField = applictionCls.getDeclaredField("mLoadedApk"); + loadedApkField.setAccessible(true); + Object loadedApk = loadedApkField.get(appContext); + Class loadedApkCls = Class.forName("android.app.LoadedApk"); + Field receiversField = loadedApkCls.getDeclaredField("mReceivers"); + receiversField.setAccessible(true); + ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); + for (Object receiverMap : receivers.values()) { + for (Object rec : ((ArrayMap) receiverMap).keySet()) { + Class clazz = rec.getClass(); + if (clazz.getName().contains("ProxyChangeListener")) { + Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class); + Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); + + /*********** optional, may be need in future *************/ + final String CLASS_NAME = "android.net.ProxyProperties"; + Class cls = Class.forName(CLASS_NAME); + Constructor constructor = cls.getConstructor(String.class, Integer.TYPE, String.class); + constructor.setAccessible(true); + Object proxyProperties = constructor.newInstance(host, port, null); + intent.putExtra("proxy", (Parcelable) proxyProperties); + /*********** optional, may be need in future *************/ + + onReceiveMethod.invoke(rec, appContext, intent); + } + } + } + } catch (ClassNotFoundException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (NoSuchFieldException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (IllegalAccessException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (NoSuchMethodException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (InvocationTargetException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } catch (InstantiationException e) { + Log.e(LOG_TAG, "Setting proxy with >= 4.4 API failed with error: " + e.getMessage()); + return false; + } + + Log.d(LOG_TAG, "Setting proxy with >= 4.4 API successful!"); + return true; + } + + private static Object getFieldValueSafely(Field field, Object classInstance) throws IllegalArgumentException, + IllegalAccessException { + boolean oldAccessibleValue = field.isAccessible(); + field.setAccessible(true); + Object result = field.get(classInstance); + field.setAccessible(oldAccessibleValue); + return result; + } + +} diff --git a/Tinfoil-for-Facebook/src/main/res/values/strings.xml b/Tinfoil-for-Facebook/src/main/res/values/strings.xml index 2b98e24..0aa9d30 100644 --- a/Tinfoil-for-Facebook/src/main/res/values/strings.xml +++ b/Tinfoil-for-Facebook/src/main/res/values/strings.xml @@ -26,6 +26,8 @@ OK Loading video… Choose file + Yes + No Share current page @@ -47,6 +49,7 @@ Facebook access General + Proxy Allow Check-ins Allows the mobile app to use coarse location in order to check-in to places Open links inside app @@ -55,6 +58,10 @@ Automatically hide the ActionBar while scrolling down or not using. Scroll back up to make it appear again. Mobile/Desktop site Force the use of the mobile or desktop site + Enable proxy + Configure proxy for network requests. + Proxy host + Proxy port Auto @@ -71,6 +78,11 @@ Written by Daniel Velazco. Click here for more information. + + market://search?q=pname:org.torproject.android + Start Orbot? + Orbot doesn\'t appear to be running. Would you like to start it up and connect to Tor? + \nFacebook is trying to access your location.\n \nMost likely you are trying to \"Check-in\" to a specific place. While Tinfoil has optional permissions used for this, it is disabled by default.\n diff --git a/Tinfoil-for-Facebook/src/main/res/xml/main_preferences.xml b/Tinfoil-for-Facebook/src/main/res/xml/main_preferences.xml index c40891a..f3b329b 100644 --- a/Tinfoil-for-Facebook/src/main/res/xml/main_preferences.xml +++ b/Tinfoil-for-Facebook/src/main/res/xml/main_preferences.xml @@ -37,6 +37,29 @@ android:summary="@string/prefs_mobile_site_sry" android:title="@string/prefs_mobile_site" /> + + + + + + + + +