Merge branch 'hidden_services' of https://github.com/arrase/orbot into arrase-hidden_services
This commit is contained in:
commit
26b9199378
|
@ -30,5 +30,5 @@ dependencies {
|
||||||
compile project(':orbotservice')
|
compile project(':orbotservice')
|
||||||
compile 'com.android.support:support-v4:23.4.0'
|
compile 'com.android.support:support-v4:23.4.0'
|
||||||
compile 'com.android.support:appcompat-v7:23.4.0'
|
compile 'com.android.support:appcompat-v7:23.4.0'
|
||||||
|
compile 'com.android.support:design:23.4.0'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,137 +1,178 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="org.torproject.android"
|
package="org.torproject.android"
|
||||||
android:versionName="15.2.0-RC-8-multi"
|
android:installLocation="auto"
|
||||||
android:versionCode="15208000"
|
android:versionCode="15208000"
|
||||||
android:installLocation="auto"
|
android:versionName="15.2.0-RC-8-multi">
|
||||||
>
|
|
||||||
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="23"/>
|
<uses-sdk
|
||||||
<!--
|
android:minSdkVersion="16"
|
||||||
|
android:targetSdkVersion="23" />
|
||||||
|
<!--
|
||||||
<permission android:name="org.torproject.android.MANAGE_TOR"
|
<permission android:name="org.torproject.android.MANAGE_TOR"
|
||||||
android:label="@string/permission_manage_tor_label"
|
android:label="@string/permission_manage_tor_label"
|
||||||
android:description="@string/permission_manage_tor_description"
|
android:description="@string/permission_manage_tor_description"
|
||||||
android:protectionLevel="signature"/>
|
android:protectionLevel="signature"/>
|
||||||
|
|
||||||
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
<uses-permission android:name="org.torproject.android.MANAGE_TOR"/>
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
|
||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
||||||
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
|
||||||
|
|
||||||
<application android:name="org.torproject.android.OrbotApp" android:icon="@drawable/ic_launcher"
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
android:label="@string/app_name"
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
android:description="@string/app_description"
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
android:configChanges="locale|orientation|screenSize"
|
<uses-permission android:name="android.permission.ACCESS_SUPERUSER" />
|
||||||
android:theme="@style/DefaultTheme"
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
android:allowBackup="false"
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
android:allowClearUserData="true"
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
android:largeHeap="false"
|
|
||||||
android:hardwareAccelerated="false"
|
|
||||||
|
|
||||||
>
|
<android:uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
|
||||||
<activity android:name=".OrbotMainActivity"
|
<application
|
||||||
|
android:name=".OrbotApp"
|
||||||
|
android:allowBackup="false"
|
||||||
|
android:allowClearUserData="true"
|
||||||
|
android:configChanges="locale|orientation|screenSize"
|
||||||
|
android:description="@string/app_description"
|
||||||
|
android:hardwareAccelerated="false"
|
||||||
|
android:icon="@drawable/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:largeHeap="false"
|
||||||
|
android:theme="@style/DefaultTheme">
|
||||||
|
<activity
|
||||||
|
android:name=".OrbotMainActivity"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:launchMode="singleTop"
|
android:launchMode="singleTop">
|
||||||
>
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
<data android:scheme="bridge" />
|
<data android:scheme="bridge" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
<intent-filter>
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
<action android:name="org.torproject.android.REQUEST_HS_PORT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
|
||||||
<action android:name="org.torproject.android.START_TOR" />
|
<action android:name="org.torproject.android.START_TOR" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<!--
|
<!-- This is for ensuring the background service still runs when/if the app is swiped away -->
|
||||||
This is for ensuring the background service still runs when/if the app is swiped away
|
<activity
|
||||||
-->
|
android:name=".service.util.DummyActivity"
|
||||||
<activity
|
android:allowTaskReparenting="true"
|
||||||
android:name=".service.util.DummyActivity"
|
android:alwaysRetainTaskState="false"
|
||||||
android:theme="@android:style/Theme.Translucent"
|
android:clearTaskOnLaunch="true"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:allowTaskReparenting="true"
|
android:excludeFromRecents="true"
|
||||||
android:noHistory="true"
|
android:finishOnTaskLaunch="true"
|
||||||
android:excludeFromRecents="true"
|
android:noHistory="true"
|
||||||
android:alwaysRetainTaskState="false"
|
android:stateNotNeeded="true"
|
||||||
android:stateNotNeeded="true"
|
android:theme="@android:style/Theme.Translucent" />
|
||||||
android:clearTaskOnLaunch="true"
|
<activity
|
||||||
android:finishOnTaskLaunch="true"
|
android:name=".vpn.VPNEnableActivity"
|
||||||
|
android:exported="false"
|
||||||
/>
|
android:label="@string/app_name" />
|
||||||
|
<activity
|
||||||
<activity
|
android:name=".ui.PromoAppsActivity"
|
||||||
android:name=".vpn.VPNEnableActivity" android:label="@string/app_name" android:exported="false"
|
android:exported="false" />
|
||||||
/>
|
<activity
|
||||||
|
android:name=".settings.SettingsPreferences"
|
||||||
|
android:label="@string/app_name" />
|
||||||
<activity android:name="org.torproject.android.ui.PromoAppsActivity" android:exported="false"/>
|
<activity
|
||||||
|
android:name=".ui.AppManager"
|
||||||
|
android:label="@string/app_name"
|
||||||
<activity android:name=".settings.SettingsPreferences" android:label="@string/app_name"/>
|
android:theme="@style/Theme.AppCompat" />
|
||||||
<activity android:name=".ui.AppManager" android:label="@string/app_name"
|
|
||||||
android:theme="@style/Theme.AppCompat"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".service.TorService"
|
android:name=".service.TorService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE"
|
android:permission="android.permission.BIND_VPN_SERVICE"
|
||||||
android:stopWithTask="false" >
|
android:stopWithTask="false"></service>
|
||||||
|
<service
|
||||||
|
android:name=".service.vpn.TorVpnService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:permission="android.permission.BIND_VPN_SERVICE">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.net.VpnService" />
|
||||||
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
|
||||||
<service
|
|
||||||
android:name=".service.vpn.TorVpnService"
|
|
||||||
android:enabled="true"
|
|
||||||
android:permission="android.permission.BIND_VPN_SERVICE" >
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.net.VpnService"/>
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".service.StartTorReceiver"
|
android:name=".service.StartTorReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.torproject.android.intent.action.START" />
|
<action android:name="org.torproject.android.intent.action.START" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
<receiver
|
||||||
|
android:name=".OnBootReceiver"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_MOUNTED" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.HOME" />
|
||||||
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<receiver android:name=".OnBootReceiver"
|
<activity
|
||||||
android:enabled="true" android:exported="true"
|
android:name=".ui.hiddenservices.HiddenServicesActivity"
|
||||||
|
android:label="@string/title_activity_hidden_services"
|
||||||
>
|
android:theme="@style/DefaultTheme">
|
||||||
<intent-filter>
|
<meta-data
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
<category android:name="android.intent.category.HOME" />
|
android:value=".OrbotMainActivity" />
|
||||||
</intent-filter>
|
</activity>
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
|
|
||||||
<category android:name="android.intent.category.HOME" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
</application>
|
<provider
|
||||||
</manifest>
|
android:name=".ui.hiddenservices.providers.HSContentProvider"
|
||||||
|
android:authorities="org.torproject.android.ui.hiddenservices.providers"
|
||||||
|
android:exported="false" />
|
||||||
|
<provider
|
||||||
|
android:name="android.support.v4.content.FileProvider"
|
||||||
|
android:authorities="org.torproject.android.ui.hiddenservices.storage"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/hidden_services_paths" />
|
||||||
|
</provider>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".ui.hiddenservices.ClientCookiesActivity"
|
||||||
|
android:label="@string/client_cookies"
|
||||||
|
android:theme="@style/DefaultTheme">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
|
android:value=".OrbotMainActivity" />
|
||||||
|
</activity>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".ui.hiddenservices.providers.CookieContentProvider"
|
||||||
|
android:authorities="org.torproject.android.ui.hiddenservices.providers.cookie"
|
||||||
|
android:exported="false" />
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
|
@ -4,6 +4,7 @@
|
||||||
package org.torproject.android;
|
package org.torproject.android;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
@ -26,6 +27,11 @@ import org.torproject.android.ui.AppManager;
|
||||||
import org.torproject.android.ui.ImageProgressView;
|
import org.torproject.android.ui.ImageProgressView;
|
||||||
import org.torproject.android.ui.PromoAppsActivity;
|
import org.torproject.android.ui.PromoAppsActivity;
|
||||||
import org.torproject.android.ui.Rotate3dAnimation;
|
import org.torproject.android.ui.Rotate3dAnimation;
|
||||||
|
import org.torproject.android.ui.hiddenservices.ClientCookiesActivity;
|
||||||
|
import org.torproject.android.ui.hiddenservices.HiddenServicesActivity;
|
||||||
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
|
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
import org.torproject.android.vpn.VPNEnableActivity;
|
import org.torproject.android.vpn.VPNEnableActivity;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
@ -34,6 +40,8 @@ import android.app.ActivityManager.RunningServiceInfo;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -43,6 +51,7 @@ import android.content.SharedPreferences.Editor;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -80,6 +89,8 @@ import android.widget.Toast;
|
||||||
import com.google.zxing.integration.android.IntentIntegrator;
|
import com.google.zxing.integration.android.IntentIntegrator;
|
||||||
import com.google.zxing.integration.android.IntentResult;
|
import com.google.zxing.integration.android.IntentResult;
|
||||||
|
|
||||||
|
import static android.support.v4.content.FileProvider.getUriForFile;
|
||||||
|
|
||||||
public class OrbotMainActivity extends AppCompatActivity
|
public class OrbotMainActivity extends AppCompatActivity
|
||||||
implements OrbotConstants, OnLongClickListener, OnTouchListener {
|
implements OrbotConstants, OnLongClickListener, OnTouchListener {
|
||||||
|
|
||||||
|
@ -114,8 +125,6 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
private final static int REQUEST_SETTINGS = 0x9874;
|
private final static int REQUEST_SETTINGS = 0x9874;
|
||||||
private final static int REQUEST_VPN_APPS_SELECT = 8889;
|
private final static int REQUEST_VPN_APPS_SELECT = 8889;
|
||||||
|
|
||||||
private final static boolean mIsLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
|
|
||||||
|
|
||||||
// message types for mStatusUpdateHandler
|
// message types for mStatusUpdateHandler
|
||||||
private final static int STATUS_UPDATE = 1;
|
private final static int STATUS_UPDATE = 1;
|
||||||
private static final int MESSAGE_TRAFFIC_COUNT = 2;
|
private static final int MESSAGE_TRAFFIC_COUNT = 2;
|
||||||
|
@ -140,13 +149,37 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
return super.onCreateView(parent, name, context, attrs);
|
return super.onCreateView(parent, name, context, attrs);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
private void migratePreferences() {
|
||||||
|
String hsPortString = mPrefs.getString("pref_hs_ports", "");
|
||||||
|
if (hsPortString.length() > 0) {
|
||||||
|
StringTokenizer st = new StringTokenizer(hsPortString, ",");
|
||||||
|
ContentResolver cr = getContentResolver();
|
||||||
|
while (st.hasMoreTokens()) {
|
||||||
|
int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put("name", hsPort);
|
||||||
|
fields.put("port", hsPort);
|
||||||
|
fields.put("onion_port", hsPort);
|
||||||
|
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
Editor pEdit = mPrefs.edit();
|
||||||
|
pEdit.remove("pref_hs_ports");
|
||||||
|
pEdit.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the activity is first created.
|
||||||
|
*/
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
mPrefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
||||||
|
|
||||||
|
migratePreferences(); // Migrate old preferences
|
||||||
|
|
||||||
/* Create the widgets before registering for broadcasts to guarantee
|
/* Create the widgets before registering for broadcasts to guarantee
|
||||||
* that the widgets exist when the status updates try to update them */
|
* that the widgets exist when the status updates try to update them */
|
||||||
doLayout();
|
doLayout();
|
||||||
|
@ -478,6 +511,10 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if (item.getItemId() == R.id.menu_hidden_services) {
|
||||||
|
startActivity(new Intent(this, HiddenServicesActivity.class));
|
||||||
|
} else if (item.getItemId() == R.id.menu_client_cookies) {
|
||||||
|
startActivity(new Intent(this, ClientCookiesActivity.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
|
@ -548,192 +585,249 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
Prefs.putUseVpn(enable);
|
Prefs.putUseVpn(enable);
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
if (mIsLollipop) //let the user choose the apps
|
if (PermissionManager.isLollipopOrHigher()) //let the user choose the apps
|
||||||
startActivityForResult(new Intent(OrbotMainActivity.this, AppManager.class), REQUEST_VPN_APPS_SELECT);
|
startActivityForResult(new Intent(OrbotMainActivity.this, AppManager.class), REQUEST_VPN_APPS_SELECT);
|
||||||
else
|
else
|
||||||
startActivity(new Intent(OrbotMainActivity.this, VPNEnableActivity.class));
|
startActivity(new Intent(OrbotMainActivity.this, VPNEnableActivity.class));
|
||||||
} else
|
} else
|
||||||
stopVpnService();
|
stopVpnService();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableHiddenServicePort (int hsPort) throws RemoteException, InterruptedException
|
|
||||||
{
|
|
||||||
|
|
||||||
Editor pEdit = mPrefs.edit();
|
|
||||||
|
|
||||||
String hsPortString = mPrefs.getString("pref_hs_ports", "");
|
|
||||||
String onionHostname = mPrefs.getString("pref_hs_hostname","");
|
|
||||||
|
|
||||||
if (hsPortString.indexOf(hsPort+"")==-1)
|
|
||||||
{
|
|
||||||
if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1)
|
|
||||||
hsPortString += ',' + hsPort;
|
|
||||||
else
|
|
||||||
hsPortString = hsPort + "";
|
|
||||||
|
|
||||||
pEdit.putString("pref_hs_ports", hsPortString);
|
|
||||||
pEdit.putBoolean("pref_hs_enable", true);
|
|
||||||
|
|
||||||
pEdit.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onionHostname == null || onionHostname.length() == 0)
|
private void enableHiddenServicePort(
|
||||||
{
|
String hsName, final int hsPort, int hsRemotePort,
|
||||||
requestTorRereadConfig();
|
final String backupToPackage, final Uri hsKeyPath,
|
||||||
|
final Boolean authCookie
|
||||||
|
) throws RemoteException, InterruptedException {
|
||||||
|
|
||||||
new Thread () {
|
String onionHostname = null;
|
||||||
|
|
||||||
public void run ()
|
if (hsName == null)
|
||||||
{
|
hsName = "hs" + hsPort;
|
||||||
String onionHostname = mPrefs.getString("pref_hs_hostname","");
|
|
||||||
|
|
||||||
while (onionHostname.length() == 0)
|
if (hsRemotePort == -1)
|
||||||
{
|
hsRemotePort = hsPort;
|
||||||
//we need to stop and start Tor
|
|
||||||
try {
|
|
||||||
Thread.sleep(3000); //wait three seconds
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
onionHostname = mPrefs.getString("pref_hs_hostname","");
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent nResult = new Intent();
|
|
||||||
nResult.putExtra("hs_host", onionHostname);
|
|
||||||
setResult(RESULT_OK, nResult);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Intent nResult = new Intent();
|
|
||||||
nResult.putExtra("hs_host", onionHostname);
|
|
||||||
setResult(RESULT_OK, nResult);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private synchronized void handleIntents ()
|
|
||||||
{
|
|
||||||
if (getIntent() == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get intent, action and MIME type
|
|
||||||
Intent intent = getIntent();
|
|
||||||
String action = intent.getAction();
|
|
||||||
Log.d(TAG, "handleIntents " + action);
|
|
||||||
|
|
||||||
//String type = intent.getType();
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put(HSContentProvider.HiddenService.NAME, hsName);
|
||||||
if (action == null)
|
fields.put(HSContentProvider.HiddenService.PORT, hsPort);
|
||||||
return;
|
fields.put(HSContentProvider.HiddenService.ONION_PORT, hsRemotePort);
|
||||||
|
fields.put(HSContentProvider.HiddenService.AUTH_COOKIE, authCookie);
|
||||||
if (action.equals(INTENT_ACTION_REQUEST_HIDDEN_SERVICE))
|
|
||||||
{
|
|
||||||
final int hiddenServicePortRequest = getIntent().getIntExtra("hs_port", -1);
|
|
||||||
|
|
||||||
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
ContentResolver cr = getContentResolver();
|
||||||
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
switch (which){
|
|
||||||
case DialogInterface.BUTTON_POSITIVE:
|
|
||||||
|
|
||||||
try {
|
|
||||||
enableHiddenServicePort (hiddenServicePortRequest);
|
|
||||||
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Cursor row = cr.query(
|
||||||
break;
|
HSContentProvider.CONTENT_URI,
|
||||||
|
HSContentProvider.PROJECTION,
|
||||||
|
HSContentProvider.HiddenService.ONION_PORT + "=" + hsPort,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
case DialogInterface.BUTTON_NEGATIVE:
|
if (row == null || row.getCount() < 1) {
|
||||||
//No button clicked
|
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||||
finish();
|
} else {
|
||||||
break;
|
onionHostname = row.getString(row.getColumnIndex(HSContentProvider.HiddenService.DOMAIN));
|
||||||
}
|
row.close();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
String requestMsg = getString(R.string.hidden_service_request, hiddenServicePortRequest);
|
if (onionHostname == null || onionHostname.length() < 1) {
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
|
|
||||||
.setNegativeButton("Deny", dialogClickListener).show();
|
|
||||||
|
|
||||||
return; //don't null the setIntent() as we need it later
|
|
||||||
}
|
|
||||||
else if (action.equals(INTENT_ACTION_REQUEST_START_TOR))
|
|
||||||
{
|
|
||||||
autoStartFromIntent = true;
|
|
||||||
|
|
||||||
startTor();
|
|
||||||
|
|
||||||
//never allow backgrounds start from this type of intent start
|
if (hsKeyPath != null) {
|
||||||
//app devs who want background starts, can use the service intents
|
BackupUtils hsutils = new BackupUtils(getApplicationContext());
|
||||||
/**
|
hsutils.restoreKeyBackup(hsPort, hsKeyPath);
|
||||||
if (Prefs.allowBackgroundStarts())
|
}
|
||||||
{
|
|
||||||
Intent resultIntent;
|
|
||||||
if (lastStatusIntent == null) {
|
|
||||||
resultIntent = new Intent(intent);
|
|
||||||
} else {
|
|
||||||
resultIntent = lastStatusIntent;
|
|
||||||
}
|
|
||||||
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
|
||||||
setResult(RESULT_OK, resultIntent);
|
|
||||||
finish();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (action.equals(Intent.ACTION_VIEW))
|
|
||||||
{
|
|
||||||
String urlString = intent.getDataString();
|
|
||||||
|
|
||||||
if (urlString != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (urlString.toLowerCase().startsWith("bridge://"))
|
|
||||||
|
|
||||||
{
|
if (torStatus.equals(TorServiceConstants.STATUS_OFF)) {
|
||||||
String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece
|
startTor();
|
||||||
newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here
|
} else {
|
||||||
|
stopTor();
|
||||||
|
Toast.makeText(
|
||||||
|
this, R.string.start_tor_again_for_finish_the_process, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
showAlert(getString(R.string.bridges_updated),getString(R.string.restart_orbot_to_use_this_bridge_) + newBridgeValue,false);
|
new Thread() {
|
||||||
|
|
||||||
setNewBridges(newBridgeValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStatus(null);
|
|
||||||
|
|
||||||
setIntent(null);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNewBridges (String newBridgeValue)
|
|
||||||
{
|
|
||||||
|
|
||||||
Prefs.setBridgesList(newBridgeValue); //set the string to a preference
|
public void run() {
|
||||||
Prefs.putBridgesEnabled(true);
|
String hostname = null;
|
||||||
|
Intent nResult = new Intent();
|
||||||
setResult(RESULT_OK);
|
|
||||||
|
while (hostname == null) {
|
||||||
mBtnBridges.setChecked(true);
|
try {
|
||||||
|
Thread.sleep(3000); //wait three seconds
|
||||||
enableBridges(true);
|
} catch (Exception e) {
|
||||||
}
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor onion = getContentResolver().query(
|
||||||
|
HSContentProvider.CONTENT_URI,
|
||||||
|
HSContentProvider.PROJECTION,
|
||||||
|
HSContentProvider.HiddenService.ONION_PORT + "=" + hsPort,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onion != null && onion.getCount() > 0) {
|
||||||
|
onion.moveToNext();
|
||||||
|
hostname = onion.getString(onion.getColumnIndex(HSContentProvider.HiddenService.DOMAIN));
|
||||||
|
|
||||||
|
if(hostname == null || hostname.length() < 1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nResult.putExtra("hs_host", hostname);
|
||||||
|
|
||||||
|
if (authCookie) {
|
||||||
|
nResult.putExtra(
|
||||||
|
"hs_auth_cookie",
|
||||||
|
onion.getString(onion.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (backupToPackage != null && backupToPackage.length() > 0) {
|
||||||
|
String servicePath = getFilesDir() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR + "/hs" + hsPort;
|
||||||
|
File hidden_service_key = new File(servicePath, "private_key");
|
||||||
|
Context context = getApplicationContext();
|
||||||
|
|
||||||
|
Uri contentUri = getUriForFile(
|
||||||
|
context,
|
||||||
|
"org.torproject.android.ui.hiddenservices.storage",
|
||||||
|
hidden_service_key
|
||||||
|
);
|
||||||
|
|
||||||
|
context.grantUriPermission(backupToPackage, contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
nResult.setData(contentUri);
|
||||||
|
nResult.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
onion.close();
|
||||||
|
setResult(RESULT_OK, nResult);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Intent nResult = new Intent();
|
||||||
|
nResult.putExtra("hs_host", onionHostname);
|
||||||
|
setResult(RESULT_OK, nResult);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void handleIntents() {
|
||||||
|
if (getIntent() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get intent, action and MIME type
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String action = intent.getAction();
|
||||||
|
Log.d(TAG, "handleIntents " + action);
|
||||||
|
|
||||||
|
//String type = intent.getType();
|
||||||
|
|
||||||
|
if (action == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case INTENT_ACTION_REQUEST_HIDDEN_SERVICE:
|
||||||
|
final int hiddenServicePort = intent.getIntExtra("hs_port", -1);
|
||||||
|
final int hiddenServiceRemotePort = intent.getIntExtra("hs_onion_port", -1);
|
||||||
|
final String hiddenServiceName = intent.getStringExtra("hs_name");
|
||||||
|
final String backupToPackage = intent.getStringExtra("hs_backup_to_package");
|
||||||
|
final Boolean authCookie = intent.getBooleanExtra("hs_auth_cookie", false);
|
||||||
|
final Uri mKeyUri = intent.getData();
|
||||||
|
|
||||||
|
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||||
|
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
try {
|
||||||
|
enableHiddenServicePort(
|
||||||
|
hiddenServiceName, hiddenServicePort,
|
||||||
|
hiddenServiceRemotePort, backupToPackage,
|
||||||
|
mKeyUri, authCookie
|
||||||
|
);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String requestMsg = getString(R.string.hidden_service_request, hiddenServicePort);
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
|
builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener)
|
||||||
|
.setNegativeButton("Deny", dialogClickListener).show();
|
||||||
|
|
||||||
|
return; //don't null the setIntent() as we need it later
|
||||||
|
|
||||||
|
case INTENT_ACTION_REQUEST_START_TOR:
|
||||||
|
autoStartFromIntent = true;
|
||||||
|
|
||||||
|
startTor();
|
||||||
|
|
||||||
|
//never allow backgrounds start from this type of intent start
|
||||||
|
//app devs who want background starts, can use the service intents
|
||||||
|
/**
|
||||||
|
if (Prefs.allowBackgroundStarts())
|
||||||
|
{
|
||||||
|
Intent resultIntent;
|
||||||
|
if (lastStatusIntent == null) {
|
||||||
|
resultIntent = new Intent(intent);
|
||||||
|
} else {
|
||||||
|
resultIntent = lastStatusIntent;
|
||||||
|
}
|
||||||
|
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
||||||
|
setResult(RESULT_OK, resultIntent);
|
||||||
|
finish();
|
||||||
|
}*/
|
||||||
|
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_VIEW:
|
||||||
|
String urlString = intent.getDataString();
|
||||||
|
|
||||||
|
if (urlString != null) {
|
||||||
|
|
||||||
|
if (urlString.toLowerCase().startsWith("bridge://"))
|
||||||
|
|
||||||
|
{
|
||||||
|
String newBridgeValue = urlString.substring(9); //remove the bridge protocol piece
|
||||||
|
newBridgeValue = URLDecoder.decode(newBridgeValue); //decode the value here
|
||||||
|
|
||||||
|
showAlert(getString(R.string.bridges_updated), getString(R.string.restart_orbot_to_use_this_bridge_) + newBridgeValue, false);
|
||||||
|
|
||||||
|
setNewBridges(newBridgeValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateStatus(null);
|
||||||
|
|
||||||
|
setIntent(null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setNewBridges(String newBridgeValue) {
|
||||||
|
|
||||||
|
Prefs.setBridgesList(newBridgeValue); //set the string to a preference
|
||||||
|
Prefs.putBridgesEnabled(true);
|
||||||
|
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
|
||||||
|
mBtnBridges.setChecked(true);
|
||||||
|
|
||||||
|
enableBridges(true);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Launch the system activity for Uri viewing with the provided url
|
* Launch the system activity for Uri viewing with the provided url
|
||||||
|
@ -1193,11 +1287,7 @@ public class OrbotMainActivity extends AppCompatActivity
|
||||||
if (autoStartFromIntent)
|
if (autoStartFromIntent)
|
||||||
{
|
{
|
||||||
autoStartFromIntent = false;
|
autoStartFromIntent = false;
|
||||||
Intent resultIntent = lastStatusIntent;
|
Intent resultIntent = lastStatusIntent;
|
||||||
|
|
||||||
if (resultIntent == null)
|
|
||||||
resultIntent = new Intent(TorServiceConstants.ACTION_START);
|
|
||||||
|
|
||||||
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
resultIntent.putExtra(TorServiceConstants.EXTRA_STATUS, torStatus);
|
||||||
setResult(RESULT_OK, resultIntent);
|
setResult(RESULT_OK, resultIntent);
|
||||||
finish();
|
finish();
|
||||||
|
|
|
@ -37,9 +37,6 @@ public class SettingsPreferences
|
||||||
private Preference prefTransProxyFlush = null;
|
private Preference prefTransProxyFlush = null;
|
||||||
|
|
||||||
private Preference prefTransProxyApps = null;
|
private Preference prefTransProxyApps = null;
|
||||||
private CheckBoxPreference prefHiddenServices = null;
|
|
||||||
private EditTextPreference prefHiddenServicesPorts;
|
|
||||||
private EditTextPreference prefHiddenServicesHostname;
|
|
||||||
private CheckBoxPreference prefRequestRoot = null;
|
private CheckBoxPreference prefRequestRoot = null;
|
||||||
private ListPreference prefLocale = null;
|
private ListPreference prefLocale = null;
|
||||||
|
|
||||||
|
@ -104,22 +101,14 @@ public class SettingsPreferences
|
||||||
prefTransProxyApps.setOnPreferenceClickListener(this);
|
prefTransProxyApps.setOnPreferenceClickListener(this);
|
||||||
prefCBTransProxy.setOnPreferenceClickListener(this);
|
prefCBTransProxy.setOnPreferenceClickListener(this);
|
||||||
prefcBTransProxyAll.setOnPreferenceClickListener(this);
|
prefcBTransProxyAll.setOnPreferenceClickListener(this);
|
||||||
prefHiddenServices = (CheckBoxPreference) findPreference("pref_hs_enable");
|
|
||||||
prefHiddenServices.setOnPreferenceClickListener(this);
|
|
||||||
prefHiddenServicesHostname = (EditTextPreference) findPreference("pref_hs_hostname");
|
|
||||||
|
|
||||||
|
|
||||||
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
|
prefCBTransProxy.setEnabled(prefRequestRoot.isChecked());
|
||||||
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked());
|
||||||
prefcbTransTethering.setEnabled(prefCBTransProxy.isChecked());
|
prefcbTransTethering.setEnabled(prefCBTransProxy.isChecked());
|
||||||
|
|
||||||
if (prefCBTransProxy.isChecked())
|
if (prefCBTransProxy.isChecked())
|
||||||
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
|
prefTransProxyApps.setEnabled((!prefcBTransProxyAll.isChecked()));
|
||||||
|
|
||||||
prefHiddenServicesPorts = (EditTextPreference) findPreference("pref_hs_ports");
|
|
||||||
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
|
||||||
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
|
||||||
{
|
{
|
||||||
prefTransProxyApps.setEnabled(true);
|
prefTransProxyApps.setEnabled(true);
|
||||||
|
@ -153,11 +142,6 @@ public class SettingsPreferences
|
||||||
{
|
{
|
||||||
startActivity(new Intent(this, AppManager.class));
|
startActivity(new Intent(this, AppManager.class));
|
||||||
|
|
||||||
}
|
|
||||||
else if (preference == prefHiddenServices)
|
|
||||||
{
|
|
||||||
prefHiddenServicesPorts.setEnabled(prefHiddenServices.isChecked());
|
|
||||||
prefHiddenServicesHostname.setEnabled(prefHiddenServices.isChecked());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.zxing.integration.android.IntentIntegrator;
|
||||||
|
import com.google.zxing.integration.android.IntentResult;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.adapters.ClientCookiesAdapter;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.AddCookieDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.CookieActionsDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.SelectCookieBackupDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
|
public class ClientCookiesActivity extends AppCompatActivity {
|
||||||
|
public final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR = 3;
|
||||||
|
|
||||||
|
private ContentResolver mResolver;
|
||||||
|
private ClientCookiesAdapter mAdapter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.layout_activity_client_cookies);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
mResolver = getContentResolver();
|
||||||
|
|
||||||
|
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
AddCookieDialog dialog = new AddCookieDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "AddCookieDialog");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mAdapter = new ClientCookiesAdapter(
|
||||||
|
this,
|
||||||
|
mResolver.query(CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null)
|
||||||
|
, 0);
|
||||||
|
|
||||||
|
mResolver.registerContentObserver(
|
||||||
|
CookieContentProvider.CONTENT_URI, true, new HSObserver(new Handler())
|
||||||
|
);
|
||||||
|
|
||||||
|
ListView cookies = (ListView) findViewById(R.id.clien_cookies_list);
|
||||||
|
cookies.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
cookies.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
||||||
|
|
||||||
|
Bundle arguments = new Bundle();
|
||||||
|
arguments.putInt(
|
||||||
|
"_id", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie._ID))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putString(
|
||||||
|
"domain", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putString(
|
||||||
|
"auth_cookie_value", item.getString(item.getColumnIndex(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putInt(
|
||||||
|
"enabled", item.getInt(item.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED))
|
||||||
|
);
|
||||||
|
|
||||||
|
CookieActionsDialog dialog = new CookieActionsDialog();
|
||||||
|
dialog.setArguments(arguments);
|
||||||
|
dialog.show(getSupportFragmentManager(), "CookieActionsDialog");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.cookie_menu, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
if (id == R.id.cookie_restore_backup) {
|
||||||
|
if (PermissionManager.isLollipopOrHigher()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(this)) {
|
||||||
|
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
||||||
|
|
||||||
|
} else if (id == R.id.cookie_from_qr) {
|
||||||
|
IntentIntegrator integrator = new IntentIntegrator(ClientCookiesActivity.this);
|
||||||
|
integrator.initiateScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
|
String permissions[], int[] grantResults) {
|
||||||
|
if (grantResults.length < 1
|
||||||
|
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTIONBAR: {
|
||||||
|
SelectCookieBackupDialog dialog = new SelectCookieBackupDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "SelectCookieBackupDialog");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CookieActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG: {
|
||||||
|
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onActivityResult(int request, int response, Intent data) {
|
||||||
|
super.onActivityResult(request, response, data);
|
||||||
|
|
||||||
|
IntentResult scanResult = IntentIntegrator.parseActivityResult(request, response, data);
|
||||||
|
|
||||||
|
if (scanResult == null) return;
|
||||||
|
|
||||||
|
String results = scanResult.getContents();
|
||||||
|
|
||||||
|
if (results == null || results.length() < 1) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject savedValues = new JSONObject(results);
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
CookieContentProvider.ClientCookie.DOMAIN,
|
||||||
|
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
||||||
|
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HSObserver extends ContentObserver {
|
||||||
|
HSObserver(Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
mAdapter.changeCursor(mResolver.query(
|
||||||
|
CookieContentProvider.CONTENT_URI, CookieContentProvider.PROJECTION, null, null, null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,214 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.design.widget.FloatingActionButton;
|
||||||
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.ListView;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.adapters.OnionListAdapter;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.HSActionsDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.HSDataDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.dialogs.SelectHSBackupDialog;
|
||||||
|
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
|
||||||
|
public class HiddenServicesActivity extends AppCompatActivity {
|
||||||
|
public final int WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR = 1;
|
||||||
|
private ContentResolver mResolver;
|
||||||
|
private OnionListAdapter mAdapter;
|
||||||
|
private FloatingActionButton fab;
|
||||||
|
|
||||||
|
private String mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.layout_hs_list_view);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
|
mResolver = getContentResolver();
|
||||||
|
|
||||||
|
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||||
|
fab.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
HSDataDialog dialog = new HSDataDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "HSDataDialog");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mAdapter = new OnionListAdapter(
|
||||||
|
this,
|
||||||
|
mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
||||||
|
),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
mResolver.registerContentObserver(
|
||||||
|
HSContentProvider.CONTENT_URI, true, new HSObserver(new Handler())
|
||||||
|
);
|
||||||
|
|
||||||
|
ListView onion_list = (ListView) findViewById(R.id.onion_list);
|
||||||
|
onion_list.setAdapter(mAdapter);
|
||||||
|
|
||||||
|
onion_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
Cursor item = (Cursor) parent.getItemAtPosition(position);
|
||||||
|
|
||||||
|
Bundle arguments = new Bundle();
|
||||||
|
arguments.putInt(
|
||||||
|
"_id", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService._ID))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putString(
|
||||||
|
"port", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putString(
|
||||||
|
"onion", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putInt(
|
||||||
|
"auth_cookie", item.getInt(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
||||||
|
);
|
||||||
|
|
||||||
|
arguments.putString(
|
||||||
|
"auth_cookie_value", item.getString(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||||
|
);
|
||||||
|
|
||||||
|
HSActionsDialog dialog = new HSActionsDialog();
|
||||||
|
dialog.setArguments(arguments);
|
||||||
|
dialog.show(getSupportFragmentManager(), "HSActionsDialog");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.hs_menu, menu);
|
||||||
|
|
||||||
|
MenuItem item = menu.findItem(R.id.hs_type);
|
||||||
|
Spinner spinner = (Spinner) MenuItemCompat.getActionView(item);
|
||||||
|
|
||||||
|
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
|
||||||
|
this, R.array.array_hs_types, android.R.layout.simple_spinner_item);
|
||||||
|
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
|
||||||
|
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> arg0, View v, int pos, long id) {
|
||||||
|
if (pos == 0) {
|
||||||
|
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=1";
|
||||||
|
fab.show();
|
||||||
|
} else {
|
||||||
|
mWhere = HSContentProvider.HiddenService.CREATED_BY_USER + "=0";
|
||||||
|
fab.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
mAdapter.changeCursor(mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> arg0) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
if (id == R.id.menu_restore_backup) {
|
||||||
|
if (PermissionManager.isLollipopOrHigher()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(this)) {
|
||||||
|
PermissionManager.requestExternalWritePermissions(this, WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode,
|
||||||
|
String permissions[], int[] grantResults) {
|
||||||
|
if (grantResults.length < 1
|
||||||
|
|| grantResults[0] != PackageManager.PERMISSION_GRANTED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case WRITE_EXTERNAL_STORAGE_FROM_ACTIONBAR: {
|
||||||
|
SelectHSBackupDialog dialog = new SelectHSBackupDialog();
|
||||||
|
dialog.show(getSupportFragmentManager(), "SelectHSBackupDialog");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HSActionsDialog.WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG: {
|
||||||
|
Toast.makeText(this, R.string.click_again_for_backup, Toast.LENGTH_LONG).show();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HSObserver extends ContentObserver {
|
||||||
|
HSObserver(Handler handler) {
|
||||||
|
super(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange) {
|
||||||
|
mAdapter.changeCursor(mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, mWhere, null, null
|
||||||
|
));
|
||||||
|
|
||||||
|
if (PermissionManager.isLollipopOrHigher()) {
|
||||||
|
Cursor active = mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, HSContentProvider.HiddenService.ENABLED + "=1", null, null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (active == null) return;
|
||||||
|
|
||||||
|
if (active.getCount() > 0) // Call only if there running services
|
||||||
|
PermissionManager.requestBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
||||||
|
else // Drop whe not needed
|
||||||
|
PermissionManager.requestDropBatteryPermmssions(HiddenServicesActivity.this, getApplicationContext());
|
||||||
|
|
||||||
|
active.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.adapters;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BackupAdapter extends ArrayAdapter<File> {
|
||||||
|
private int mResource;
|
||||||
|
|
||||||
|
public BackupAdapter(Context context, int resource) {
|
||||||
|
super(context, resource);
|
||||||
|
mResource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BackupAdapter(Context context, int resource, List<File> zips) {
|
||||||
|
super(context, resource, zips);
|
||||||
|
mResource = resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(int position, View convertView, ViewGroup parent) {
|
||||||
|
|
||||||
|
View v = convertView;
|
||||||
|
|
||||||
|
if (v == null) {
|
||||||
|
LayoutInflater vi;
|
||||||
|
vi = LayoutInflater.from(getContext());
|
||||||
|
v = vi.inflate(mResource, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
File p = getItem(position);
|
||||||
|
|
||||||
|
if (p != null) {
|
||||||
|
TextView name = (TextView) v.findViewById(R.id.backup_name);
|
||||||
|
|
||||||
|
if (name != null)
|
||||||
|
name.setText(p.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.adapters;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
|
public class ClientCookiesAdapter extends CursorAdapter {
|
||||||
|
private LayoutInflater cursorInflater;
|
||||||
|
|
||||||
|
public ClientCookiesAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
|
||||||
|
cursorInflater = (LayoutInflater) context.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
final Context mContext = context;
|
||||||
|
int id = cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie._ID));
|
||||||
|
final String where = CookieContentProvider.ClientCookie._ID + "=" + id;
|
||||||
|
|
||||||
|
TextView domain = (TextView) view.findViewById(R.id.cookie_onion);
|
||||||
|
domain.setText(cursor.getString(cursor.getColumnIndex(CookieContentProvider.ClientCookie.DOMAIN)));
|
||||||
|
|
||||||
|
Switch enabled = (Switch) view.findViewById(R.id.cookie_switch);
|
||||||
|
enabled.setChecked(
|
||||||
|
cursor.getInt(cursor.getColumnIndex(CookieContentProvider.ClientCookie.ENABLED)) == 1
|
||||||
|
);
|
||||||
|
|
||||||
|
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put(CookieContentProvider.ClientCookie.ENABLED, isChecked);
|
||||||
|
resolver.update(
|
||||||
|
CookieContentProvider.CONTENT_URI, fields, where, null
|
||||||
|
);
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return cursorInflater.inflate(R.layout.layout_client_cookie_list_item, parent, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.adapters;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.v4.widget.CursorAdapter;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CompoundButton;
|
||||||
|
import android.widget.Switch;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
|
||||||
|
public class OnionListAdapter extends CursorAdapter {
|
||||||
|
private LayoutInflater cursorInflater;
|
||||||
|
|
||||||
|
public OnionListAdapter(Context context, Cursor c, int flags) {
|
||||||
|
super(context, c, flags);
|
||||||
|
|
||||||
|
cursorInflater = (LayoutInflater) context.getSystemService(
|
||||||
|
Context.LAYOUT_INFLATER_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(View view, Context context, Cursor cursor) {
|
||||||
|
final Context mContext = context;
|
||||||
|
int id = cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService._ID));
|
||||||
|
final String where = HSContentProvider.HiddenService._ID + "=" + id;
|
||||||
|
|
||||||
|
TextView port = (TextView) view.findViewById(R.id.hs_port);
|
||||||
|
port.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.PORT)));
|
||||||
|
TextView name = (TextView) view.findViewById(R.id.hs_name);
|
||||||
|
name.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.NAME)));
|
||||||
|
TextView domain = (TextView) view.findViewById(R.id.hs_onion);
|
||||||
|
domain.setText(cursor.getString(cursor.getColumnIndex(HSContentProvider.HiddenService.DOMAIN)));
|
||||||
|
|
||||||
|
Switch enabled = (Switch) view.findViewById(R.id.hs_switch);
|
||||||
|
enabled.setChecked(
|
||||||
|
cursor.getInt(cursor.getColumnIndex(HSContentProvider.HiddenService.ENABLED)) == 1
|
||||||
|
);
|
||||||
|
|
||||||
|
enabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put(HSContentProvider.HiddenService.ENABLED, isChecked);
|
||||||
|
resolver.update(
|
||||||
|
HSContentProvider.CONTENT_URI, fields, where, null
|
||||||
|
);
|
||||||
|
|
||||||
|
Toast.makeText(
|
||||||
|
mContext, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View newView(Context context, Cursor cursor, ViewGroup parent) {
|
||||||
|
return cursorInflater.inflate(R.layout.layout_hs_list_item, parent, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,336 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.backup;
|
||||||
|
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.MappedByteBuffer;
|
||||||
|
import java.nio.channels.FileChannel;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
public class BackupUtils {
|
||||||
|
private final String configFileName = "config.json";
|
||||||
|
private Context mContext;
|
||||||
|
private ContentResolver mResolver;
|
||||||
|
|
||||||
|
public BackupUtils(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
mResolver = mContext.getContentResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createZipBackup(Integer port) {
|
||||||
|
File mHSBasePath = new File(
|
||||||
|
mContext.getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
|
String configFilePath = mHSBasePath + "/hs" + port + "/" + configFileName;
|
||||||
|
String hostnameFilePath = mHSBasePath + "/hs" + port + "/hostname";
|
||||||
|
String keyFilePath = mHSBasePath + "/hs" + port + "/private_key";
|
||||||
|
|
||||||
|
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
||||||
|
|
||||||
|
if (storage_path == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Cursor portData = mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI,
|
||||||
|
HSContentProvider.PROJECTION,
|
||||||
|
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
JSONObject config = new JSONObject();
|
||||||
|
try {
|
||||||
|
if (portData == null || portData.getCount() != 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
portData.moveToNext();
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.NAME,
|
||||||
|
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.NAME))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.PORT,
|
||||||
|
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.PORT))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.ONION_PORT,
|
||||||
|
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ONION_PORT))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.DOMAIN,
|
||||||
|
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.AUTH_COOKIE,
|
||||||
|
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.AUTH_COOKIE_VALUE,
|
||||||
|
portData.getString(portData.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.CREATED_BY_USER,
|
||||||
|
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.CREATED_BY_USER))
|
||||||
|
);
|
||||||
|
|
||||||
|
config.put(
|
||||||
|
HSContentProvider.HiddenService.ENABLED,
|
||||||
|
portData.getInt(portData.getColumnIndex(HSContentProvider.HiddenService.ENABLED))
|
||||||
|
);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
portData.close();
|
||||||
|
|
||||||
|
try {
|
||||||
|
FileWriter file = new FileWriter(configFilePath);
|
||||||
|
file.write(config.toString());
|
||||||
|
file.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String zip_path = storage_path.getAbsolutePath() + "/hs" + port + ".zip";
|
||||||
|
String files[] = {hostnameFilePath, keyFilePath, configFilePath};
|
||||||
|
|
||||||
|
ZipIt zip = new ZipIt(files, zip_path);
|
||||||
|
|
||||||
|
if (!zip.zip())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return zip_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restoreZipBackup(File backup) {
|
||||||
|
|
||||||
|
File mHSBasePath = new File(
|
||||||
|
mContext.getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
|
int port;
|
||||||
|
Cursor service;
|
||||||
|
String backupName = backup.getName();
|
||||||
|
String hsDir = backupName.substring(0, backupName.lastIndexOf('.'));
|
||||||
|
String configFilePath = mHSBasePath + "/" + hsDir + "/" + configFileName;
|
||||||
|
String jString = null;
|
||||||
|
|
||||||
|
File hsPath = new File(mHSBasePath.getAbsolutePath(), hsDir);
|
||||||
|
if (!hsPath.isDirectory())
|
||||||
|
hsPath.mkdirs();
|
||||||
|
|
||||||
|
ZipIt zip = new ZipIt(null, backup.getAbsolutePath());
|
||||||
|
zip.unzip(hsPath.getAbsolutePath());
|
||||||
|
|
||||||
|
File config = new File(configFilePath);
|
||||||
|
FileInputStream stream;
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = new FileInputStream(config);
|
||||||
|
FileChannel fc = stream.getChannel();
|
||||||
|
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||||
|
jString = Charset.defaultCharset().decode(bb).toString();
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jString == null)
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject savedValues = new JSONObject(jString);
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.NAME,
|
||||||
|
savedValues.getString(HSContentProvider.HiddenService.NAME)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.ONION_PORT,
|
||||||
|
savedValues.getInt(HSContentProvider.HiddenService.ONION_PORT)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.DOMAIN,
|
||||||
|
savedValues.getString(HSContentProvider.HiddenService.DOMAIN)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.AUTH_COOKIE,
|
||||||
|
savedValues.getInt(HSContentProvider.HiddenService.AUTH_COOKIE)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.CREATED_BY_USER,
|
||||||
|
savedValues.getInt(HSContentProvider.HiddenService.CREATED_BY_USER)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
HSContentProvider.HiddenService.ENABLED,
|
||||||
|
savedValues.getInt(HSContentProvider.HiddenService.ENABLED)
|
||||||
|
);
|
||||||
|
|
||||||
|
port = savedValues.getInt(HSContentProvider.HiddenService.PORT);
|
||||||
|
fields.put(HSContentProvider.HiddenService.PORT, port);
|
||||||
|
|
||||||
|
service = mResolver.query(
|
||||||
|
HSContentProvider.CONTENT_URI,
|
||||||
|
HSContentProvider.PROJECTION,
|
||||||
|
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
if (service == null || service.getCount() == 0) {
|
||||||
|
mResolver.insert(HSContentProvider.CONTENT_URI, fields);
|
||||||
|
} else {
|
||||||
|
mResolver.update(
|
||||||
|
HSContentProvider.CONTENT_URI,
|
||||||
|
fields,
|
||||||
|
HSContentProvider.HiddenService.PORT + "=" + port,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
service.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restoreKeyBackup(int hsPort, Uri hsKeyPath) {
|
||||||
|
File mHSBasePath = new File(
|
||||||
|
mContext.getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
|
File serviceDir = new File(mHSBasePath, "hs" + hsPort);
|
||||||
|
|
||||||
|
if (!serviceDir.isDirectory())
|
||||||
|
serviceDir.mkdirs();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ParcelFileDescriptor mInputPFD = mContext.getContentResolver().openFileDescriptor(hsKeyPath, "r");
|
||||||
|
InputStream fileStream = new FileInputStream(mInputPFD.getFileDescriptor());
|
||||||
|
OutputStream file = new FileOutputStream(serviceDir.getAbsolutePath() + "/private_key");
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int length;
|
||||||
|
while ((length = fileStream.read(buffer)) > 0) {
|
||||||
|
file.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
} catch (IOException | NullPointerException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restoreCookieBackup(File p) {
|
||||||
|
File config = new File(p.getAbsolutePath());
|
||||||
|
FileInputStream stream;
|
||||||
|
String jString = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
stream = new FileInputStream(config);
|
||||||
|
FileChannel fc = stream.getChannel();
|
||||||
|
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
|
||||||
|
jString = Charset.defaultCharset().decode(bb).toString();
|
||||||
|
stream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jString == null)
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject savedValues = new JSONObject(jString);
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
CookieContentProvider.ClientCookie.DOMAIN,
|
||||||
|
savedValues.getString(CookieContentProvider.ClientCookie.DOMAIN)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE,
|
||||||
|
savedValues.getString(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
fields.put(
|
||||||
|
CookieContentProvider.ClientCookie.ENABLED,
|
||||||
|
savedValues.getInt(CookieContentProvider.ClientCookie.ENABLED)
|
||||||
|
);
|
||||||
|
|
||||||
|
mResolver.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String createCookieBackup(String domain, String cookie, Integer enabled) {
|
||||||
|
File storage_path = ExternalStorage.getOrCreateBackupDir();
|
||||||
|
String backupFile = storage_path.getAbsolutePath() + '/' + domain.replace(".onion", ".json");
|
||||||
|
|
||||||
|
JSONObject backup = new JSONObject();
|
||||||
|
try {
|
||||||
|
backup.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
||||||
|
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
||||||
|
backup.put(CookieContentProvider.ClientCookie.ENABLED, enabled);
|
||||||
|
FileWriter file = new FileWriter(backupFile);
|
||||||
|
file.write(backup.toString());
|
||||||
|
file.close();
|
||||||
|
} catch (JSONException | IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return backupFile;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.backup;
|
||||||
|
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class ZipIt {
|
||||||
|
private static final int BUFFER = 2048;
|
||||||
|
|
||||||
|
private String[] _files;
|
||||||
|
private String _zipFile;
|
||||||
|
|
||||||
|
public ZipIt(@Nullable String[] files, @NonNull String zipFile) {
|
||||||
|
_files = files;
|
||||||
|
_zipFile = zipFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean zip() {
|
||||||
|
try {
|
||||||
|
BufferedInputStream origin;
|
||||||
|
FileOutputStream dest = new FileOutputStream(_zipFile);
|
||||||
|
|
||||||
|
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
|
||||||
|
|
||||||
|
byte data[] = new byte[BUFFER];
|
||||||
|
|
||||||
|
for (String _file : _files) {
|
||||||
|
FileInputStream fi = new FileInputStream(_file);
|
||||||
|
origin = new BufferedInputStream(fi, BUFFER);
|
||||||
|
ZipEntry entry = new ZipEntry(_file.substring(_file.lastIndexOf("/") + 1));
|
||||||
|
out.putNextEntry(entry);
|
||||||
|
int count;
|
||||||
|
while ((count = origin.read(data, 0, BUFFER)) != -1) {
|
||||||
|
out.write(data, 0, count);
|
||||||
|
}
|
||||||
|
origin.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
out.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean unzip(String output_path) {
|
||||||
|
InputStream is;
|
||||||
|
ZipInputStream zis;
|
||||||
|
|
||||||
|
try {
|
||||||
|
String filename;
|
||||||
|
is = new FileInputStream(_zipFile);
|
||||||
|
zis = new ZipInputStream(new BufferedInputStream(is));
|
||||||
|
ZipEntry ze;
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int count;
|
||||||
|
|
||||||
|
while ((ze = zis.getNextEntry()) != null) {
|
||||||
|
// zapis do souboru
|
||||||
|
filename = ze.getName();
|
||||||
|
|
||||||
|
// Need to create directories if not exists, or
|
||||||
|
// it will generate an Exception...
|
||||||
|
if (ze.isDirectory()) {
|
||||||
|
File fmd = new File(output_path + "/" + filename);
|
||||||
|
fmd.mkdirs();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileOutputStream fout = new FileOutputStream(output_path + "/" + filename);
|
||||||
|
|
||||||
|
// cteni zipu a zapis
|
||||||
|
while ((count = zis.read(buffer)) != -1) {
|
||||||
|
fout.write(buffer, 0, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
fout.close();
|
||||||
|
zis.closeEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
zis.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.database;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
public class HSDatabase extends SQLiteOpenHelper {
|
||||||
|
|
||||||
|
public static final String HS_DATA_TABLE_NAME = "hs_data";
|
||||||
|
public static final String HS_CLIENT_COOKIE_TABLE_NAME = "hs_client_cookie";
|
||||||
|
private static final int DATABASE_VERSION = 2;
|
||||||
|
private static final String DATABASE_NAME = "hidden_services";
|
||||||
|
private static final String HS_DATA_TABLE_CREATE =
|
||||||
|
"CREATE TABLE " + HS_DATA_TABLE_NAME + " (" +
|
||||||
|
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
"name TEXT, " +
|
||||||
|
"domain TEXT, " +
|
||||||
|
"onion_port INTEGER, " +
|
||||||
|
"auth_cookie INTEGER DEFAULT 0, " +
|
||||||
|
"auth_cookie_value TEXT, " +
|
||||||
|
"created_by_user INTEGER DEFAULT 0, " +
|
||||||
|
"enabled INTEGER DEFAULT 1, " +
|
||||||
|
"port INTEGER);";
|
||||||
|
|
||||||
|
private static final String HS_CLIENT_COOKIE_TABLE_CREATE =
|
||||||
|
"CREATE TABLE " + HS_CLIENT_COOKIE_TABLE_NAME + " (" +
|
||||||
|
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||||
|
"domain TEXT, " +
|
||||||
|
"auth_cookie_value TEXT, " +
|
||||||
|
"enabled INTEGER DEFAULT 1);";
|
||||||
|
|
||||||
|
public HSDatabase(Context context) {
|
||||||
|
super(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(SQLiteDatabase db) {
|
||||||
|
db.execSQL(HS_DATA_TABLE_CREATE);
|
||||||
|
db.execSQL(HS_CLIENT_COOKIE_TABLE_CREATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
|
public class AddCookieDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_add_client_cookie_dialog, null);
|
||||||
|
|
||||||
|
final AlertDialog addCookieDialog = new AlertDialog.Builder(getActivity())
|
||||||
|
.setView(dialog_view)
|
||||||
|
.setTitle(R.string.client_cookies)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
Button save = (Button) dialog_view.findViewById(R.id.cookie_dialog_save);
|
||||||
|
save.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
String onion = ((EditText) dialog_view.findViewById(R.id.cookie_onion)).getText().toString();
|
||||||
|
String cookie = ((EditText) dialog_view.findViewById(R.id.cookie_value)).getText().toString();
|
||||||
|
|
||||||
|
if (checkInput(onion, cookie)) {
|
||||||
|
saveData(onion, cookie);
|
||||||
|
Toast.makeText(
|
||||||
|
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
addCookieDialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = (Button) dialog_view.findViewById(R.id.cookie_dialog_cancel);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
addCookieDialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return addCookieDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkInput(String onion, String cookie) {
|
||||||
|
|
||||||
|
boolean is_set = ((onion != null && onion.length() > 0) && (cookie != null && cookie.length() > 0));
|
||||||
|
if (!is_set) {
|
||||||
|
Toast.makeText(getContext(), R.string.fields_can_t_be_empty, Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!onion.matches("([a-z0-9]{16})\\.onion")) {
|
||||||
|
Toast.makeText(getContext(), R.string.invalid_onion_address, Toast.LENGTH_SHORT).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveData(String domain, String cookie) {
|
||||||
|
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put(CookieContentProvider.ClientCookie.DOMAIN, domain);
|
||||||
|
fields.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, cookie);
|
||||||
|
|
||||||
|
ContentResolver cr = getContext().getContentResolver();
|
||||||
|
|
||||||
|
cr.insert(CookieContentProvider.CONTENT_URI, fields);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
|
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||||
|
|
||||||
|
public class CookieActionsDialog extends DialogFragment {
|
||||||
|
public static final int WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG = 4;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_cookie_actions, null);
|
||||||
|
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
||||||
|
.setView(dialog_view)
|
||||||
|
.setTitle(R.string.client_cookies)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
Button backup = (Button) dialog_view.findViewById(R.id.btn_cookie_backup);
|
||||||
|
backup.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
Context mContext = v.getContext();
|
||||||
|
|
||||||
|
if (PermissionManager.isLollipopOrHigher()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
||||||
|
|
||||||
|
PermissionManager.requestExternalWritePermissions(
|
||||||
|
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_COOKIE_ACTION_DIALOG);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackupUtils backup_utils = new BackupUtils(mContext);
|
||||||
|
String backupPath = backup_utils.createCookieBackup(
|
||||||
|
arguments.getString("domain"),
|
||||||
|
arguments.getString("auth_cookie_value"),
|
||||||
|
arguments.getInt("enabled")
|
||||||
|
);
|
||||||
|
|
||||||
|
if (backupPath == null || backupPath.length() < 1) {
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
actionDialog.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(selectedUri, "resource/folder");
|
||||||
|
|
||||||
|
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button delete = (Button) dialog_view.findViewById(R.id.btn_cookie_delete);
|
||||||
|
delete.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
CookieDeleteDialog dialog = new CookieDeleteDialog();
|
||||||
|
dialog.setArguments(arguments);
|
||||||
|
dialog.show(getFragmentManager(), "CookieDeleteDialog");
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = (Button) dialog_view.findViewById(R.id.btn_cookie_cancel);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return actionDialog;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
|
public class CookieDeleteDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
final Context context = getContext();
|
||||||
|
|
||||||
|
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
// Delete from db
|
||||||
|
context.getContentResolver().delete(
|
||||||
|
CookieContentProvider.CONTENT_URI,
|
||||||
|
CookieContentProvider.ClientCookie._ID + "=" + arguments.getInt("_id"),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DialogInterface.BUTTON_NEGATIVE:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new AlertDialog.Builder(context)
|
||||||
|
.setMessage(R.string.confirm_cookie_deletion)
|
||||||
|
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
||||||
|
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
|
import org.torproject.android.ui.hiddenservices.permissions.PermissionManager;
|
||||||
|
|
||||||
|
public class HSActionsDialog extends DialogFragment {
|
||||||
|
public static final int WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG = 2;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_actions, null);
|
||||||
|
final AlertDialog actionDialog = new AlertDialog.Builder(getActivity())
|
||||||
|
.setView(dialog_view)
|
||||||
|
.setTitle(R.string.hidden_services)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
Button backup = (Button) dialog_view.findViewById(R.id.btn_hs_backup);
|
||||||
|
backup.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
Context mContext = v.getContext();
|
||||||
|
|
||||||
|
if (PermissionManager.isLollipopOrHigher()
|
||||||
|
&& !PermissionManager.hasExternalWritePermission(mContext)) {
|
||||||
|
|
||||||
|
PermissionManager.requestExternalWritePermissions(
|
||||||
|
getActivity(), WRITE_EXTERNAL_STORAGE_FROM_ACTION_DIALOG);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackupUtils hsutils = new BackupUtils(mContext);
|
||||||
|
String backupPath = hsutils.createZipBackup(Integer.parseInt(arguments.getString("port")));
|
||||||
|
|
||||||
|
if (backupPath == null || backupPath.length() < 1) {
|
||||||
|
Toast.makeText(mContext, R.string.error, Toast.LENGTH_LONG).show();
|
||||||
|
actionDialog.dismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toast.makeText(mContext, R.string.backup_saved_at_external_storage, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
|
Uri selectedUri = Uri.parse(backupPath.substring(0, backupPath.lastIndexOf("/")));
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setDataAndType(selectedUri, "resource/folder");
|
||||||
|
|
||||||
|
if (intent.resolveActivityInfo(mContext.getPackageManager(), 0) != null) {
|
||||||
|
startActivity(intent);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(mContext, R.string.filemanager_not_available, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button copy = (Button) dialog_view.findViewById(R.id.btn_hs_clipboard);
|
||||||
|
copy.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
Context mContext = v.getContext();
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText("onion", arguments.getString("onion"));
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button showAuth = (Button) dialog_view.findViewById(R.id.bt_hs_show_auth);
|
||||||
|
showAuth.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
String auth_cookie_value = arguments.getString("auth_cookie_value");
|
||||||
|
|
||||||
|
if (arguments.getInt("auth_cookie") == 1) {
|
||||||
|
if (auth_cookie_value == null || auth_cookie_value.length() < 1) {
|
||||||
|
Toast.makeText(
|
||||||
|
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
} else {
|
||||||
|
HSCookieDialog dialog = new HSCookieDialog();
|
||||||
|
dialog.setArguments(arguments);
|
||||||
|
dialog.show(getFragmentManager(), "HSCookieDialog");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
v.getContext(), R.string.auth_cookie_was_not_configured, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button delete = (Button) dialog_view.findViewById(R.id.btn_hs_delete);
|
||||||
|
delete.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
HSDeleteDialog dialog = new HSDeleteDialog();
|
||||||
|
dialog.setArguments(arguments);
|
||||||
|
dialog.show(getFragmentManager(), "HSDeleteDialog");
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = (Button) dialog_view.findViewById(R.id.btn_hs_cancel);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
actionDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return actionDialog;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.zxing.integration.android.IntentIntegrator;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.CookieContentProvider;
|
||||||
|
|
||||||
|
public class HSCookieDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_cookie, null);
|
||||||
|
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
final String auth_cookie_value = arguments.getString("auth_cookie_value");
|
||||||
|
|
||||||
|
final AlertDialog cookieDialog = new AlertDialog.Builder(getActivity())
|
||||||
|
.setView(dialog_view)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
TextView cookie = (TextView) dialog_view.findViewById(R.id.hs_cookie);
|
||||||
|
cookie.setText(auth_cookie_value);
|
||||||
|
|
||||||
|
Button clipboard = (Button) dialog_view.findViewById(R.id.hs_cookie_to_clipboard);
|
||||||
|
clipboard.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
Context mContext = v.getContext();
|
||||||
|
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||||
|
ClipData clip = ClipData.newPlainText("cookie", auth_cookie_value);
|
||||||
|
clipboard.setPrimaryClip(clip);
|
||||||
|
Toast.makeText(mContext, R.string.done, Toast.LENGTH_LONG).show();
|
||||||
|
cookieDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button shareQR = (Button) dialog_view.findViewById(R.id.hs_cookie_to_qr);
|
||||||
|
shareQR.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
try {
|
||||||
|
JSONObject backup = new JSONObject();
|
||||||
|
backup.put(CookieContentProvider.ClientCookie.DOMAIN, arguments.getString("onion"));
|
||||||
|
backup.put(CookieContentProvider.ClientCookie.AUTH_COOKIE_VALUE, arguments.getString("auth_cookie_value"));
|
||||||
|
|
||||||
|
IntentIntegrator integrator = new IntentIntegrator(getActivity());
|
||||||
|
integrator.shareText(backup.toString());
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
// TODO Auto-generated catch block
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
cookieDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = (Button) dialog_view.findViewById(R.id.hs_cookie_cancel);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
cookieDialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return cookieDialog;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
|
||||||
|
public class HSDataDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
// Get the layout
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_data_dialog, null);
|
||||||
|
|
||||||
|
// Use the Builder class for convenient dialog construction
|
||||||
|
final AlertDialog serviceDataDialog = new AlertDialog.Builder(getActivity())
|
||||||
|
.setView(dialog_view)
|
||||||
|
.setTitle(R.string.hidden_services)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
// Buttons action
|
||||||
|
Button save = (Button) dialog_view.findViewById(R.id.HSDialogSave);
|
||||||
|
save.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
String serverName = ((EditText) dialog_view.findViewById(R.id.hsName)).getText().toString();
|
||||||
|
Integer localPort = Integer.parseInt(
|
||||||
|
((EditText) dialog_view.findViewById(R.id.hsLocalPort)).getText().toString()
|
||||||
|
);
|
||||||
|
Integer onionPort = Integer.parseInt(
|
||||||
|
((EditText) dialog_view.findViewById(R.id.hsOnionPort)).getText().toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
Boolean authCookie = ((CheckBox) dialog_view.findViewById(R.id.hsAuth)).isChecked();
|
||||||
|
|
||||||
|
if (checkInput(serverName, localPort, onionPort)) {
|
||||||
|
saveData(serverName, localPort, onionPort, authCookie);
|
||||||
|
Toast.makeText(
|
||||||
|
v.getContext(), R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG
|
||||||
|
).show();
|
||||||
|
serviceDataDialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Button cancel = (Button) dialog_view.findViewById(R.id.HSDialogCancel);
|
||||||
|
cancel.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
serviceDataDialog.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return serviceDataDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean checkInput(String serverName, Integer local, Integer remote) {
|
||||||
|
boolean is_ok = true;
|
||||||
|
Integer error_msg = 0;
|
||||||
|
|
||||||
|
if ((local < 1 || local > 65535) || (remote < 1 || remote > 65535)) {
|
||||||
|
error_msg = R.string.invalid_port;
|
||||||
|
is_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serverName == null || serverName.length() < 1) {
|
||||||
|
error_msg = R.string.name_can_t_be_empty;
|
||||||
|
is_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_ok) {
|
||||||
|
Toast.makeText(getContext(), error_msg, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveData(String name, Integer local, Integer remote, Boolean authCookie) {
|
||||||
|
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
fields.put(HSContentProvider.HiddenService.NAME, name);
|
||||||
|
fields.put(HSContentProvider.HiddenService.PORT, local);
|
||||||
|
fields.put(HSContentProvider.HiddenService.ONION_PORT, remote);
|
||||||
|
fields.put(HSContentProvider.HiddenService.AUTH_COOKIE, authCookie);
|
||||||
|
fields.put(HSContentProvider.HiddenService.CREATED_BY_USER, 1);
|
||||||
|
|
||||||
|
ContentResolver cr = getContext().getContentResolver();
|
||||||
|
|
||||||
|
cr.insert(HSContentProvider.CONTENT_URI, fields);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.service.TorServiceConstants;
|
||||||
|
import org.torproject.android.ui.hiddenservices.providers.HSContentProvider;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class HSDeleteDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
final Bundle arguments = getArguments();
|
||||||
|
final Context context = getContext();
|
||||||
|
|
||||||
|
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
switch (which) {
|
||||||
|
case DialogInterface.BUTTON_POSITIVE:
|
||||||
|
// Delete from db
|
||||||
|
context.getContentResolver().delete(
|
||||||
|
HSContentProvider.CONTENT_URI,
|
||||||
|
HSContentProvider.HiddenService._ID + "=" + arguments.getInt("_id"),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
// Delete from interal storage
|
||||||
|
String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR;
|
||||||
|
File dir = new File(base, "hs" + arguments.getString("port"));
|
||||||
|
|
||||||
|
if (dir.isDirectory()) {
|
||||||
|
String[] children = dir.list();
|
||||||
|
for (String aChildren : children) {
|
||||||
|
new File(dir, aChildren).delete();
|
||||||
|
}
|
||||||
|
dir.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DialogInterface.BUTTON_NEGATIVE:
|
||||||
|
// Do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return new AlertDialog.Builder(context)
|
||||||
|
.setMessage(R.string.confirm_service_deletion)
|
||||||
|
.setPositiveButton(R.string.btn_okay, dialogClickListener)
|
||||||
|
.setNegativeButton(R.string.btn_cancel, dialogClickListener)
|
||||||
|
.create();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
||||||
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
|
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SelectCookieBackupDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder cookieBackupDialog = new AlertDialog.Builder(getActivity());
|
||||||
|
|
||||||
|
cookieBackupDialog.setTitle(R.string.restore_backup);
|
||||||
|
|
||||||
|
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
||||||
|
File[] files = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
files = backupDir.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.toLowerCase().endsWith(".json");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// Silent block
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files == null || files.length < 1) {
|
||||||
|
cookieBackupDialog.setMessage(R.string.create_a_backup_first);
|
||||||
|
cookieBackupDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return cookieBackupDialog.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
||||||
|
|
||||||
|
cookieBackupDialog.setView(dialog_view);
|
||||||
|
cookieBackupDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
||||||
|
|
||||||
|
List<File> json_backups = new ArrayList<>();
|
||||||
|
Collections.addAll(json_backups, files);
|
||||||
|
|
||||||
|
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, json_backups));
|
||||||
|
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
||||||
|
File p = (File) parent.getItemAtPosition(position);
|
||||||
|
backupUtils.restoreCookieBackup(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return cookieBackupDialog.create();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.dialogs;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ListView;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
import org.torproject.android.ui.hiddenservices.adapters.BackupAdapter;
|
||||||
|
import org.torproject.android.ui.hiddenservices.backup.BackupUtils;
|
||||||
|
import org.torproject.android.ui.hiddenservices.storage.ExternalStorage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SelectHSBackupDialog extends DialogFragment {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder backupsDialog = new AlertDialog.Builder(getActivity());
|
||||||
|
|
||||||
|
backupsDialog.setTitle(R.string.restore_backup);
|
||||||
|
|
||||||
|
File backupDir = ExternalStorage.getOrCreateBackupDir();
|
||||||
|
File[] files = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
files = backupDir.listFiles(new FilenameFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.toLowerCase().endsWith(".zip");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (NullPointerException e) {
|
||||||
|
// Silent block
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files == null || files.length < 1) {
|
||||||
|
backupsDialog.setMessage(R.string.create_a_backup_first);
|
||||||
|
backupsDialog.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return backupsDialog.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
final View dialog_view = getActivity().getLayoutInflater().inflate(R.layout.layout_hs_backups_list, null);
|
||||||
|
|
||||||
|
backupsDialog.setView(dialog_view);
|
||||||
|
backupsDialog.setPositiveButton(R.string.btn_okay, new DialogInterface.OnClickListener() {
|
||||||
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ListView backups = (ListView) dialog_view.findViewById(R.id.listview_hs_backups);
|
||||||
|
|
||||||
|
List<File> zips = new ArrayList<>();
|
||||||
|
Collections.addAll(zips, files);
|
||||||
|
|
||||||
|
backups.setAdapter(new BackupAdapter(getContext(), R.layout.layout_hs_backups_list_item, zips));
|
||||||
|
backups.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
BackupUtils backupUtils = new BackupUtils(view.getContext().getApplicationContext());
|
||||||
|
File p = (File) parent.getItemAtPosition(position);
|
||||||
|
backupUtils.restoreZipBackup(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return backupsDialog.create();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.permissions;
|
||||||
|
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.ActivityCompat;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import org.torproject.android.R;
|
||||||
|
|
||||||
|
public class PermissionManager {
|
||||||
|
public static int VERY_LONG_LENGTH = 6000;
|
||||||
|
|
||||||
|
public static boolean isLollipopOrHigher() {
|
||||||
|
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public static boolean hasExternalWritePermission(Context context) {
|
||||||
|
return (context.checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void requestExternalWritePermissions(FragmentActivity activity, int action) {
|
||||||
|
final int mAction = action;
|
||||||
|
final FragmentActivity mActivity = activity;
|
||||||
|
|
||||||
|
if (ActivityCompat.shouldShowRequestPermissionRationale
|
||||||
|
(mActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||||
|
Snackbar.make(mActivity.findViewById(android.R.id.content),
|
||||||
|
R.string.please_grant_permissions_for_external_storage,
|
||||||
|
Snackbar.LENGTH_INDEFINITE).setAction(R.string.activate,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
ActivityCompat.requestPermissions(mActivity,
|
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||||
|
mAction);
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
} else {
|
||||||
|
ActivityCompat.requestPermissions(mActivity,
|
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||||
|
mAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
public static void requestBatteryPermmssions(FragmentActivity activity, Context context) {
|
||||||
|
final Context mContext = context;
|
||||||
|
final String packageName = mContext.getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
|
||||||
|
|
||||||
|
if (pm.isIgnoringBatteryOptimizations(packageName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Snackbar.make(activity.findViewById(android.R.id.content),
|
||||||
|
R.string.consider_disable_battery_optimizations,
|
||||||
|
VERY_LONG_LENGTH).setAction(R.string.disable,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
|
intent.setData(Uri.parse("package:" + packageName));
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.M)
|
||||||
|
public static void requestDropBatteryPermmssions(FragmentActivity activity, Context context) {
|
||||||
|
final Context mContext = context;
|
||||||
|
|
||||||
|
final String packageName = context.getPackageName();
|
||||||
|
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
|
||||||
|
if (!pm.isIgnoringBatteryOptimizations(packageName))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Snackbar.make(activity.findViewById(android.R.id.content),
|
||||||
|
R.string.consider_enable_battery_optimizations,
|
||||||
|
VERY_LONG_LENGTH).setAction(R.string.enable,
|
||||||
|
new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
|
||||||
|
mContext.startActivity(intent);
|
||||||
|
}
|
||||||
|
}).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.providers;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
||||||
|
|
||||||
|
|
||||||
|
public class CookieContentProvider extends ContentProvider {
|
||||||
|
public static final String[] PROJECTION = new String[]{
|
||||||
|
ClientCookie._ID,
|
||||||
|
ClientCookie.DOMAIN,
|
||||||
|
ClientCookie.AUTH_COOKIE_VALUE,
|
||||||
|
ClientCookie.ENABLED
|
||||||
|
};
|
||||||
|
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers.cookie";
|
||||||
|
public static final Uri CONTENT_URI =
|
||||||
|
Uri.parse("content://" + AUTH + "/cookie");
|
||||||
|
//UriMatcher
|
||||||
|
private static final int COOKIES = 1;
|
||||||
|
private static final int COOKIE_ID = 2;
|
||||||
|
|
||||||
|
private static final UriMatcher uriMatcher;
|
||||||
|
|
||||||
|
static {
|
||||||
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
uriMatcher.addURI(AUTH, "hs", COOKIES);
|
||||||
|
uriMatcher.addURI(AUTH, "hs/#", COOKIE_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HSDatabase mServervices;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
mContext = getContext();
|
||||||
|
mServervices = new HSDatabase(mContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getReadableDatabase();
|
||||||
|
|
||||||
|
return db.query(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, projection, where,
|
||||||
|
selectionArgs, null, null, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
|
switch (match) {
|
||||||
|
case COOKIES:
|
||||||
|
return "vnd.android.cursor.dir/vnd.torproject.cookies";
|
||||||
|
case COOKIE_ID:
|
||||||
|
return "vnd.android.cursor.item/vnd.torproject.cookie";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
|
long regId;
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
regId = db.insert(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, null, values);
|
||||||
|
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return ContentUris.withAppendedId(CONTENT_URI, regId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
Integer rows = db.delete(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, where, selectionArgs);
|
||||||
|
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == COOKIE_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer rows = db.update(HSDatabase.HS_CLIENT_COOKIE_TABLE_NAME, values, where, null);
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ClientCookie implements BaseColumns {
|
||||||
|
public static final String DOMAIN = "domain";
|
||||||
|
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||||
|
public static final String ENABLED = "enabled";
|
||||||
|
|
||||||
|
private ClientCookie() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.providers;
|
||||||
|
|
||||||
|
import android.content.ContentProvider;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.UriMatcher;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.torproject.android.ui.hiddenservices.database.HSDatabase;
|
||||||
|
|
||||||
|
|
||||||
|
public class HSContentProvider extends ContentProvider {
|
||||||
|
public static final String[] PROJECTION = new String[]{
|
||||||
|
HiddenService._ID,
|
||||||
|
HiddenService.NAME,
|
||||||
|
HiddenService.PORT,
|
||||||
|
HiddenService.DOMAIN,
|
||||||
|
HiddenService.ONION_PORT,
|
||||||
|
HiddenService.AUTH_COOKIE,
|
||||||
|
HiddenService.AUTH_COOKIE_VALUE,
|
||||||
|
HiddenService.CREATED_BY_USER,
|
||||||
|
HiddenService.ENABLED
|
||||||
|
};
|
||||||
|
private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers";
|
||||||
|
public static final Uri CONTENT_URI =
|
||||||
|
Uri.parse("content://" + AUTH + "/hs");
|
||||||
|
//UriMatcher
|
||||||
|
private static final int ONIONS = 1;
|
||||||
|
private static final int ONION_ID = 2;
|
||||||
|
|
||||||
|
private static final UriMatcher uriMatcher;
|
||||||
|
|
||||||
|
static {
|
||||||
|
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
||||||
|
uriMatcher.addURI(AUTH, "hs", ONIONS);
|
||||||
|
uriMatcher.addURI(AUTH, "hs/#", ONION_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HSDatabase mServervices;
|
||||||
|
private Context mContext;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreate() {
|
||||||
|
mContext = getContext();
|
||||||
|
mServervices = new HSDatabase(mContext);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == ONION_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getReadableDatabase();
|
||||||
|
|
||||||
|
return db.query(HSDatabase.HS_DATA_TABLE_NAME, projection, where,
|
||||||
|
selectionArgs, null, null, sortOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public String getType(@NonNull Uri uri) {
|
||||||
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
|
switch (match) {
|
||||||
|
case ONIONS:
|
||||||
|
return "vnd.android.cursor.dir/vnd.torproject.onions";
|
||||||
|
case ONION_ID:
|
||||||
|
return "vnd.android.cursor.item/vnd.torproject.onion";
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
|
long regId;
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
regId = db.insert(HSDatabase.HS_DATA_TABLE_NAME, null, values);
|
||||||
|
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return ContentUris.withAppendedId(CONTENT_URI, regId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == ONION_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
Integer rows = db.delete(HSDatabase.HS_DATA_TABLE_NAME, where, selectionArgs);
|
||||||
|
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||||
|
SQLiteDatabase db = mServervices.getWritableDatabase();
|
||||||
|
|
||||||
|
String where = selection;
|
||||||
|
if (uriMatcher.match(uri) == ONION_ID) {
|
||||||
|
where = "_id=" + uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer rows = db.update(HSDatabase.HS_DATA_TABLE_NAME, values, where, null);
|
||||||
|
mContext.getContentResolver().notifyChange(CONTENT_URI, null);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class HiddenService implements BaseColumns {
|
||||||
|
public static final String NAME = "name";
|
||||||
|
public static final String PORT = "port";
|
||||||
|
public static final String ONION_PORT = "onion_port";
|
||||||
|
public static final String DOMAIN = "domain";
|
||||||
|
public static final String AUTH_COOKIE = "auth_cookie";
|
||||||
|
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||||
|
public static final String CREATED_BY_USER = "created_by_user";
|
||||||
|
public static final String ENABLED = "enabled";
|
||||||
|
|
||||||
|
private HiddenService() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
package org.torproject.android.ui.hiddenservices.storage;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ExternalStorage {
|
||||||
|
private static final String ORBOT_BACKUPS_DIR = "Orbot";
|
||||||
|
|
||||||
|
public static File getOrCreateBackupDir() {
|
||||||
|
if (!isExternalStorageWritable())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
File dir = new File(Environment.getExternalStorageDirectory(), ORBOT_BACKUPS_DIR);
|
||||||
|
|
||||||
|
if (!dir.isDirectory() && !dir.mkdirs())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if external storage is available for read and write */
|
||||||
|
public static boolean isExternalStorageWritable() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
return Environment.MEDIA_MOUNTED.equals(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if external storage is available to at least read */
|
||||||
|
public static boolean isExternalStorageReadable() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
return Environment.MEDIA_MOUNTED.equals(state) ||
|
||||||
|
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context="org.torproject.android.ui.hiddenservices.ClientCookiesActivity">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:theme="@style/DefaultTheme.AppBarOverlay">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
app:popupTheme="@style/DefaultTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/layout_content_client_cookies" />
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
app:srcCompat="@android:drawable/stat_notify_more"
|
||||||
|
app:backgroundTint="@android:color/darker_gray" />
|
||||||
|
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/label_onion_name"
|
||||||
|
android:text="@string/onion"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/cookie_onion" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="@string/auth_cookie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/label_cookie_value"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/cookie_value"
|
||||||
|
android:inputType="text" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/btn_cancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cookie_dialog_cancel"
|
||||||
|
android:layout_weight="1"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/save"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/cookie_dialog_save"
|
||||||
|
android:layout_weight="1"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="15dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cookie_onion"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="18sp"
|
||||||
|
android:layout_weight="1" />
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/cookie_switch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:switchPadding="30dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,21 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/content_client_cookies"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:context="org.torproject.android.ui.hiddenservices.ClientCookiesActivity"
|
||||||
|
tools:showIn="@layout/layout_activity_client_cookies">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/clien_cookies_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/backup_cookie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_cookie_backup" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/delete_cookie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_cookie_delete" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/btn_cancel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_cookie_cancel" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/copy_address_to_clipboard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_hs_clipboard" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/show_auth_cookie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/bt_hs_show_auth" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/backup_service"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_hs_backup" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/delete_service"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_hs_delete" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/btn_cancel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/btn_hs_cancel" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/listview_hs_backups"
|
||||||
|
android:layout_marginTop="15dp" />
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/backup_list_item"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
tools:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
tools:paddingRight="15dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/backup_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="30sp"
|
||||||
|
android:layout_marginBottom="10dp" />
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical" android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/hs_cookie"
|
||||||
|
android:paddingBottom="5dp"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:paddingTop="5dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/copy_cookie_to_clipboard"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/hs_cookie_to_clipboard" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/share_as_qr"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/hs_cookie_to_qr" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/btn_cancel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/hs_cookie_cancel" />
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingLeft="5dp"
|
||||||
|
android:paddingRight="5dp"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="5dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/HSNameLabel"
|
||||||
|
android:text="@string/name"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="text"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/hsName" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="@string/local_port"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/HSLocalPortLabel"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/hsLocalPort"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:text="@string/onion_port"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/HSOnionPortLabel"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Widget.PopupMenu.Small"
|
||||||
|
android:paddingLeft="5dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:ems="10"
|
||||||
|
android:id="@+id/hsOnionPort"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:text="@string/auth_cookie"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/hsAuth"
|
||||||
|
android:paddingTop="5dp"
|
||||||
|
android:paddingBottom="10dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/btn_cancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/HSDialogCancel"
|
||||||
|
android:layout_weight="1"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:text="@string/save"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/HSDialogSave"
|
||||||
|
android:layout_weight="1"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless.Colored" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingLeft="15dp"
|
||||||
|
android:paddingRight="15dp"
|
||||||
|
tools:paddingLeft="15dp"
|
||||||
|
tools:paddingRight="15dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hs_port"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingRight="10dp"
|
||||||
|
android:paddingTop="10dp"
|
||||||
|
android:textSize="35sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hs_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:textSize="24sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/hs_onion"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="10dp"
|
||||||
|
android:paddingLeft="10dp"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
|
android:id="@+id/hs_switch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:switchPadding="30dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context="org.torproject.android.ui.hiddenservices.HiddenServicesActivity">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/layout_hs_list_view_main" />
|
||||||
|
|
||||||
|
<android.support.design.widget.FloatingActionButton
|
||||||
|
android:id="@+id/fab"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="bottom|end"
|
||||||
|
android:layout_margin="@dimen/fab_margin"
|
||||||
|
app:srcCompat="@android:drawable/stat_notify_more"
|
||||||
|
app:backgroundTint="@android:color/darker_gray" />
|
||||||
|
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/content_main"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:context="org.torproject.android.ui.hiddenservices.HiddenServicesActivity"
|
||||||
|
tools:showIn="@layout/layout_hs_list_view">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/onion_list"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</RelativeLayout>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/cookie_restore_backup"
|
||||||
|
android:title="@string/restore_backup" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/cookie_from_qr"
|
||||||
|
android:title="@string/cookie_from_QR" />
|
||||||
|
</menu>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item
|
||||||
|
android:id="@+id/hs_type"
|
||||||
|
android:background="#ff00"
|
||||||
|
android:title="@string/service_type"
|
||||||
|
app:actionViewClass="android.widget.Spinner"
|
||||||
|
app:showAsAction="always" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/menu_restore_backup"
|
||||||
|
android:title="@string/restore_backup" />
|
||||||
|
</menu>
|
|
@ -43,7 +43,23 @@
|
||||||
/>
|
/>
|
||||||
</menu>
|
</menu>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:title="@string/menu_hidden_services"
|
||||||
|
yourapp:showAsAction="never"
|
||||||
|
>
|
||||||
|
<menu>
|
||||||
|
<item android:id="@+id/menu_hidden_services"
|
||||||
|
android:title="@string/hosted_services"
|
||||||
|
yourapp:showAsAction="never"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<item android:id="@+id/menu_client_cookies"
|
||||||
|
android:title="@string/client_cookies"
|
||||||
|
yourapp:showAsAction="never"
|
||||||
|
/>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<item android:id="@+id/menu_promo_apps"
|
<item android:id="@+id/menu_promo_apps"
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string-array name="bridge_options">
|
||||||
|
<item>Obfs4 (Preferido)</item>
|
||||||
|
<item>Túnel a través de Azure</item>
|
||||||
|
<item>Túnel a través de Amazon</item>
|
||||||
|
<item>Obtener nuevos puentes</item>
|
||||||
|
<item></item>
|
||||||
|
</string-array>
|
||||||
|
<string-array name="array_hs_types">
|
||||||
|
<item>Usuarios</item>
|
||||||
|
<item>Aplicaciones</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
|
@ -291,4 +291,36 @@ direcciones (o rangos). No prevalecen sobre las configuraciones de exclusión de
|
||||||
<string name="standard_browser">Navegador estándar</string>
|
<string name="standard_browser">Navegador estándar</string>
|
||||||
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTA: Sólo los repetidores puente (bridges) estándar de Tor funcionan en dispositivos Intel X86/ATOM</string>
|
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTA: Sólo los repetidores puente (bridges) estándar de Tor funcionan en dispositivos Intel X86/ATOM</string>
|
||||||
<string name="vpn_default_world">Mundo</string>
|
<string name="vpn_default_world">Mundo</string>
|
||||||
|
|
||||||
|
<string name="hidden_services">Servicios Ocultos</string>
|
||||||
|
<string name="title_activity_hidden_services">Servicios Ocultos</string>
|
||||||
|
<string name="menu_hidden_services">Servicios Ocultos</string>
|
||||||
|
<string name="save">Guardar</string>
|
||||||
|
<string name="ports">Puertos</string>
|
||||||
|
<string name="local_port">Puerto Local</string>
|
||||||
|
<string name="onion_port">Puerto Onion</string>
|
||||||
|
<string name="name">Nombre</string>
|
||||||
|
<string name="done">Hecho!</string>
|
||||||
|
<string name="invalid_port">Puerto no vàlido</string>
|
||||||
|
<string name="copy_address_to_clipboard">Copiar dirección a portapapeles</string>
|
||||||
|
<string name="show_auth_cookie">Mostra cookie de autenticación</string>
|
||||||
|
<string name="backup_service">Crear backup</string>
|
||||||
|
<string name="delete_service">Borrar servicio</string>
|
||||||
|
<string name="backup_saved_at_external_storage">Backup guardado en almacenamiento externo</string>
|
||||||
|
<string name="backup_restored">Backup restaurado</string>
|
||||||
|
<string name="filemanager_not_available">Gestor de archivos no disponible</string>
|
||||||
|
<string name="please_grant_permissions_for_external_storage">Permitir permisos para almacenamiento externo</string>
|
||||||
|
<string name="permission_granted">Permisos concedidos</string>
|
||||||
|
<string name="permission_denied">Permisos denegados</string>
|
||||||
|
<string name="restore_backup">Restaurar Backup</string>
|
||||||
|
<string name="create_a_backup_first">Crear primero una copia de seguridad</string>
|
||||||
|
<string name="name_can_t_be_empty">Nombre no puede estar vacio</string>
|
||||||
|
<string name="start_tor_again_for_finish_the_process">Inicie Tor de nuevo para finalizar el proceso</string>
|
||||||
|
<string name="confirm_service_deletion">Confirmar eliminación de servicio</string>
|
||||||
|
<string name="click_again_for_backup">Haga clic de nuevo para realizar copias de seguridad</string>
|
||||||
|
<string name="service_type">Tipo de servicio</string>
|
||||||
|
<string name="auth_cookie">Cookie de autenticación</string>
|
||||||
|
<string name="copy_cookie_to_clipboard">Copiar cookie al portapapeles</string>
|
||||||
|
<string name="auth_cookie_was_not_configured">La cookie de autenticación no estaba configurada</string>
|
||||||
|
<string name="please_restart_Orbot_to_enable_the_changes">Reinicie Orbot para habilitar los cambios</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<resources></resources>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<resources>
|
||||||
|
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
|
||||||
|
(such as screen margins) for screens with more than 820dp of available width. This
|
||||||
|
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
|
||||||
|
<dimen name="activity_horizontal_margin">64dp</dimen>
|
||||||
|
</resources>
|
|
@ -9,5 +9,8 @@
|
||||||
<item>Get New Bridges</item>
|
<item>Get New Bridges</item>
|
||||||
<item></item>
|
<item></item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="array_hs_types">
|
||||||
|
<item>User services</item>
|
||||||
|
<item>App services</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!--
|
||||||
<!--
|
|
||||||
/* //device/apps/common/assets/res/any/dimens.xml
|
/* //device/apps/common/assets/res/any/dimens.xml
|
||||||
**
|
**
|
||||||
** Copyright 2006, The Android Open Source Project
|
** Copyright 2006, The Android Open Source Project
|
||||||
|
@ -118,26 +117,27 @@
|
||||||
<!-- The platform's desired minimum size for a dialog's width when it
|
<!-- The platform's desired minimum size for a dialog's width when it
|
||||||
is along the major axis (that is the screen is landscape). This may
|
is along the major axis (that is the screen is landscape). This may
|
||||||
be either a fraction or a dimension. -->
|
be either a fraction or a dimension. -->
|
||||||
<item type="dimen" name="dialog_min_width_major">65%</item>
|
<item name="dialog_min_width_major" type="dimen">65%</item>
|
||||||
|
|
||||||
<!-- The platform's desired fixed width for a dialog along the major axis
|
<!-- The platform's desired fixed width for a dialog along the major axis
|
||||||
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
||||||
<item type="dimen" name="dialog_fixed_width_major">320dp</item>
|
<item name="dialog_fixed_width_major" type="dimen">320dp</item>
|
||||||
<!-- The platform's desired fixed width for a dialog along the minor axis
|
<!-- The platform's desired fixed width for a dialog along the minor axis
|
||||||
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
||||||
<item type="dimen" name="dialog_fixed_width_minor">320dp</item>
|
<item name="dialog_fixed_width_minor" type="dimen">320dp</item>
|
||||||
<!-- The platform's desired fixed height for a dialog along the major axis
|
<!-- The platform's desired fixed height for a dialog along the major axis
|
||||||
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
(the screen is in portrait). This may be either a fraction or a dimension.-->
|
||||||
<item type="dimen" name="dialog_fixed_height_major">80%</item>
|
<item name="dialog_fixed_height_major" type="dimen">80%</item>
|
||||||
<!-- The platform's desired fixed height for a dialog along the minor axis
|
<!-- The platform's desired fixed height for a dialog along the minor axis
|
||||||
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
(the screen is in landscape). This may be either a fraction or a dimension.-->
|
||||||
<item type="dimen" name="dialog_fixed_height_minor">100%</item>
|
<item name="dialog_fixed_height_minor" type="dimen">100%</item>
|
||||||
|
|
||||||
<!-- Preference activity, vertical padding for the header list -->
|
<!-- Preference activity, vertical padding for the header list -->
|
||||||
<dimen name="preference_screen_header_vertical_padding">0dp</dimen>
|
<dimen name="preference_screen_header_vertical_padding">0dp</dimen>
|
||||||
|
|
||||||
<dimen name="preference_screen_header_padding_side">16dip</dimen>
|
<dimen name="preference_screen_header_padding_side">16dip</dimen>
|
||||||
<integer name="preference_screen_header_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
|
<integer name="preference_screen_header_scrollbarStyle">0x02000000
|
||||||
|
</integer> <!-- outsideOverlay -->
|
||||||
|
|
||||||
<integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
|
<integer name="preference_fragment_scrollbarStyle">0x02000000</integer> <!-- outsideOverlay -->
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
<!-- The platform's desired minimum size for a dialog's width when it
|
<!-- The platform's desired minimum size for a dialog's width when it
|
||||||
is along the minor axis (that is the screen is portrait). This may
|
is along the minor axis (that is the screen is portrait). This may
|
||||||
be either a fraction or a dimension. -->
|
be either a fraction or a dimension. -->
|
||||||
<item type="dimen" name="dialog_min_width_minor">95%</item>
|
<item name="dialog_min_width_minor" type="dimen">95%</item>
|
||||||
|
|
||||||
<!-- The width of the big icons in notifications. -->
|
<!-- The width of the big icons in notifications. -->
|
||||||
<dimen name="notification_large_icon_width">64dp</dimen>
|
<dimen name="notification_large_icon_width">64dp</dimen>
|
||||||
|
@ -358,4 +358,8 @@
|
||||||
|
|
||||||
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
|
<!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
|
||||||
<dimen name="immersive_mode_cling_width">-1px</dimen>
|
<dimen name="immersive_mode_cling_width">-1px</dimen>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -128,21 +128,21 @@
|
||||||
<string name="pref_entrance_node_dialog">Enter Entrance Nodes</string>
|
<string name="pref_entrance_node_dialog">Enter Entrance Nodes</string>
|
||||||
<string name="pref_allow_background_starts_title">Allow Background Starts</string>
|
<string name="pref_allow_background_starts_title">Allow Background Starts</string>
|
||||||
<string name="pref_allow_background_starts_summary">Let any app tell Orbot to start Tor and related services</string>
|
<string name="pref_allow_background_starts_summary">Let any app tell Orbot to start Tor and related services</string>
|
||||||
|
|
||||||
<string name="button_proxy_all">Proxy All</string>
|
<string name="button_proxy_all">Proxy All</string>
|
||||||
<string name="button_proxy_none">Proxy None</string>
|
<string name="button_proxy_none">Proxy None</string>
|
||||||
<string name="button_invert_selection">Invert Selection</string>
|
<string name="button_invert_selection">Invert Selection</string>
|
||||||
|
|
||||||
<string name="pref_proxy_title">Outbound Network Proxy (Optional)</string>
|
<string name="pref_proxy_title">Outbound Network Proxy (Optional)</string>
|
||||||
|
|
||||||
<string name="pref_proxy_type_title">Outbound Proxy Type</string>
|
<string name="pref_proxy_type_title">Outbound Proxy Type</string>
|
||||||
<string name="pref_proxy_type_summary">Protocol to use for proxy server: HTTP, HTTPS, Socks4, Socks5</string>
|
<string name="pref_proxy_type_summary">Protocol to use for proxy server: HTTP, HTTPS, Socks4, Socks5</string>
|
||||||
<string name="pref_proxy_type_dialog">Enter Proxy Type</string>
|
<string name="pref_proxy_type_dialog">Enter Proxy Type</string>
|
||||||
|
|
||||||
<string name="pref_proxy_host_title">Outbound Proxy Host</string>
|
<string name="pref_proxy_host_title">Outbound Proxy Host</string>
|
||||||
<string name="pref_proxy_host_summary">Proxy Server hostname</string>
|
<string name="pref_proxy_host_summary">Proxy Server hostname</string>
|
||||||
<string name="pref_proxy_host_dialog">Enter Proxy Host</string>
|
<string name="pref_proxy_host_dialog">Enter Proxy Host</string>
|
||||||
|
|
||||||
<string name="pref_proxy_port_title">Outbound Proxy Port</string>
|
<string name="pref_proxy_port_title">Outbound Proxy Port</string>
|
||||||
<string name="pref_proxy_port_summary">Proxy Server port</string>
|
<string name="pref_proxy_port_summary">Proxy Server port</string>
|
||||||
<string name="pref_proxy_port_dialog">Enter Proxy port</string>
|
<string name="pref_proxy_port_dialog">Enter Proxy port</string>
|
||||||
|
@ -150,13 +150,12 @@
|
||||||
<string name="pref_proxy_username_title">Outbound Proxy Username</string>
|
<string name="pref_proxy_username_title">Outbound Proxy Username</string>
|
||||||
<string name="pref_proxy_username_summary">Proxy Username (Optional)</string>
|
<string name="pref_proxy_username_summary">Proxy Username (Optional)</string>
|
||||||
<string name="pref_proxy_username_dialog">Enter Proxy Username</string>
|
<string name="pref_proxy_username_dialog">Enter Proxy Username</string>
|
||||||
|
|
||||||
<string name="pref_proxy_password_title">Outbound Proxy Password</string>
|
<string name="pref_proxy_password_title">Outbound Proxy Password</string>
|
||||||
<string name="pref_proxy_password_summary">Proxy Password (Optional)</string>
|
<string name="pref_proxy_password_summary">Proxy Password (Optional)</string>
|
||||||
<string name="pref_proxy_password_dialog">Enter Proxy Password</string>
|
<string name="pref_proxy_password_dialog">Enter Proxy Password</string>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<string name="status">Status</string>
|
<string name="status">Status</string>
|
||||||
<string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying…</string>
|
<string name="setting_up_full_transparent_proxying_">Setting up full transparent proxying…</string>
|
||||||
<string name="setting_up_app_based_transparent_proxying_">Setting up app-based transparent proxying…</string>
|
<string name="setting_up_app_based_transparent_proxying_">Setting up app-based transparent proxying…</string>
|
||||||
|
@ -227,17 +226,17 @@
|
||||||
<string name="unable_to_reset_tor">Reboot your device, unable to reset Tor!</string>
|
<string name="unable_to_reset_tor">Reboot your device, unable to reset Tor!</string>
|
||||||
<string name="pref_use_sys_iptables_title">Use Default Iptables</string>
|
<string name="pref_use_sys_iptables_title">Use Default Iptables</string>
|
||||||
<string name="pref_use_sys_iptables_summary">use the built-in iptables binary instead of the one bundled with Orbot</string>
|
<string name="pref_use_sys_iptables_summary">use the built-in iptables binary instead of the one bundled with Orbot</string>
|
||||||
|
|
||||||
<string name="error_installing_binares">The Tor binaries were not able to be installed or upgraded.</string>
|
<string name="error_installing_binares">The Tor binaries were not able to be installed or upgraded.</string>
|
||||||
|
|
||||||
<string name="pref_use_persistent_notifications">Always keep the icon in toolbar when Orbot is connected</string>
|
<string name="pref_use_persistent_notifications">Always keep the icon in toolbar when Orbot is connected</string>
|
||||||
<string name="pref_use_persistent_notifications_title">Always-On Notifications</string>
|
<string name="pref_use_persistent_notifications_title">Always-On Notifications</string>
|
||||||
|
|
||||||
<string name="pref_use_expanded_notifications">Show expanded notification with Tor exit country and IP</string>
|
<string name="pref_use_expanded_notifications">Show expanded notification with Tor exit country and IP</string>
|
||||||
<string name="pref_use_expanded_notifications_title">Expanded Notifications</string>
|
<string name="pref_use_expanded_notifications_title">Expanded Notifications</string>
|
||||||
|
|
||||||
<string name="notification_using_bridges">Bridges enabled!</string>
|
<string name="notification_using_bridges">Bridges enabled!</string>
|
||||||
<string name="default_bridges"/>
|
<string name="default_bridges" />
|
||||||
<string name="set_locale_title">Language</string>
|
<string name="set_locale_title">Language</string>
|
||||||
<string name="set_locale_summary">Choose the locale and language for Orbot</string>
|
<string name="set_locale_summary">Choose the locale and language for Orbot</string>
|
||||||
<string name="wizard_locale_title">Choose Language</string>
|
<string name="wizard_locale_title">Choose Language</string>
|
||||||
|
@ -251,8 +250,8 @@
|
||||||
<string name="pref_disable_network_title">No Network Auto-Sleep</string>
|
<string name="pref_disable_network_title">No Network Auto-Sleep</string>
|
||||||
<string name="pref_disable_network_summary">Put Tor to sleep when there is no internet available</string>
|
<string name="pref_disable_network_summary">Put Tor to sleep when there is no internet available</string>
|
||||||
<string name="newnym">You\'ve switched to a new Tor identity!</string>
|
<string name="newnym">You\'ve switched to a new Tor identity!</string>
|
||||||
|
|
||||||
<string name="menu_verify_browser">Browser</string>
|
<string name="menu_verify_browser">Browser</string>
|
||||||
<string name="menu_use_chatsecure">Use ChatSecure</string>
|
<string name="menu_use_chatsecure">Use ChatSecure</string>
|
||||||
|
|
||||||
<string name="permission_manage_tor_label">Manage Tor</string>
|
<string name="permission_manage_tor_label">Manage Tor</string>
|
||||||
|
@ -262,78 +261,123 @@
|
||||||
<string name="no_network_connectivity_putting_tor_to_sleep_">No network connectivity. Putting Tor to sleep…</string>
|
<string name="no_network_connectivity_putting_tor_to_sleep_">No network connectivity. Putting Tor to sleep…</string>
|
||||||
<string name="network_connectivity_is_good_waking_tor_up_">Network connectivity is good. Waking Tor up…</string>
|
<string name="network_connectivity_is_good_waking_tor_up_">Network connectivity is good. Waking Tor up…</string>
|
||||||
<string name="updating_settings_in_tor_service">updating settings in Tor service</string>
|
<string name="updating_settings_in_tor_service">updating settings in Tor service</string>
|
||||||
|
|
||||||
<string name="pref_socks_title">Tor SOCKS</string>
|
<string name="pref_socks_title">Tor SOCKS</string>
|
||||||
<string name="pref_socks_summary">Port that Tor offers its SOCKS proxy on (default: 9050 or 0 to disable)</string>
|
<string name="pref_socks_summary">Port that Tor offers its SOCKS proxy on (default: 9050 or 0 to disable)</string>
|
||||||
<string name="pref_socks_dialog">SOCKS Port Config</string>
|
<string name="pref_socks_dialog">SOCKS Port Config</string>
|
||||||
|
|
||||||
<string name="pref_transport_title">Tor TransProxy Port</string>
|
<string name="pref_transport_title">Tor TransProxy Port</string>
|
||||||
<string name="pref_transport_summary">Port that Tor offers its Transparent Proxy on (default: 9040 or 0 to disable)</string>
|
<string name="pref_transport_summary">Port that Tor offers its Transparent Proxy on (default: 9040 or 0 to disable)</string>
|
||||||
<string name="pref_transport_dialog">TransProxy Port Config</string>
|
<string name="pref_transport_dialog">TransProxy Port Config</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="pref_dnsport_title">Tor DNS Port</string>
|
<string name="pref_dnsport_title">Tor DNS Port</string>
|
||||||
<string name="pref_dnsport_summary">Port that Tor offers its DNS on (default: 5400 or 0 to disable)</string>
|
<string name="pref_dnsport_summary">Port that Tor offers its DNS on (default: 5400 or 0 to disable)</string>
|
||||||
<string name="pref_dnsport_dialog">DNS Port Config</string>
|
<string name="pref_dnsport_dialog">DNS Port Config</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="pref_torrc_title">Torrc Custom Config</string>
|
<string name="pref_torrc_title">Torrc Custom Config</string>
|
||||||
<string name="pref_torrc_summary">EXPERTS ONLY: enter direct torrc config lines</string>
|
<string name="pref_torrc_summary">EXPERTS ONLY: enter direct torrc config lines</string>
|
||||||
<string name="pref_torrc_dialog">Custom Torrc</string>
|
<string name="pref_torrc_dialog">Custom Torrc</string>
|
||||||
|
|
||||||
<string name="wizard_tips_martus">Mobile Martus - Benetech Human Rights Documentation App</string>
|
<string name="wizard_tips_martus">Mobile Martus - Benetech Human Rights Documentation App</string>
|
||||||
<string name="your_tor_public_ips_">Your Tor Public IPs:</string>
|
<string name="your_tor_public_ips_">Your Tor Public IPs:</string>
|
||||||
<string name="please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_">"Please disable this app in Android->Settings->Apps if you are having problems with Orbot: "</string>
|
<string name="please_disable_this_app_in_android_settings_apps_if_you_are_having_problems_with_orbot_">"Please disable this app in Android->Settings->Apps if you are having problems with Orbot: "</string>
|
||||||
<string name="app_conflict">App Conflict</string>
|
<string name="app_conflict">App Conflict</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="pref_transproxy_refresh_title">Transproxy Auto-Refresh</string>
|
<string name="pref_transproxy_refresh_title">Transproxy Auto-Refresh</string>
|
||||||
<string name="pref_transproxy_refresh_summary">Re-apply Transproxy rules when the network state changes</string>
|
<string name="pref_transproxy_refresh_summary">Re-apply Transproxy rules when the network state changes</string>
|
||||||
|
|
||||||
<string name="pref_transproxy_flush_title">Transproxy FORCE REMOVE</string>
|
<string name="pref_transproxy_flush_title">Transproxy FORCE REMOVE</string>
|
||||||
<string name="pref_transproxy_flush_summary">Tap here to flush all transproxy network rules NOW</string>
|
<string name="pref_transproxy_flush_summary">Tap here to flush all transproxy network rules NOW</string>
|
||||||
<string name="transparent_proxy_rules_flushed_">Transparent proxy rules flushed!</string>
|
<string name="transparent_proxy_rules_flushed_">Transparent proxy rules flushed!</string>
|
||||||
<string name="you_do_not_have_root_access_enabled">You do not have ROOT access enabled</string>
|
<string name="you_do_not_have_root_access_enabled">You do not have ROOT access enabled</string>
|
||||||
<string name="you_may_need_to_stop_and_start_orbot_for_settings_change_to_be_enabled_">You may need to stop and start Orbot for settings change to be enabled.</string>
|
<string name="you_may_need_to_stop_and_start_orbot_for_settings_change_to_be_enabled_">You may need to stop and start Orbot for settings change to be enabled.</string>
|
||||||
|
|
||||||
<string name="menu_vpn">Apps</string>
|
<string name="menu_vpn">Apps</string>
|
||||||
|
|
||||||
<string name="kbps">kbps</string>
|
<string name="kbps">kbps</string>
|
||||||
|
|
||||||
<string name="mbps">mbps</string>
|
<string name="mbps">mbps</string>
|
||||||
|
|
||||||
<string name="kb">KB</string>
|
<string name="kb">KB</string>
|
||||||
|
|
||||||
<string name="mb">MB</string>
|
<string name="mb">MB</string>
|
||||||
|
|
||||||
<string name="bridges_updated">Bridges Updated</string>
|
<string name="bridges_updated">Bridges Updated</string>
|
||||||
|
|
||||||
<string name="restart_orbot_to_use_this_bridge_">Please restart Orbot to enable the changes</string>
|
<string name="restart_orbot_to_use_this_bridge_">Please restart Orbot to enable the changes</string>
|
||||||
|
|
||||||
<string name="menu_qr">QR Codes</string>
|
<string name="menu_qr">QR Codes</string>
|
||||||
|
|
||||||
<string name="if_your_mobile_network_actively_blocks_tor_you_can_use_a_tor_bridge_to_access_the_network_another_way_to_get_bridges_is_to_send_an_email_to_bridges_torproject_org_please_note_that_you_must_send_the_email_using_an_address_from_one_of_the_following_email_providers_riseup_gmail_or_yahoo_">If your mobile network actively blocks Tor, you can use a Bridge to access the network. SELECT one of the bridge types above to enable bridges.</string>
|
<string name="if_your_mobile_network_actively_blocks_tor_you_can_use_a_tor_bridge_to_access_the_network_another_way_to_get_bridges_is_to_send_an_email_to_bridges_torproject_org_please_note_that_you_must_send_the_email_using_an_address_from_one_of_the_following_email_providers_riseup_gmail_or_yahoo_">If your mobile network actively blocks Tor, you can use a Bridge to access the network. SELECT one of the bridge types above to enable bridges.</string>
|
||||||
|
|
||||||
<string name="bridge_mode">Bridge Mode</string>
|
<string name="bridge_mode">Bridge Mode</string>
|
||||||
|
|
||||||
<string name="get_bridges_email">Email</string>
|
<string name="get_bridges_email">Email</string>
|
||||||
<string name="get_bridges_web">Web</string>
|
<string name="get_bridges_web">Web</string>
|
||||||
|
|
||||||
<string name="activate">Activate</string>
|
<string name="activate">Activate</string>
|
||||||
|
|
||||||
<string name="apps_mode">Apps VPN Mode</string>
|
<string name="apps_mode">Apps VPN Mode</string>
|
||||||
|
|
||||||
<string name="you_can_enable_all_apps_on_your_device_to_run_through_the_tor_network_using_the_vpn_feature_of_android_">You can enable all apps on your device to run through the Tor network using the VPN feature of Android.\n\n*WARNING* This is a new, experimental feature and in some cases may not start automatically, or may stop. It should NOT be used for anonymity, and ONLY used for getting through firewalls and filters.</string>
|
<string name="you_can_enable_all_apps_on_your_device_to_run_through_the_tor_network_using_the_vpn_feature_of_android_">You can enable all apps on your device to run through the Tor network using the VPN feature of Android.\n\n*WARNING* This is a new, experimental feature and in some cases may not start automatically, or may stop. It should NOT be used for anonymity, and ONLY used for getting through firewalls and filters.</string>
|
||||||
|
|
||||||
<string name="send_email">Send Email</string>
|
<string name="send_email">Send Email</string>
|
||||||
|
|
||||||
<string name="you_must_get_a_bridge_address_by_email_web_or_from_a_friend_once_you_have_this_address_please_paste_it_into_the_bridges_preference_in_orbot_s_setting_and_restart_">You can get a bridge address through email, the web or by scanning a bridge QR code. Select \'Email\' or \'Web\' below to request a bridge address.\n\nOnce you have an address, copy & paste it into the \"Bridges\" preference in Orbot\'s setting and restart.</string>
|
<string name="you_must_get_a_bridge_address_by_email_web_or_from_a_friend_once_you_have_this_address_please_paste_it_into_the_bridges_preference_in_orbot_s_setting_and_restart_">You can get a bridge address through email, the web or by scanning a bridge QR code. Select \'Email\' or \'Web\' below to request a bridge address.\n\nOnce you have an address, copy & paste it into the \"Bridges\" preference in Orbot\'s setting and restart.</string>
|
||||||
|
|
||||||
<string name="install_orweb">Install Orfox</string>
|
<string name="install_orweb">Install Orfox</string>
|
||||||
|
|
||||||
<string name="standard_browser">Standard Browser</string>
|
<string name="standard_browser">Standard Browser</string>
|
||||||
|
|
||||||
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTE: Only standard Tor bridges work on Intel X86/ATOM devices</string>
|
<string name="note_only_standard_tor_bridges_work_on_intel_x86_atom_devices">NOTE: Only standard Tor bridges work on Intel X86/ATOM devices</string>
|
||||||
|
|
||||||
<string name="vpn_default_world">World (Location)</string>
|
<string name="vpn_default_world">World (Location)</string>
|
||||||
|
<string name="hidden_services">Hidden Services</string>
|
||||||
|
<string name="title_activity_hidden_services">Hidden Services</string>
|
||||||
|
<string name="menu_hidden_services">Hidden Services</string>
|
||||||
|
<string name="save">Save</string>
|
||||||
|
<string name="ports">Ports</string>
|
||||||
|
<string name="local_port">Local Port</string>
|
||||||
|
<string name="onion_port">Onion Port</string>
|
||||||
|
<string name="name">Name</string>
|
||||||
|
<string name="done">Done!</string>
|
||||||
|
<string name="invalid_port">Invalid Port</string>
|
||||||
|
<string name="copy_address_to_clipboard">Copy address to clipboard</string>
|
||||||
|
<string name="show_auth_cookie">Show auth cookie</string>
|
||||||
|
<string name="backup_service">Backup Service</string>
|
||||||
|
<string name="delete_service">Delete Service</string>
|
||||||
|
<string name="backup_saved_at_external_storage">Backup saved at external storage</string>
|
||||||
|
<string name="backup_restored">Backup restored</string>
|
||||||
|
<string name="filemanager_not_available">Filemanager not available</string>
|
||||||
|
<string name="please_grant_permissions_for_external_storage">Please grant permissions for external storage</string>
|
||||||
|
<string name="permission_granted">Permission granted</string>
|
||||||
|
<string name="permission_denied">Permission denied</string>
|
||||||
|
<string name="restore_backup">Restore Backup</string>
|
||||||
|
<string name="create_a_backup_first">Create a backup first</string>
|
||||||
|
<string name="name_can_t_be_empty">Name can\'t be empty</string>
|
||||||
|
<string name="fields_can_t_be_empty">Fields can\'t be empty</string>
|
||||||
|
<string name="start_tor_again_for_finish_the_process">Start Tor again for finish the process</string>
|
||||||
|
<string name="confirm_service_deletion">Confirm service deletion</string>
|
||||||
|
<string name="click_again_for_backup">Click again for backup</string>
|
||||||
|
<string name="service_type">Service type</string>
|
||||||
|
<string name="auth_cookie">Auth cookie</string>
|
||||||
|
<string name="copy_cookie_to_clipboard">Copy cookie to clipboard</string>
|
||||||
|
<string name="auth_cookie_was_not_configured">Auth cookie was not configured</string>
|
||||||
|
<string name="please_restart_Orbot_to_enable_the_changes">Please restart Orbot to enable the changes</string>
|
||||||
|
<string name="client_cookies">Client cookies</string>
|
||||||
|
<string name="onion">.onion</string>
|
||||||
|
<string name="invalid_onion_address">Invalid .onion address</string>
|
||||||
|
<string name="cookie_from_QR">Read from QR</string>
|
||||||
|
<string name="backup_cookie">Backup cookie</string>
|
||||||
|
<string name="delete_cookie">Delete cookie</string>
|
||||||
|
<string name="confirm_cookie_deletion">Confirm cookie deletion</string>
|
||||||
|
<string name="hosted_services">Hosted Services</string>
|
||||||
|
<string name="share_as_qr">Share as QR</string>
|
||||||
|
<string name="disable">Disable</string>
|
||||||
|
<string name="enable">Enable</string>
|
||||||
|
<string name="consider_disable_battery_optimizations">Consider disable battery optimizations</string>
|
||||||
|
<string name="consider_enable_battery_optimizations">Consider enable battery optimizations</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
|
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
|
||||||
|
|
||||||
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
|
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
|
||||||
|
|
||||||
|
<style name="DefaultTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||||
|
|
||||||
|
<style name="DefaultTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<files-path name="hidden-services" path="hidden_services/" />
|
||||||
|
</paths>
|
|
@ -157,17 +157,6 @@ android:dialogTitle="@string/enter_ports"
|
||||||
/>
|
/>
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_hs_group">
|
|
||||||
<CheckBoxPreference android:title="@string/enable_hidden_services"
|
|
||||||
android:summary="@string/run_servers_accessible_via_the_tor_network" android:key="pref_hs_enable"></CheckBoxPreference>
|
|
||||||
<EditTextPreference android:summary="@string/enter_localhost_ports_for_hidden_services"
|
|
||||||
android:title="@string/hidden_service_ports" android:enabled="false" android:key="pref_hs_ports"></EditTextPreference>
|
|
||||||
|
|
||||||
<EditTextPreference android:key="pref_hs_hostname"
|
|
||||||
android:summary="@string/the_addressable_name_for_your_hidden_service_generated_automatically_"
|
|
||||||
android:title=".Onion Hostname"></EditTextPreference>
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/pref_proxy_title">
|
<PreferenceCategory android:title="@string/pref_proxy_title">
|
||||||
<EditTextPreference android:key="pref_proxy_type"
|
<EditTextPreference android:key="pref_proxy_type"
|
||||||
android:title="@string/pref_proxy_type_title"
|
android:title="@string/pref_proxy_type_title"
|
||||||
|
|
|
@ -16,19 +16,22 @@ import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.provider.BaseColumns;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -62,7 +65,6 @@ import java.io.PrintStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.text.Normalizer;
|
import java.text.Normalizer;
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -124,10 +126,54 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
public static File fileObfsclient;
|
public static File fileObfsclient;
|
||||||
public static File fileXtables;
|
public static File fileXtables;
|
||||||
public static File fileTorRc;
|
public static File fileTorRc;
|
||||||
|
private File mHSBasePath;
|
||||||
|
|
||||||
private Shell mShell;
|
private Shell mShell;
|
||||||
private Shell mShellPolipo;
|
private Shell mShellPolipo;
|
||||||
|
|
||||||
|
|
||||||
|
private static final Uri HS_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers/hs");
|
||||||
|
private static final Uri COOKIE_CONTENT_URI = Uri.parse("content://org.torproject.android.ui.hiddenservices.providers.cookie/cookie");
|
||||||
|
|
||||||
|
public static final class HiddenService implements BaseColumns {
|
||||||
|
public static final String NAME = "name";
|
||||||
|
public static final String PORT = "port";
|
||||||
|
public static final String ONION_PORT = "onion_port";
|
||||||
|
public static final String DOMAIN = "domain";
|
||||||
|
public static final String AUTH_COOKIE = "auth_cookie";
|
||||||
|
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||||
|
public static final String CREATED_BY_USER = "created_by_user";
|
||||||
|
public static final String ENABLED = "enabled";
|
||||||
|
|
||||||
|
private HiddenService() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ClientCookie implements BaseColumns {
|
||||||
|
public static final String DOMAIN = "domain";
|
||||||
|
public static final String AUTH_COOKIE_VALUE = "auth_cookie_value";
|
||||||
|
public static final String ENABLED = "enabled";
|
||||||
|
|
||||||
|
private ClientCookie() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] hsProjection = new String[]{
|
||||||
|
HiddenService._ID,
|
||||||
|
HiddenService.NAME,
|
||||||
|
HiddenService.DOMAIN,
|
||||||
|
HiddenService.PORT,
|
||||||
|
HiddenService.AUTH_COOKIE,
|
||||||
|
HiddenService.AUTH_COOKIE_VALUE,
|
||||||
|
HiddenService.ONION_PORT,
|
||||||
|
HiddenService.ENABLED};
|
||||||
|
|
||||||
|
private String[] cookieProjection = new String[]{
|
||||||
|
ClientCookie._ID,
|
||||||
|
ClientCookie.DOMAIN,
|
||||||
|
ClientCookie.AUTH_COOKIE_VALUE,
|
||||||
|
ClientCookie.ENABLED};
|
||||||
|
|
||||||
public void debug(String msg)
|
public void debug(String msg)
|
||||||
{
|
{
|
||||||
if (Prefs.useDebugLogging())
|
if (Prefs.useDebugLogging())
|
||||||
|
@ -441,75 +487,6 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
sendCallbackStatus(STATUS_OFF);
|
sendCallbackStatus(STATUS_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private String getHiddenServiceHostname ()
|
|
||||||
{
|
|
||||||
|
|
||||||
SharedPreferences prefs = TorServiceUtils.getSharedPrefs(getApplicationContext());
|
|
||||||
|
|
||||||
boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false);
|
|
||||||
|
|
||||||
StringBuffer result = new StringBuffer();
|
|
||||||
|
|
||||||
if (enableHiddenServices)
|
|
||||||
{
|
|
||||||
String hsPorts = prefs.getString("pref_hs_ports","");
|
|
||||||
|
|
||||||
StringTokenizer st = new StringTokenizer (hsPorts,",");
|
|
||||||
String hsPortConfig = null;
|
|
||||||
|
|
||||||
while (st.hasMoreTokens())
|
|
||||||
{
|
|
||||||
|
|
||||||
int hsPort = Integer.parseInt(st.nextToken().split(" ")[0]);;
|
|
||||||
|
|
||||||
File fileDir = new File(appCacheHome, "hs" + hsPort);
|
|
||||||
File file = new File(fileDir, "hostname");
|
|
||||||
|
|
||||||
|
|
||||||
if (file.exists())
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
String onionHostname = Utils.readString(new FileInputStream(file)).trim();
|
|
||||||
|
|
||||||
if (result.length() > 0)
|
|
||||||
result.append(",");
|
|
||||||
|
|
||||||
result.append(onionHostname);
|
|
||||||
|
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
logException("unable to read onion hostname file",e);
|
|
||||||
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.length() > 0)
|
|
||||||
{
|
|
||||||
String onionHostname = result.toString();
|
|
||||||
|
|
||||||
showToolbarNotification(getString(R.string.hidden_service_on) + ' ' + onionHostname, HS_NOTIFY_ID, R.drawable.ic_stat_tor);
|
|
||||||
Editor pEdit = prefs.edit();
|
|
||||||
pEdit.putString("pref_hs_hostname",onionHostname);
|
|
||||||
pEdit.commit();
|
|
||||||
|
|
||||||
return onionHostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void killAllDaemons() throws Exception {
|
private void killAllDaemons() throws Exception {
|
||||||
if (conn != null) {
|
if (conn != null) {
|
||||||
logNotice("Using control port to shutdown Tor");
|
logNotice("Using control port to shutdown Tor");
|
||||||
|
@ -577,6 +554,14 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
fileXtables = new File(appBinHome, TorServiceConstants.IPTABLES_ASSET_KEY);
|
fileXtables = new File(appBinHome, TorServiceConstants.IPTABLES_ASSET_KEY);
|
||||||
fileTorRc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY);
|
fileTorRc = new File(appBinHome, TorServiceConstants.TORRC_ASSET_KEY);
|
||||||
|
|
||||||
|
mHSBasePath = new File(
|
||||||
|
getFilesDir().getAbsolutePath(),
|
||||||
|
TorServiceConstants.HIDDEN_SERVICES_DIR
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!mHSBasePath.isDirectory())
|
||||||
|
mHSBasePath.mkdirs();
|
||||||
|
|
||||||
mEventHandler = new TorEventHandler(this);
|
mEventHandler = new TorEventHandler(this);
|
||||||
|
|
||||||
if (mNotificationManager == null)
|
if (mNotificationManager == null)
|
||||||
|
@ -809,7 +794,56 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
enableTransparentProxy();
|
enableTransparentProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
getHiddenServiceHostname ();
|
// Tor is running, update new .onion names at db
|
||||||
|
ContentResolver mCR = getApplicationContext().getContentResolver();
|
||||||
|
Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, null, null, null);
|
||||||
|
if(hidden_services != null) {
|
||||||
|
try {
|
||||||
|
while (hidden_services.moveToNext()) {
|
||||||
|
String HSDomain = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.DOMAIN));
|
||||||
|
Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
|
||||||
|
Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
|
||||||
|
String HSAuthCookieValue = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE_VALUE));
|
||||||
|
|
||||||
|
// Update only new domains or restored from backup with auth cookie
|
||||||
|
if((HSDomain == null || HSDomain.length() < 1) || (HSAuthCookie == 1 && (HSAuthCookieValue == null || HSAuthCookieValue.length() < 1))) {
|
||||||
|
String hsDirPath = new File(mHSBasePath.getAbsolutePath(),"hs" + HSLocalPort).getCanonicalPath();
|
||||||
|
File file = new File(hsDirPath, "hostname");
|
||||||
|
|
||||||
|
if (file.exists())
|
||||||
|
{
|
||||||
|
ContentValues fields = new ContentValues();
|
||||||
|
|
||||||
|
try {
|
||||||
|
String onionHostname = Utils.readString(new FileInputStream(file)).trim();
|
||||||
|
if(HSAuthCookie == 1) {
|
||||||
|
String[] aux = onionHostname.split(" ");
|
||||||
|
onionHostname = aux[0];
|
||||||
|
fields.put(HiddenService.AUTH_COOKIE_VALUE, aux[1]);
|
||||||
|
}
|
||||||
|
fields.put(HiddenService.DOMAIN, onionHostname);
|
||||||
|
mCR.update(HS_CONTENT_URI, fields, "port=" + HSLocalPort , null);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
logException("unable to read onion hostname file",e);
|
||||||
|
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
showToolbarNotification(getString(R.string.unable_to_read_hidden_service_name), HS_NOTIFY_ID, R.drawable.ic_stat_notifyerr);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||||
|
}
|
||||||
|
|
||||||
|
hidden_services.close();
|
||||||
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logException("Unable to start Tor: " + e.toString(), e);
|
logException("Unable to start Tor: " + e.toString(), e);
|
||||||
|
@ -1574,7 +1608,6 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
|
|
||||||
boolean becomeRelay = prefs.getBoolean(OrbotConstants.PREF_OR, false);
|
boolean becomeRelay = prefs.getBoolean(OrbotConstants.PREF_OR, false);
|
||||||
boolean ReachableAddresses = prefs.getBoolean(OrbotConstants.PREF_REACHABLE_ADDRESSES,false);
|
boolean ReachableAddresses = prefs.getBoolean(OrbotConstants.PREF_REACHABLE_ADDRESSES,false);
|
||||||
boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false);
|
|
||||||
|
|
||||||
boolean enableStrictNodes = prefs.getBoolean("pref_strict_nodes", false);
|
boolean enableStrictNodes = prefs.getBoolean("pref_strict_nodes", false);
|
||||||
String entranceNodes = prefs.getString("pref_entrance_nodes", "");
|
String entranceNodes = prefs.getString("pref_entrance_nodes", "");
|
||||||
|
@ -1777,49 +1810,52 @@ public class TorService extends Service implements TorServiceConstants, OrbotCon
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableHiddenServices)
|
ContentResolver mCR = getApplicationContext().getContentResolver();
|
||||||
{
|
|
||||||
logNotice("hidden services are enabled");
|
/* ---- Hidden Services ---- */
|
||||||
|
Cursor hidden_services = mCR.query(HS_CONTENT_URI, hsProjection, HiddenService.ENABLED + "=1", null, null);
|
||||||
//updateConfiguration("RendPostPeriod", "600 seconds", false); //possible feature to investigate
|
if(hidden_services != null) {
|
||||||
|
try {
|
||||||
String hsPorts = prefs.getString("pref_hs_ports","");
|
while (hidden_services.moveToNext()) {
|
||||||
|
String HSname = hidden_services.getString(hidden_services.getColumnIndex(HiddenService.NAME));
|
||||||
StringTokenizer st = new StringTokenizer (hsPorts,",");
|
Integer HSLocalPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.PORT));
|
||||||
String hsPortConfig = null;
|
Integer HSOnionPort = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.ONION_PORT));
|
||||||
int hsPort = -1;
|
Integer HSAuthCookie = hidden_services.getInt(hidden_services.getColumnIndex(HiddenService.AUTH_COOKIE));
|
||||||
|
String hsDirPath = new File(mHSBasePath.getAbsolutePath(),"hs" + HSLocalPort).getCanonicalPath();
|
||||||
while (st.hasMoreTokens())
|
|
||||||
{
|
debug("Adding hidden service on port: " + HSLocalPort);
|
||||||
try
|
|
||||||
{
|
|
||||||
hsPortConfig = st.nextToken().trim();
|
|
||||||
|
|
||||||
if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed
|
|
||||||
{
|
|
||||||
hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
hsPort = Integer.parseInt(hsPortConfig.split(" ")[0]);
|
|
||||||
|
|
||||||
String hsDirPath = new File(appCacheHome,"hs" + hsPort).getCanonicalPath();
|
|
||||||
|
|
||||||
debug("Adding hidden service on port: " + hsPortConfig);
|
|
||||||
|
|
||||||
extraLines.append("HiddenServiceDir" + ' ' + hsDirPath).append('\n');
|
extraLines.append("HiddenServiceDir" + ' ' + hsDirPath).append('\n');
|
||||||
extraLines.append("HiddenServicePort" + ' ' + hsPortConfig).append('\n');
|
extraLines.append("HiddenServicePort" + ' ' + HSOnionPort + " 127.0.0.1:" + HSLocalPort).append('\n');
|
||||||
|
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
if(HSAuthCookie == 1)
|
||||||
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
extraLines.append("HiddenServiceAuthorizeClient stealth " + HSname).append('\n');
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
|
||||||
}
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
Log.e(OrbotConstants.TAG,"error parsing hsport",e);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hidden_services.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Client Cookies ---- */
|
||||||
|
Cursor client_cookies = mCR.query(COOKIE_CONTENT_URI, cookieProjection, ClientCookie.ENABLED + "=1", null, null);
|
||||||
|
if(client_cookies != null) {
|
||||||
|
try {
|
||||||
|
while (client_cookies.moveToNext()) {
|
||||||
|
String domain = client_cookies.getString(client_cookies.getColumnIndex(ClientCookie.DOMAIN));
|
||||||
|
String cookie = client_cookies.getString(client_cookies.getColumnIndex(ClientCookie.AUTH_COOKIE_VALUE));
|
||||||
|
extraLines.append("HidServAuth" + ' ' + domain + ' ' + cookie).append('\n');
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(OrbotConstants.TAG,"error starting share server",e);
|
||||||
|
}
|
||||||
|
|
||||||
|
client_cookies.close();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,4 +161,6 @@ public interface TorServiceConstants {
|
||||||
"meek_lite 0.0.2.0:3 url=https://az668014.vo.msecnd.net/ front=ajax.aspnetcdn.com"
|
"meek_lite 0.0.2.0:3 url=https://az668014.vo.msecnd.net/ front=ajax.aspnetcdn.com"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static final String HIDDEN_SERVICES_DIR = "hidden_services";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue