1
0
Fork 0
mirror of https://github.com/gsantner/dandelion synced 2025-09-13 20:29:42 +02:00
This commit is contained in:
vanitasvitae 2016-06-06 12:35:12 +00:00
commit 7006aabf78
11 changed files with 309 additions and 29 deletions

View file

@ -37,6 +37,7 @@ dependencies {
// More libraries // More libraries
compile 'com.getbase:floatingactionbutton:1.9.1' compile 'com.getbase:floatingactionbutton:1.9.1'
compile 'com.jakewharton:butterknife:8.0.1' compile 'com.jakewharton:butterknife:8.0.1'
compile 'info.guardianproject.netcipher:netcipher:1.2.1'
apt 'com.jakewharton:butterknife-compiler:8.0.1' apt 'com.jakewharton:butterknife-compiler:8.0.1'
} }

View file

@ -70,6 +70,12 @@
android:exported="false" > android:exported="false" >
</service> </service>
<receiver android:name=".util.OrbotStatusReceiver">
<intent-filter>
<action android:name="org.torproject.android.intent.action.STATUS"/>
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View file

@ -82,6 +82,7 @@ import com.github.dfa.diaspora_android.listener.WebUserProfileChangedListener;
import com.github.dfa.diaspora_android.ui.ContextMenuWebView; import com.github.dfa.diaspora_android.ui.ContextMenuWebView;
import com.github.dfa.diaspora_android.ui.CustomWebViewClient; import com.github.dfa.diaspora_android.ui.CustomWebViewClient;
import com.github.dfa.diaspora_android.util.Helpers; import com.github.dfa.diaspora_android.util.Helpers;
import com.github.dfa.diaspora_android.util.OrbotStatusReceiver;
import org.json.JSONException; import org.json.JSONException;
@ -97,6 +98,7 @@ import java.util.Locale;
import butterknife.BindView; import butterknife.BindView;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.OnClick; import butterknife.OnClick;
import info.guardianproject.netcipher.proxy.OrbotHelper;
public class MainActivity extends AppCompatActivity public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener { implements NavigationView.OnNavigationItemSelectedListener, WebUserProfileChangedListener {
@ -176,6 +178,19 @@ public class MainActivity extends AppCompatActivity
podUserProfile.setCallbackHandler(uiHandler); podUserProfile.setCallbackHandler(uiHandler);
podUserProfile.setListener(this); podUserProfile.setListener(this);
//Orbot integration
OrbotStatusReceiver.setMainActivity(this);
OrbotHelper.requestStartTor(getApplicationContext());
if(appSettings.isProxyOrbot()) {
if(!OrbotHelper.isOrbotInstalled(getApplicationContext())) {
appSettings.setProxyOrbot(false);
promptInstallOrbot();
} else {
//precautionary set Proxy
OrbotStatusReceiver.setProxy(getApplicationContext(), OrbotStatusReceiver.DEFAULT_HOST, OrbotStatusReceiver.DEFAULT_PORT);
}
}
this.registerForContextMenu(webView); this.registerForContextMenu(webView);
webView.setParentActivity(this); webView.setParentActivity(this);
webView.setOverScrollMode(WebView.OVER_SCROLL_ALWAYS); webView.setOverScrollMode(WebView.OVER_SCROLL_ALWAYS);
@ -308,9 +323,9 @@ public class MainActivity extends AppCompatActivity
} }
}); });
OrbotHelper.requestStartTor(getApplicationContext());
if (savedInstanceState == null) { if (savedInstanceState == null) {
if (Helpers.isOnline(MainActivity.this)) { if (Helpers.isOnline(this)) {
webView.loadData("", "text/html", null); webView.loadData("", "text/html", null);
webView.loadUrl("https://" + podDomain); webView.loadUrl("https://" + podDomain);
} else { } else {
@ -929,7 +944,8 @@ public class MainActivity extends AppCompatActivity
case R.id.nav_settings_app: { case R.id.nav_settings_app: {
final CharSequence[] options = {getString(R.string.settings_font), getString(R.string.settings_view), appSettings.isLoadImages() ? final CharSequence[] options = {getString(R.string.settings_font), getString(R.string.settings_view), appSettings.isLoadImages() ?
getString(R.string.settings_images_switch_off) : getString(R.string.settings_images_switch_on), getString(R.string.jb_pod)}; getString(R.string.settings_images_switch_off) : getString(R.string.settings_images_switch_on), getString(R.string.jb_pod),
appSettings.isProxyOrbot() ? getString(R.string.orbot_proxy_enabled) : getString(R.string.orbot_proxy_disabled)};
if (Helpers.isOnline(MainActivity.this)) { if (Helpers.isOnline(MainActivity.this)) {
new AlertDialog.Builder(MainActivity.this) new AlertDialog.Builder(MainActivity.this)
@ -962,6 +978,17 @@ public class MainActivity extends AppCompatActivity
}) })
.show(); .show();
break; break;
case 4:
boolean before = appSettings.isProxyOrbot();
appSettings.setProxyOrbot(!before);
if(before) {
OrbotStatusReceiver.resetProxy(getApplicationContext());
} else {
OrbotHelper.requestStartTor(getApplicationContext());
webView.reload();
}
break;
} }
} }
}).show(); }).show();
@ -1032,7 +1059,7 @@ public class MainActivity extends AppCompatActivity
} }
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.main__layout); DrawerLayout drawer = (DrawerLayout) findViewById(R.id.main__layout);
drawer.closeDrawer(GravityCompat.START); if(drawer != null) drawer.closeDrawer(GravityCompat.START);
return true; return true;
} }
@ -1052,4 +1079,34 @@ public class MainActivity extends AppCompatActivity
grantResults); grantResults);
} }
} }
/**
* Ask the user whether to install Orbot or not. Check if installing from
* F-Droid or Google Play, otherwise take the user to the Orbot download
* page on f-droid.org.
*/
void promptInstallOrbot() {
final Intent intent = OrbotHelper.getOrbotInstallIntent(MainActivity.this);
String message = this.getString(R.string.you_must_have_orbot) + " "
+ (intent.getPackage() == null ? getString(R.string.download_orbot_from_fdroid)
: getString(R.string.get_orbot_from_fdroid));
new AlertDialog.Builder(this).setTitle(R.string.install_orbot_).setMessage(message).setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
MainActivity.this.startActivity(intent);
}
}).setNegativeButton(android.R.string.no, null).show();
}
public void requestOrbotStart(boolean backgroundStartsDisabled) {
AlertDialog.Builder startDialog = new AlertDialog.Builder(this).setTitle(R.string.start_orbot_)
.setMessage((backgroundStartsDisabled ? R.string.orbot_starts_disabled_message
: R.string.orbot_doesn_t_appear_to_be_running_would_you_like_to_start_it_up_and_connect_to_tor_));
startDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
startActivityForResult(OrbotHelper.getShowOrbotStartIntent(), 1);
}
}).setNegativeButton(android.R.string.no, null).show();
}
} }

View file

@ -74,6 +74,7 @@ public class AppSettings {
private static final String PODUSERPROFILE_ID = "podUserProfile_guid"; private static final String PODUSERPROFILE_ID = "podUserProfile_guid";
private static final String PODDOMAIN = "podDomain"; private static final String PODDOMAIN = "podDomain";
private static final String PODUSERPROFILE_ASPECTS = "podUserProfile_aspects"; private static final String PODUSERPROFILE_ASPECTS = "podUserProfile_aspects";
private static final String IS_PROXY_ORBOT = "proxyViaOrbot";
} }
@ -142,6 +143,13 @@ public class AppSettings {
setStringArray(prefApp, PREF.PREVIOUS_PODLIST, pods); setStringArray(prefApp, PREF.PREVIOUS_PODLIST, pods);
} }
public boolean isProxyOrbot() {
return prefApp.getBoolean(PREF.IS_PROXY_ORBOT, false);
}
public void setProxyOrbot(boolean active) {
prefApp.edit().putBoolean(PREF.IS_PROXY_ORBOT, active).commit();
}
public void setPodAspects(PodAspect[] aspects) { public void setPodAspects(PodAspect[] aspects) {
setStringArray(prefPod, PREF.PODUSERPROFILE_ASPECTS, aspects); setStringArray(prefPod, PREF.PODUSERPROFILE_ASPECTS, aspects);
} }

View file

@ -28,12 +28,6 @@ import android.util.Log;
import com.github.dfa.diaspora_android.App; import com.github.dfa.diaspora_android.App;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
@ -44,6 +38,10 @@ import java.io.InputStreamReader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.net.ssl.HttpsURLConnection;
import info.guardianproject.netcipher.NetCipher;
public class GetPodsService extends Service { public class GetPodsService extends Service {
public static final String MESSAGE_PODS_RECEIVED = "com.github.dfa.diaspora.podsreceived"; public static final String MESSAGE_PODS_RECEIVED = "com.github.dfa.diaspora.podsreceived";
private static final String TAG = App.TAG; private static final String TAG = App.TAG;
@ -73,24 +71,28 @@ public class GetPodsService extends Service {
// TODO: Update deprecated code // TODO: Update deprecated code
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
HttpClient client = new DefaultHttpClient(); //HttpClient client = new DefaultHttpClient();
List<String> list = null; List<String> list = null;
HttpsURLConnection connection;
InputStream inStream;
try { try {
HttpGet httpGet = new HttpGet("http://podupti.me/api.php?key=4r45tg&format=json"); connection = NetCipher.getHttpsURLConnection("https://podupti.me/api.php?key=4r45tg&format=json");
HttpResponse response = client.execute(httpGet); int statusCode = connection.getResponseCode();
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) { if (statusCode == 200) {
HttpEntity entity = response.getEntity(); inStream = connection.getInputStream();
InputStream content = entity.getContent();
BufferedReader reader = new BufferedReader( BufferedReader reader = new BufferedReader(
new InputStreamReader(content)); new InputStreamReader(inStream));
String line; String line;
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
builder.append(line); builder.append(line);
} }
try {
inStream.close();
} catch (IOException e) {}
connection.disconnect();
} else { } else {
//TODO Notify User about failure
Log.e(TAG, "Failed to download list of pods"); Log.e(TAG, "Failed to download list of pods");
} }
} catch (IOException e) { } catch (IOException e) {

View file

@ -13,6 +13,10 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import javax.net.ssl.HttpsURLConnection;
import info.guardianproject.netcipher.NetCipher;
/** /**
* Created by Gregor Santner (gsantner) on 24.03.16. * Created by Gregor Santner (gsantner) on 24.03.16.
*/ */
@ -35,9 +39,12 @@ public class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
String url = urls[0]; String url = urls[0];
Bitmap bitmap = null; Bitmap bitmap = null;
FileOutputStream out = null; FileOutputStream out = null;
InputStream inStream;
HttpsURLConnection connection;
try { try {
InputStream in = new java.net.URL(url).openStream(); connection = NetCipher.getHttpsURLConnection(url);
bitmap = BitmapFactory.decodeStream(in); inStream = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(inStream);
// Save to file if not null // Save to file if not null
if (savePath != null) { if (savePath != null) {
@ -45,6 +52,12 @@ public class ImageDownloadTask extends AsyncTask<String, Void, Bitmap> {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
} }
try {
inStream.close();
} catch (IOException e) {}
connection.disconnect();
} catch (Exception e) { } catch (Exception e) {
Log.e(App.TAG, e.getMessage()); Log.e(App.TAG, e.getMessage());
} finally { } finally {

View file

@ -10,10 +10,14 @@ import com.github.dfa.diaspora_android.data.PodUserProfile;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import info.guardianproject.netcipher.NetCipher;
/** /**
* Created by Gregor Santner (gsantner) on 30.03.16. * Created by Gregor Santner (gsantner) on 30.03.16.
*/ */
@ -37,18 +41,21 @@ public class ProfileFetchTask extends AsyncTask<Void, Void, Void> {
String cookies = cookieManager.getCookie("https://" + app.getSettings().getPodDomain()); String cookies = cookieManager.getCookie("https://" + app.getSettings().getPodDomain());
Log.d(App.TAG, cookies); Log.d(App.TAG, cookies);
HttpsURLConnection connection;
InputStream inStream;
try { try {
URL url = new URL("https://" + app.getSettings().getPodDomain() + "/stream"); URL url = new URL("https://" + app.getSettings().getPodDomain() + "/stream");
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); connection = NetCipher.getHttpsURLConnection(url);
conn.setReadTimeout(10000); connection.setReadTimeout(10000);
conn.setConnectTimeout(15000); connection.setConnectTimeout(15000);
conn.setRequestMethod("GET"); connection.setRequestMethod("GET");
if (cookies != null) { if (cookies != null) {
conn.setRequestProperty("Cookie", cookies); connection.setRequestProperty("Cookie", cookies);
} }
conn.connect(); connection.connect();
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); inStream = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inStream));
String line; String line;
final String TARGET_TAG = "window.gon={};gon.user="; final String TARGET_TAG = "window.gon={};gon.user=";
while ((line = br.readLine()) != null && !line.startsWith("<body")) { while ((line = br.readLine()) != null && !line.startsWith("<body")) {
@ -57,6 +64,14 @@ public class ProfileFetchTask extends AsyncTask<Void, Void, Void> {
break; break;
} }
} }
try{
br.close();
inStream.close();
} catch (IOException e){}
connection.disconnect();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }

View file

@ -20,9 +20,13 @@ import android.widget.Toast;
import com.github.dfa.diaspora_android.R; import com.github.dfa.diaspora_android.R;
import com.github.dfa.diaspora_android.activity.MainActivity; import com.github.dfa.diaspora_android.activity.MainActivity;
import com.github.dfa.diaspora_android.util.OrbotStatusReceiver;
import java.io.File; import java.io.File;
import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper;
/** /**
* Subclass of WebView which adds a context menu for long clicks on images or links to share, save * Subclass of WebView which adds a context menu for long clicks on images or links to share, save
* or open with another browser * or open with another browser
@ -149,4 +153,17 @@ public class ContextMenuWebView extends NestedWebView {
public void setParentActivity(Activity activity) { public void setParentActivity(Activity activity) {
this.parentActivity = activity; this.parentActivity = activity;
} }
@Override
public void reload() {
OrbotHelper.requestStartTor(context.getApplicationContext());
super.reload();
}
@Override
public void loadUrl(String url) {
if(!OrbotStatusReceiver.isProxySet())
OrbotHelper.requestStartTor(context.getApplicationContext());
super.loadUrl(url);
}
} }

View file

@ -0,0 +1,141 @@
/*
This file is part of the Diaspora for Android.
Diaspora for Android is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Diaspora for Android is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with the Diaspora for Android.
If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.dfa.diaspora_android.util;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import com.github.dfa.diaspora_android.App;
import com.github.dfa.diaspora_android.activity.MainActivity;
import com.github.dfa.diaspora_android.data.AppSettings;
import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper;
import info.guardianproject.netcipher.web.WebkitProxy;
/**
* BroadcastReceiver that handles Orbot status intents and sets the proxy.
* Created by vanitas on 06.06.16.
*/
public class OrbotStatusReceiver extends BroadcastReceiver {
public static final String EXTRA_HTTP_HOST = "org.torproject.android.intent.extra.HTTP_PROXY_HOST";
public static final String EXTRA_HTTP_PORT = "org.torproject.android.intent.extra.HTTP_PROXY_PORT";
public static final String DEFAULT_HOST = "127.0.0.1";
public static final int DEFAULT_PORT = 8118;
private static String host = "";
private static int port = 0;
private Intent lastStatus;
private Context lastContext;
private static MainActivity mainActivity;
private AppSettings appSettings;
private static boolean proxySet = false;
public OrbotStatusReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
if(OrbotHelper.ACTION_STATUS.equals(intent.getAction())) {
lastStatus = intent;
lastContext = context;
if(appSettings == null) appSettings = new AppSettings(context.getApplicationContext());
String orbotStatus = intent.getExtras().getString(OrbotHelper.EXTRA_STATUS);
if(appSettings.isProxyOrbot()) {
if (orbotStatus.equals(OrbotHelper.STATUS_ON)) {
setProxy(lastContext, lastStatus);
} else if(orbotStatus.equals(OrbotHelper.STATUS_OFF)) {
Log.d(App.TAG, "Warning: Orbot reports status off.");
OrbotHelper.requestStartTor(context.getApplicationContext());
} else if(orbotStatus.equals(OrbotHelper.STATUS_STARTS_DISABLED)) {
Log.d(App.TAG, "Warning: Orbot has background starts disabled.");
if(mainActivity != null) mainActivity.requestOrbotStart(true);
}
}
} else {
Log.e(App.TAG, "Warning: Intents action "+intent.getAction()+ " does not equal "+OrbotHelper.ACTION_STATUS);
}
}
public static void setProxy(Context context, Intent intent) {
if(intent != null) {
String status = intent.getStringExtra(OrbotHelper.EXTRA_STATUS);
if(status.equals(OrbotHelper.STATUS_ON)) {
String nHost = intent.getExtras().getString(EXTRA_HTTP_HOST, null);
int nPort = intent.getIntExtra(EXTRA_HTTP_PORT, -1);
//Got no values from intent
if((nHost == null || nPort == -1)) {
if(host.equals("") || port == 0) {
setProxy(context, DEFAULT_HOST, DEFAULT_PORT);
}
} else {
setProxy(context, nHost, nPort);
}
}
} else {
Log.e(App.TAG, "OrbotStatusReceiver: lastStatus intent is null. Cannot set Proxy.");
}
}
public static void setProxy(Context context, String host, int port) {
try {
OrbotStatusReceiver.host = host;
OrbotStatusReceiver.port = port;
NetCipher.setProxy(host, port);
WebkitProxy.setProxy(MainActivity.class.getName(), context.getApplicationContext(), null, host, port);
Log.d(App.TAG, "Proxy successfully set.");
proxySet = true;
} catch(Exception e) {
Log.e(App.TAG, "setProxy failed: ");
e.printStackTrace();
}
}
public static void resetProxy(Context context) {
try {
OrbotStatusReceiver.host = "";
OrbotStatusReceiver.port = 0;
NetCipher.clearProxy();
WebkitProxy.resetProxy(MainActivity.class.getName(), context.getApplicationContext());
} catch (Exception e) {
//Fails in any case on android 6. Ignore it and restart application.
}
proxySet = false;
//Restart application
Intent restartActivity = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 12374, restartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, pendingIntent);
System.exit(0);
}
public static boolean isProxySet() {
return proxySet;
}
public static void setMainActivity(MainActivity main) {
mainActivity = main;
}
}

View file

@ -166,5 +166,14 @@ along with this program. If not, see http://www.gnu.org/licenses.&lt;br> &lt;br
<string name="toast_saved_image_to_location">Speichere Bild als</string> <string name="toast_saved_image_to_location">Speichere Bild als</string>
<string name="toast_link_address_copied">Linkadresse kopiert …</string> <string name="toast_link_address_copied">Linkadresse kopiert …</string>
<string name="share_dotdodot">Teilen…</string> <string name="share_dotdodot">Teilen…</string>
<string name="download_orbot_from_fdroid">Möchten Sie Orbot nun von f-droid.org herunterladen?</string>
<string name="get_orbot_from_fdroid">Möchten Sie Orbot nun über F-Droid installieren?</string>
<string name="install_orbot_">Orbot installieren?</string>
<string name="orbot_doesn_t_appear_to_be_running_would_you_like_to_start_it_up_and_connect_to_tor_">Orbot scheint nicht aktiv zu sein. Möchten Sie es nun starten um sich mit dem Tor-Netzwerk zu verbinden?</string>
<string name="orbot_proxy_disabled">Orbot-Proxy: Deaktiviert</string>
<string name="orbot_proxy_enabled">Orbot-Proxy: Aktiviert</string>
<string name="orbot_starts_disabled_message">Orbot scheint Hintergrund-Starts deaktiviert zu haben. Möchten Sie Orbot jetzt öffnen?</string>
<string name="start_orbot_">Orbot starten?</string>
<string name="you_must_have_orbot">Um das Tor-Netzwerk als Proxy nutzen zu können, müssen Sie Orbot installieren.</string>
</resources> </resources>

View file

@ -211,4 +211,15 @@
<string name="prefix_https" translatable="false">https://</string> <string name="prefix_https" translatable="false">https://</string>
<string name="share_dotdodot">Share…</string> <string name="share_dotdodot">Share…</string>
<string name="app_hashtag" translatable="false">#DiasporaForAndroid</string> <string name="app_hashtag" translatable="false">#DiasporaForAndroid</string>
<string name="install_orbot_">Install Orbot?</string>
<string name="you_must_have_orbot">You must have Orbot installed and activated to proxy traffic through it.</string>
<string name="get_orbot_from_fdroid">Would you like to install it from F-Droid?</string>
<string name="download_orbot_from_fdroid">Would you like to download it from f-droid.org?</string>
<string name="start_orbot_">Start Orbot?</string>
<string name="orbot_doesn_t_appear_to_be_running_would_you_like_to_start_it_up_and_connect_to_tor_">Orbot doesn\'t appear to be running. Would you like to start it up and connect to Tor?</string>
<string name="orbot_starts_disabled_message">Orbot has background starts disabled. Would you like to open Orbot?</string>
<string name="orbot_proxy_enabled">Orbot Proxy: Enabled</string>
<string name="orbot_proxy_disabled">Orbot Proxy: Disabled</string>
</resources> </resources>