summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorParménides GV <parmegv@sdf.org>2014-12-20 12:06:03 +0100
committerParménides GV <parmegv@sdf.org>2014-12-20 12:06:03 +0100
commit5f6cb652dbd2ef0879673cf7338520dc2be236b9 (patch)
treebc47c8ccbcd48bfc5aee6b404c8ef3f1d9a359cb /app/src/main
parent576b0c26baf9b5b1418650a572604325ca7fb3af (diff)
parentf347c871d0433ef3efb85beb5e386b58d700faad (diff)
Merge branch 'develop'
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml24
-rw-r--r--app/src/main/java/de/blinkt/openvpn/VpnProfile.java38
-rw-r--r--app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java8
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java47
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java1
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java7
-rw-r--r--app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java151
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/AboutActivity.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java13
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Dashboard.java400
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EIP.java524
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipFragment.java362
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java424
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java3
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/Provider.java227
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java104
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java234
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderManager.java179
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java57
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java25
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SessionDialog.java (renamed from app/src/main/java/se/leap/bitmaskclient/LogInDialog.java)90
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SessionDialogInterface.java39
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java142
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Constants.java47
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EIP.java251
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java138
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java156
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java46
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java (renamed from app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java)6
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java (renamed from app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java)31
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java60
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java (renamed from app/src/main/java/se/leap/bitmaskclient/VpnConfigGenerator.java)17
-rw-r--r--app/src/main/res/drawable-hdpi/ic_close_white_24dp.pngbin0 -> 324 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_delete_white_24dp.pngbin0 -> 246 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.pngbin0 -> 341 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_edit_white_24dp.pngbin0 -> 339 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.pngbin0 -> 206 bytes
-rw-r--r--app/src/main/res/drawable-hdpi/ic_share_white_24dp.pngbin0 -> 506 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_close_white_24dp.pngbin0 -> 279 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_delete_white_24dp.pngbin0 -> 197 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.pngbin0 -> 276 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_edit_white_24dp.pngbin0 -> 272 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_filter_list_white_24dp.pngbin0 -> 181 bytes
-rw-r--r--app/src/main/res/drawable-mdpi/ic_share_white_24dp.pngbin0 -> 361 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_close_white_24dp.pngbin0 -> 402 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.pngbin0 -> 270 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.pngbin0 -> 379 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_edit_white_24dp.pngbin0 -> 378 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_filter_list_white_24dp.pngbin0 -> 200 bytes
-rw-r--r--app/src/main/res/drawable-xhdpi/ic_share_white_24dp.pngbin0 -> 625 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.pngbin0 -> 492 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.pngbin0 -> 338 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.pngbin0 -> 493 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.pngbin0 -> 490 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_filter_list_white_24dp.pngbin0 -> 223 bytes
-rw-r--r--app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.pngbin0 -> 857 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.pngbin0 -> 662 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.pngbin0 -> 397 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.pngbin0 -> 639 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.pngbin0 -> 632 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_filter_list_white_24dp.pngbin0 -> 254 bytes
-rw-r--r--app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.pngbin0 -> 1115 bytes
-rw-r--r--app/src/main/res/layout-xlarge/dashboard.xml (renamed from app/src/main/res/layout-xlarge/client_dashboard.xml)0
-rw-r--r--app/src/main/res/layout-xlarge/eip_service_fragment.xml35
-rw-r--r--app/src/main/res/layout-xlarge/provider_list_fragment.xml16
-rw-r--r--app/src/main/res/layout-xlarge/session_dialog.xml (renamed from app/src/main/res/layout-xlarge/log_in_dialog.xml)0
-rw-r--r--app/src/main/res/layout/about.xml11
-rw-r--r--app/src/main/res/layout/configuration_wizard_activity.xml7
-rw-r--r--app/src/main/res/layout/dashboard.xml (renamed from app/src/main/res/layout/client_dashboard.xml)1
-rw-r--r--app/src/main/res/layout/eip_service_fragment.xml40
-rw-r--r--app/src/main/res/layout/provider_list_fragment.xml15
-rw-r--r--app/src/main/res/layout/provider_list_item.xml8
-rw-r--r--app/src/main/res/layout/session_dialog.xml (renamed from app/src/main/res/layout/log_in_dialog.xml)3
-rw-r--r--app/src/main/res/menu/logmenu.xml10
-rw-r--r--app/src/main/res/values-v21/refs.xml13
-rw-r--r--app/src/main/res/values-v21/styles.xml15
-rw-r--r--app/src/main/res/values/colours.xml13
-rw-r--r--app/src/main/res/values/refs.xml15
-rw-r--r--app/src/main/res/values/strings.xml2
-rw-r--r--app/src/main/res/values/styles.xml6
-rw-r--r--app/src/main/res/values/untranslatable.xml700
84 files changed, 2782 insertions, 1995 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d5081b8d..5f61cd78 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -17,8 +17,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.leap.bitmaskclient"
- android:versionCode="102"
- android:versionName="0.8.0" >
+ android:versionCode="106"
+ android:versionName="0.8.1" >
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -27,16 +27,17 @@
<uses-sdk
android:minSdkVersion="14"
- android:targetSdkVersion="18" />
+ android:targetSdkVersion="21"/>
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:logo="@drawable/icon"
- android:label="@string/app" >
+ android:label="@string/app"
+ android:theme="@style/appstyle">
<service
- android:name="se.leap.bitmaskclient.VoidVpnService"
+ android:name="se.leap.bitmaskclient.eip.VoidVpnService"
android:permission="android.permission.BIND_VPN_SERVICE">
<intent-filter>
<action android:name="android.net.VpnService" />
@@ -62,9 +63,10 @@
</receiver>
<activity
- android:name="se.leap.bitmaskclient.VoidVpnLauncher" />
+ android:name="se.leap.bitmaskclient.eip.VoidVpnLauncher"
+ android:theme="@android:style/Theme.NoDisplay" />
+
<activity
- android:theme="@android:style/Theme.DeviceDefault.Light.Dialog"
android:name="de.blinkt.openvpn.activities.DisconnectVPN" />
<activity
@@ -99,11 +101,11 @@
android:label="@string/title_about_activity" >
</activity>
- <service android:name="se.leap.bitmaskclient.EIP" android:exported="false">
+ <service android:name="se.leap.bitmaskclient.eip.EIP" android:exported="false">
<intent-filter>
- <action android:name="se.leap.bitmaskclient.UPDATE_EIP_SERVICE"/>
- <action android:name="se.leap.bitmaskclient.START_EIP"/>
- <action android:name="se.leap.bitmaskclient.STOP_EIP"/>
+ <action android:name="se.leap.bitmaskclient.eip.UPDATE_EIP_SERVICE"/>
+ <action android:name="se.leap.bitmaskclient.eip.START_EIP"/>
+ <action android:name="se.leap.bitmaskclient.eip.STOP_EIP"/>
</intent-filter>
</service>
</application>
diff --git a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 65214c4f..fb2ba90d 100644
--- a/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/app/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -45,7 +45,6 @@ import java.util.Collection;
import java.util.Locale;
import java.util.UUID;
import java.util.Vector;
-import java.util.concurrent.Future;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -54,6 +53,7 @@ import javax.crypto.NoSuchPaddingException;
import de.blinkt.openvpn.core.NativeUtils;
import de.blinkt.openvpn.core.OpenVPNService;
+import de.blinkt.openvpn.core.VPNLaunchHelper;
import de.blinkt.openvpn.core.VpnStatus;
import de.blinkt.openvpn.core.X509Utils;
@@ -68,11 +68,8 @@ public class VpnProfile implements Serializable {
public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID";
public static final String INLINE_TAG = "[[INLINE]]";
public static final String DISPLAYNAME_TAG = "[[NAME]]";
- private static final String MININONPIEVPN = "nopievpn";
- private static final String MINIPIEVPN = "pievpn";
private static final long serialVersionUID = 7085688938959334563L;
- private static final String OVPNCONFIGFILE = "android.conf";
public static final int MAXLOGLEVEL = 4;
public static final int CURRENT_PROFILE_VERSION = 2;
public static final int DEFAULT_MSSFIX_SIZE = 1450;
@@ -164,20 +161,6 @@ public class VpnProfile implements Serializable {
mProfileVersion = CURRENT_PROFILE_VERSION;
}
- public static String getMiniVPNExecutableName()
- {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
- return VpnProfile.MINIPIEVPN;
- else
- return VpnProfile.MININONPIEVPN;
- }
-
- public static String[] replacePieWithNoPie(String[] mArgv)
- {
- mArgv[0] = mArgv[0].replace(MINIPIEVPN, MININONPIEVPN);
- return mArgv;
- }
-
public static String openVpnEscape(String unescaped) {
if (unescaped == null)
return null;
@@ -576,19 +559,6 @@ public class VpnProfile implements Serializable {
return parts[0] + " " + netmask;
}
- private String[] buildOpenvpnArgv(File cacheDir) {
- Vector<String> args = new Vector<String>();
-
- // Add fixed paramenters
- //args.add("/data/data/de.blinkt.openvpn/lib/openvpn");
- args.add(cacheDir.getAbsolutePath() + "/" + getMiniVPNExecutableName());
-
- args.add("--config");
- args.add(cacheDir.getAbsolutePath() + "/" + OVPNCONFIGFILE);
-
-
- return args.toArray(new String[args.size()]);
- }
@@ -603,7 +573,7 @@ public class VpnProfile implements Serializable {
try {
- FileWriter cfg = new FileWriter(context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
+ FileWriter cfg = new FileWriter(VPNLaunchHelper.getConfigFilePath(context));
cfg.write(getConfigFile(context, false));
cfg.flush();
cfg.close();
@@ -618,7 +588,7 @@ public class VpnProfile implements Serializable {
String prefix = context.getPackageName();
Intent intent = new Intent(context, OpenVPNService.class);
- intent.putExtra(prefix + ".ARGV", buildOpenvpnArgv(context.getCacheDir()));
+ intent.putExtra(prefix + ".ARGV", VPNLaunchHelper.buildOpenvpnArgv(context));
intent.putExtra(prefix + ".profileUUID", mUuid.toString());
ApplicationInfo info = context.getApplicationInfo();
@@ -648,7 +618,7 @@ public class VpnProfile implements Serializable {
public static boolean isEmbedded(String data) {
if (data==null)
return false;
- if(data.startsWith(INLINE_TAG) || data.startsWith(DISPLAYNAME_TAG))
+ if (data.startsWith(INLINE_TAG) || data.startsWith(DISPLAYNAME_TAG))
return true;
else
return false;
diff --git a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
index e6b73a48..4940d5d6 100644
--- a/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
+++ b/app/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java
@@ -17,7 +17,7 @@ import de.blinkt.openvpn.core.ProfileManager;
/**
* Created by arne on 13.10.13.
*/
-public class DisconnectVPN extends Activity implements DialogInterface.OnClickListener{
+public class DisconnectVPN extends Activity implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
protected OpenVPNService mService;
private ServiceConnection mConnection = new ServiceConnection() {
@@ -71,6 +71,7 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi
builder.setMessage(R.string.cancel_connection_query);
builder.setNegativeButton(android.R.string.no, this);
builder.setPositiveButton(android.R.string.yes,this);
+ builder.setOnCancelListener(this);
builder.show();
}
@@ -84,4 +85,9 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi
}
finish();
}
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ finish();
+ }
}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java b/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java
new file mode 100644
index 00000000..5b1dda58
--- /dev/null
+++ b/app/src/main/java/de/blinkt/openvpn/core/GetRestrictionReceiver.java
@@ -0,0 +1,47 @@
+package de.blinkt.openvpn.core;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.RestrictionEntry;
+import android.os.Build;
+import android.os.Bundle;
+
+import java.util.ArrayList;
+
+import se.leap.bitmaskclient.R;
+
+/**
+ * Created by arne on 25.07.13.
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class GetRestrictionReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ final PendingResult result = goAsync();
+
+ new Thread() {
+ @Override
+ public void run() {
+ final Bundle extras = new Bundle();
+
+ ArrayList<RestrictionEntry> restrictionEntries = initRestrictions(context);
+
+ extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, restrictionEntries);
+ result.setResult(Activity.RESULT_OK,null,extras);
+ result.finish();
+ }
+ }.run();
+ }
+
+ private ArrayList<RestrictionEntry> initRestrictions(Context context) {
+ ArrayList<RestrictionEntry> restrictions = new ArrayList<RestrictionEntry>();
+ RestrictionEntry allowChanges = new RestrictionEntry("allow_changes",false);
+ allowChanges.setTitle(context.getString(R.string.allow_vpn_changes));
+ restrictions.add(allowChanges);
+
+ return restrictions;
+ }
+}
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index ada065ba..d9830955 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -719,6 +719,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public void updateState(String state, String logmessage, int resid, ConnectionStatus level) {
// If the process is not running, ignore any state,
// Notification should be invisible in this state
+
doSendBroadcast(state, level);
if (mProcessThread == null && !mNotificationAlwaysVisible)
return;
diff --git a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
index e3c60854..e36a5b8a 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/OpenVPNThread.java
@@ -5,6 +5,7 @@
package de.blinkt.openvpn.core;
+import android.annotation.SuppressLint;
import android.util.Log;
import java.io.BufferedReader;
@@ -30,6 +31,7 @@ import de.blinkt.openvpn.core.VpnStatus.LogItem;
public class OpenVPNThread implements Runnable {
private static final String DUMP_PATH_STRING = "Dump path: ";
+ @SuppressLint("SdCardPath")
private static final String BROKEN_PIE_SUPPORT = "/data/data/de.blinkt.openvpn/cache/pievpn[1]: syntax error:";
private static final String TAG = "OpenVPN";
public static final int M_FATAL = (1 << 4);
@@ -78,7 +80,8 @@ public class OpenVPNThread implements Runnable {
if( exitvalue != 0) {
VpnStatus.logError("Process exited with exit value " + exitvalue);
if (mBrokenPie) {
- String[] noPieArgv = VpnProfile.replacePieWithNoPie(mArgv);
+ /* This will probably fail since the NoPIE binary is probably not written */
+ String[] noPieArgv = VPNLaunchHelper.replacePieWithNoPie(mArgv);
// We are already noPIE, nothing to gain
if (!noPieArgv.equals(mArgv)) {
@@ -190,7 +193,7 @@ public class OpenVPNThread implements Runnable {
private String genLibraryPath(String[] argv, ProcessBuilder pb) {
// Hack until I find a good way to get the real library path
- String applibpath = argv[0].replace("/cache/" + VpnProfile.getMiniVPNExecutableName() , "/lib");
+ String applibpath = argv[0].replaceFirst("/cache/.*$" , "/lib");
String lbpath = pb.environment().get("LD_LIBRARY_PATH");
if(lbpath==null)
diff --git a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
index 40f9742b..208aa359 100644
--- a/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
+++ b/app/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java
@@ -5,70 +5,122 @@
package de.blinkt.openvpn.core;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ApplicationInfo;
import android.os.Build;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.util.Vector;
import se.leap.bitmaskclient.R;
import de.blinkt.openvpn.VpnProfile;
public class VPNLaunchHelper {
- static private boolean writeMiniVPN(Context context) {
- File mvpnout = new File(context.getCacheDir(),VpnProfile.getMiniVPNExecutableName());
- if (mvpnout.exists() && mvpnout.canExecute())
- return true;
-
- IOException e2 = null;
-
- try {
- InputStream mvpn;
-
- try {
- mvpn = context.getAssets().open(VpnProfile.getMiniVPNExecutableName() + "." + Build.CPU_ABI);
- }
- catch (IOException errabi) {
- VpnStatus.logInfo("Failed getting assets for archicture " + Build.CPU_ABI);
- e2=errabi;
- mvpn = context.getAssets().open(VpnProfile.getMiniVPNExecutableName() + "." + Build.CPU_ABI2);
-
- }
-
-
- FileOutputStream fout = new FileOutputStream(mvpnout);
-
- byte buf[]= new byte[4096];
-
- int lenread = mvpn.read(buf);
- while(lenread> 0) {
- fout.write(buf, 0, lenread);
- lenread = mvpn.read(buf);
- }
- fout.close();
-
- if(!mvpnout.setExecutable(true)) {
- VpnStatus.logError("Failed to make OpenVPN executable");
- return false;
- }
-
-
- return true;
- } catch (IOException e) {
- if(e2!=null)
- VpnStatus.logException(e2);
- VpnStatus.logException(e);
-
- return false;
- }
+ private static final String MININONPIEVPN = "nopievpn";
+ private static final String MINIPIEVPN = "pievpn";
+ private static final String OVPNCONFIGFILE = "android.conf";
+
+
+
+ static private String writeMiniVPN(Context context) {
+ String[] abis;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ abis = getSupportedAbisLollipop();
+ else
+ abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2};
+
+ for (String abi: abis) {
+
+ File mvpnout = new File(context.getCacheDir(), getMiniVPNExecutableName() + "." + abi);
+ if ((mvpnout.exists() && mvpnout.canExecute()) || writeMiniVPNBinary(context, abi, mvpnout)) {
+ return mvpnout.getPath();
+ }
+ }
+
+ return null;
}
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ private static String[] getSupportedAbisLollipop() {
+ return Build.SUPPORTED_ABIS;
+ }
+
+ private static String getMiniVPNExecutableName()
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ return MINIPIEVPN;
+ else
+ return MININONPIEVPN;
+ }
+
+
+ public static String[] replacePieWithNoPie(String[] mArgv)
+ {
+ mArgv[0] = mArgv[0].replace(MINIPIEVPN, MININONPIEVPN);
+ return mArgv;
+ }
+
+
+ public static String[] buildOpenvpnArgv(Context c) {
+ Vector<String> args = new Vector<String>();
+
+ // Add fixed paramenters
+ //args.add("/data/data/de.blinkt.openvpn/lib/openvpn");
+ args.add(writeMiniVPN(c));
+
+ args.add("--config");
+ args.add(c.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE);
+
+
+ return args.toArray(new String[args.size()]);
+ }
+
+ private static boolean writeMiniVPNBinary(Context context, String abi, File mvpnout) {
+ try {
+ InputStream mvpn;
+
+ try {
+ mvpn = context.getAssets().open(getMiniVPNExecutableName() + "." + abi);
+ }
+ catch (IOException errabi) {
+ VpnStatus.logInfo("Failed getting assets for archicture " + abi);
+ return false;
+ }
+
+
+ FileOutputStream fout = new FileOutputStream(mvpnout);
+
+ byte buf[]= new byte[4096];
+
+ int lenread = mvpn.read(buf);
+ while(lenread> 0) {
+ fout.write(buf, 0, lenread);
+ lenread = mvpn.read(buf);
+ }
+ fout.close();
+
+ if(!mvpnout.setExecutable(true)) {
+ VpnStatus.logError("Failed to make OpenVPN executable");
+ return false;
+ }
+
+
+ return true;
+ } catch (IOException e) {
+ VpnStatus.logException(e);
+ return false;
+ }
+
+ }
public static void startOpenVpn(VpnProfile startprofile, Context context) {
- if(!writeMiniVPN(context)) {
+ if(writeMiniVPN(context)==null) {
VpnStatus.logError("Error writing minivpn binary");
return;
}
@@ -80,4 +132,9 @@ public class VPNLaunchHelper {
context.startService(startVPN);
}
+
+ public static String getConfigFilePath(Context context) {
+ return context.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE;
+ }
+
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java b/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
index 6d025422..6c4e517b 100644
--- a/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
+++ b/app/src/main/java/se/leap/bitmaskclient/AboutActivity.java
@@ -1,15 +1,10 @@
package se.leap.bitmaskclient;
import android.app.Activity;
-import android.app.Fragment;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
import android.widget.TextView;
-import se.leap.bitmaskclient.R;
public class AboutActivity extends Activity {
diff --git a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
index c95d0c8b..c0f0b0c3 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ConfigHelper.java
@@ -16,11 +16,15 @@
*/
package se.leap.bitmaskclient;
+import android.util.Base64;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
-import java.lang.IllegalArgumentException;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.KeyStoreException;
@@ -33,13 +37,6 @@ import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.util.Base64;
-
/**
* Stores constants, and implements auxiliary methods used across all LEAP Android classes.
*
diff --git a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
index 364a79af..e32dbdee 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Dashboard.java
@@ -14,18 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
- package se.leap.bitmaskclient;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver;
-import se.leap.bitmaskclient.FragmentManagerEnhanced;
-import se.leap.bitmaskclient.SignUpDialog;
-
-import de.blinkt.openvpn.activities.LogWindow;
+package se.leap.bitmaskclient;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
@@ -41,24 +32,36 @@ import android.os.ResultReceiver;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
-import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
-import android.widget.Toast;
+
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import de.blinkt.openvpn.activities.LogWindow;
+import se.leap.bitmaskclient.eip.Constants;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.eip.EipStatus;
/**
- * The main user facing Activity of LEAP Android, consisting of status, controls,
+ * The main user facing Activity of Bitmask Android, consisting of status, controls,
* and access to preferences.
*
* @author Sean Leonard <meanderingcode@aetherislands.net>
* @author parmegv
*/
-public class Dashboard extends Activity implements LogInDialog.LogInDialogInterface, SignUpDialog.SignUpDialogInterface, Receiver {
+public class Dashboard extends Activity implements SessionDialog.SessionDialogInterface, ProviderAPIResultReceiver.Receiver {
protected static final int CONFIGURE_LEAP = 0;
protected static final int SWITCH_PROVIDER = 1;
+ final public static String TAG = Dashboard.class.getSimpleName();
final public static String SHARED_PREFERENCES = "LEAPPreferences";
final public static String ACTION_QUIT = "quit";
public static final String REQUEST_CODE = "request_code";
@@ -66,98 +69,119 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
public static final String START_ON_BOOT = "dashboard start on boot";
final public static String ON_BOOT = "dashboard on boot";
public static final String APP_VERSION = "bitmask version";
- final public static String TAG = Dashboard.class.getSimpleName();
-
- private EipServiceFragment eipFragment;
- private ProgressBar mProgressBar;
- private TextView eipStatus;
- private static Context app;
- protected static SharedPreferences preferences;
- private static Provider provider;
+ private static Context app;
+ protected static SharedPreferences preferences;
+ private FragmentManagerEnhanced fragment_manager;
- private boolean authed_eip = false;
+ @InjectView(R.id.providerName)
+ TextView provider_name;
+ EipFragment eip_fragment;
+ private Provider provider;
+ private static boolean authed_eip;
public ProviderAPIResultReceiver providerAPI_result_receiver;
- private FragmentManagerEnhanced fragment_manager;
- @Override
+ @Override
+ protected void onSaveInstanceState(@NotNull Bundle outState) {
+ if(provider != null)
+ outState.putParcelable(Provider.KEY, provider);
+ super.onSaveInstanceState(outState);
+ }
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = this;
PRNGFixes.apply();
-
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
fragment_manager = new FragmentManagerEnhanced(getFragmentManager());
handleVersion();
-
- authed_eip = preferences.getBoolean(EIP.AUTHED_EIP, false);
- if (preferences.getString(Provider.KEY, "").isEmpty())
- startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
- else
- buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
+
+ provider = getSavedProvider(savedInstanceState);
+ if (provider == null || provider.getName().isEmpty())
+ startActivityForResult(new Intent(this,ConfigurationWizard.class),CONFIGURE_LEAP);
+ else
+ buildDashboard(getIntent().getBooleanExtra(ON_BOOT, false));
}
+ private Provider getSavedProvider(Bundle savedInstanceState) {
+ Provider provider = null;
+ if(savedInstanceState != null)
+ provider = savedInstanceState.getParcelable(Provider.KEY);
+ else if(preferences.getBoolean(Constants.PROVIDER_CONFIGURED, false))
+ provider = getSavedProviderFromSharedPreferences();
+
+ return provider;
+ }
+
+ private Provider getSavedProviderFromSharedPreferences() {
+ Provider provider = null;
+ try {
+ provider = new Provider(new URL(preferences.getString(Provider.MAIN_URL, "")));
+ provider.define(new JSONObject(preferences.getString(Provider.KEY, "")));
+ } catch (MalformedURLException | JSONException e) {
+ e.printStackTrace();
+ }
+
+ return provider;
+ }
+
+
private void handleVersion() {
try {
int versionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
int lastDetectedVersion = preferences.getInt(APP_VERSION, 0);
- preferences.edit().putInt(APP_VERSION, versionCode);
+ preferences.edit().putInt(APP_VERSION, versionCode).apply();
Log.d("Dashboard", "detected version code: " + versionCode);
Log.d("Dashboard", "last detected version code: " + lastDetectedVersion);
switch(versionCode) {
case 91: // 0.6.0 without Bug #5999
case 101: // 0.8.0
- if(!preferences.getString(EIP.KEY, "").isEmpty()) {
+ if(!preferences.getString(Constants.KEY, "").isEmpty()) {
Intent rebuildVpnProfiles = new Intent(getApplicationContext(), EIP.class);
- rebuildVpnProfiles.setAction(EIP.ACTION_REBUILD_PROFILES);
+ rebuildVpnProfiles.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
startService(rebuildVpnProfiles);
}
break;
}
} catch (NameNotFoundException e) {
+ Log.d(TAG, "Handle version didn't find any " + getPackageName() + " package");
}
}
-
- @Override
- protected void onDestroy() {
-
- super.onDestroy();
- }
-
- protected void onPause() {
- super.onPause();
- }
- @Override
- protected void onActivityResult(int requestCode, int resultCode, final Intent data){
- if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) {
- // It should be equivalent: if ( (requestCode == CONFIGURE_LEAP) || (data!= null && data.hasExtra(STOP_FIRST))) {
- if ( resultCode == RESULT_OK ){
- preferences.edit().putInt(EIP.PARSED_SERIAL, 0).commit();
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
-
- Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
- updateEIP.setAction(EIP.ACTION_UPDATE_EIP_SERVICE);
- startService(updateEIP);
-
- buildDashboard(false);
- invalidateOptionsMenu();
- if(data != null && data.hasExtra(LogInDialog.TAG)) {
- View view = ((ViewGroup)findViewById(android.R.id.content)).getChildAt(0);
- logInDialog(Bundle.EMPTY);
- }
- } else if(resultCode == RESULT_CANCELED && (data == null || data.hasExtra(ACTION_QUIT))) {
- finish();
- } else
- configErrorDialog();
- }
+ @SuppressLint("CommitPrefEdits")
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data){
+ Log.d(TAG, "onActivityResult: requestCode = " + requestCode);
+ if ( requestCode == CONFIGURE_LEAP || requestCode == SWITCH_PROVIDER) {
+ if ( resultCode == RESULT_OK ) {
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
+ updateEipService();
+
+ if (data.hasExtra(Provider.KEY)) {
+ provider = data.getParcelableExtra(Provider.KEY);
+ preferences.edit().putBoolean(Constants.PROVIDER_CONFIGURED, true).commit();
+ preferences.edit().putString(Provider.MAIN_URL, provider.mainUrl().toString()).apply();
+ preferences.edit().putString(Provider.KEY, provider.definition().toString()).apply();
+ }
+ buildDashboard(false);
+ invalidateOptionsMenu();
+ if (data.hasExtra(SessionDialog.TAG)) {
+ logInDialog(Bundle.EMPTY);
+ }
+ } else if (resultCode == RESULT_CANCELED && data.hasExtra(ACTION_QUIT)) {
+ finish();
+ } else
+ configErrorDialog();
+ } else if(requestCode == EIP.DISCONNECT) {
+ EipStatus.getInstance().setConnectedOrDisconnected();
}
+ }
/**
* Dialog shown when encountering a configuration error. Such errors require
@@ -178,7 +202,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
.setNegativeButton(getResources().getString(R.string.setup_error_close_button), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- preferences.edit().remove(Provider.KEY).commit();
+ preferences.edit().remove(Provider.KEY).remove(Constants.PROVIDER_CONFIGURED).apply();
finish();
}
})
@@ -190,30 +214,27 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
* service dependent UI elements to include.
*/
private void buildDashboard(boolean hide_and_turn_on_eip) {
- provider = Provider.getInstance();
- provider.init( this );
-
- setContentView(R.layout.client_dashboard);
-
- TextView providerNameTV = (TextView) findViewById(R.id.providerName);
- providerNameTV.setText(provider.getDomain());
- providerNameTV.setTextSize(28);
-
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
+ setContentView(R.layout.dashboard);
+ ButterKnife.inject(this);
+ provider_name.setText(provider.getDomain());
if ( provider.hasEIP()){
- eipFragment = new EipServiceFragment();
- if (hide_and_turn_on_eip) {
- preferences.edit().remove(Dashboard.START_ON_BOOT).commit();
- Bundle arguments = new Bundle();
- arguments.putBoolean(EipServiceFragment.START_ON_BOOT, true);
- eipFragment.setArguments(arguments);
- }
- fragment_manager.replace(R.id.servicesCollection, eipFragment, EipServiceFragment.TAG);
- if (hide_and_turn_on_eip) {
- onBackPressed();
- }
+ fragment_manager.removePreviousFragment(EipFragment.TAG);
+ eip_fragment = new EipFragment();
+
+ if (hide_and_turn_on_eip) {
+ preferences.edit().remove(Dashboard.START_ON_BOOT).apply();
+ Bundle arguments = new Bundle();
+ arguments.putBoolean(EipFragment.START_ON_BOOT, true);
+ if(eip_fragment != null) eip_fragment.setArguments(arguments);
+ }
+
+ fragment_manager.replace(R.id.servicesCollection, eip_fragment, EipFragment.TAG);
+
+ if (hide_and_turn_on_eip) {
+ onBackPressed();
+ }
}
}
@@ -222,12 +243,12 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
JSONObject provider_json;
try {
String provider_json_string = preferences.getString(Provider.KEY, "");
- if(provider_json_string.isEmpty() == false) {
+ if(!provider_json_string.isEmpty()) {
provider_json = new JSONObject(provider_json_string);
JSONObject service_description = provider_json.getJSONObject(Provider.SERVICE);
boolean authed_eip = !LeapSRPSession.getToken().isEmpty();
boolean allow_registered_eip = service_description.getBoolean(Provider.ALLOW_REGISTRATION);
- preferences.edit().putBoolean(EIP.ALLOWED_REGISTERED, allow_registered_eip);
+ preferences.edit().putBoolean(Constants.ALLOWED_REGISTERED, allow_registered_eip).apply();
if(allow_registered_eip) {
if(authed_eip) {
@@ -267,13 +288,13 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
startActivity(startLW);
return true;
case R.id.switch_provider:
- if (Provider.getInstance().hasEIP()){
- if (preferences.getBoolean(EIP.AUTHED_EIP, false)){
- logOut();
- }
- eipStop();
+ if (provider.hasEIP()){
+ if (preferences.getBoolean(Constants.AUTHED_EIP, false)) {
+ logOut();
+ }
+ eip_fragment.askToStopEIP();
}
- preferences.edit().clear().commit();
+ preferences.edit().clear().apply();
startActivityForResult(new Intent(this,ConfigurationWizard.class), SWITCH_PROVIDER);
return true;
case R.id.login_button:
@@ -291,10 +312,7 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
}
- private Intent prepareProviderAPICommand() {
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- eipStatus = (TextView) findViewById(R.id.eipStatus);
-
+ protected Intent prepareProviderAPICommand() {
providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
providerAPI_result_receiver.setReceiver(this);
@@ -308,12 +326,12 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
* Shows the log in dialog.
*/
public void logInDialog(Bundle resultData) {
- FragmentTransaction transaction = fragment_manager.removePreviousFragment(LogInDialog.TAG);
+ FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
- DialogFragment newFragment = LogInDialog.newInstance();
+ DialogFragment newFragment = SessionDialog.newInstance();
if(resultData != null && !resultData.isEmpty())
newFragment.setArguments(resultData);
- newFragment.show(transaction, LogInDialog.TAG);
+ newFragment.show(transaction, SessionDialog.TAG);
}
@Override
@@ -323,24 +341,20 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
if(parameters == null)
parameters = new Bundle();
- parameters.putString(SessionDialogInterface.USERNAME, username);
- parameters.putString(SessionDialogInterface.PASSWORD, password);
-
- mProgressBar.setVisibility(ProgressBar.VISIBLE);
- eipStatus.setText(R.string.authenticating_message);
+ parameters.putString(SessionDialog.USERNAME, username);
+ parameters.putString(SessionDialog.PASSWORD, password);
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.authenticating_message);
+ }
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
provider_API_command.setAction(ProviderAPI.SRP_AUTH);
startService(provider_API_command);
}
- public void cancelAuthedEipOn() {
- EipServiceFragment eipFragment = (EipServiceFragment) getFragmentManager().findFragmentByTag(EipServiceFragment.TAG);
- eipFragment.checkEipSwitch(false);
- }
-
public void cancelLoginOrSignup() {
- hideProgressBar();
+ EipStatus.getInstance().setConnectedOrDisconnected();
}
/**
@@ -348,12 +362,11 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
*/
public void logOut() {
Intent provider_API_command = prepareProviderAPICommand();
-
- if(mProgressBar == null) mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- mProgressBar.setVisibility(ProgressBar.VISIBLE);
- if(eipStatus == null) eipStatus = (TextView) findViewById(R.id.eipStatus);
- eipStatus.setText(R.string.logout_message);
-
+ if(eip_fragment != null) {
+
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.logout_message);
+ }
provider_API_command.setAction(ProviderAPI.LOG_OUT);
startService(provider_API_command);
}
@@ -362,13 +375,13 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
* Shows the sign up dialog.
*/
public void signUpDialog(Bundle resultData) {
- FragmentTransaction transaction = fragment_manager.removePreviousFragment(SignUpDialog.TAG);
+ FragmentTransaction transaction = fragment_manager.removePreviousFragment(SessionDialog.TAG);
- DialogFragment newFragment = SignUpDialog.newInstance();
+ DialogFragment newFragment = SessionDialog.newInstance();
if(resultData != null && !resultData.isEmpty()) {
newFragment.setArguments(resultData);
}
- newFragment.show(transaction, SignUpDialog.TAG);
+ newFragment.show(transaction, SessionDialog.TAG);
}
@Override
@@ -378,12 +391,12 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
if(parameters == null)
parameters = new Bundle();
- parameters.putString(SessionDialogInterface.USERNAME, username);
- parameters.putString(SessionDialogInterface.PASSWORD, password);
-
- mProgressBar.setVisibility(ProgressBar.VISIBLE);
- eipStatus.setText(R.string.signingup_message);
-
+ parameters.putString(SessionDialog.USERNAME, username);
+ parameters.putString(SessionDialog.PASSWORD, password);
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setVisibility(ProgressBar.VISIBLE);
+ eip_fragment.status_message.setText(R.string.signingup_message);
+ }
provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
provider_API_command.setAction(ProviderAPI.SRP_REGISTER);
startService(provider_API_command);
@@ -410,38 +423,39 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
- if(resultCode == ProviderAPI.SRP_REGISTRATION_SUCCESSFUL) {
- String username = resultData.getString(SessionDialogInterface.USERNAME);
- String password = resultData.getString(SessionDialogInterface.PASSWORD);
+ Log.d(TAG, "onReceiveResult");
+ if(resultCode == ProviderAPI.SUCCESSFUL_SIGNUP) {
+ String username = resultData.getString(SessionDialog.USERNAME);
+ String password = resultData.getString(SessionDialog.PASSWORD);
logIn(username, password);
- } else if(resultCode == ProviderAPI.SRP_REGISTRATION_FAILED) {
+ } else if(resultCode == ProviderAPI.FAILED_SIGNUP) {
changeStatusMessage(resultCode);
hideProgressBar();
signUpDialog(resultData);
- } else if(resultCode == ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL) {
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGIN) {
changeStatusMessage(resultCode);
hideProgressBar();
invalidateOptionsMenu();
authed_eip = true;
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
downloadAuthedUserCertificate();
- } else if(resultCode == ProviderAPI.SRP_AUTHENTICATION_FAILED) {
+ } else if(resultCode == ProviderAPI.FAILED_LOGIN) {
changeStatusMessage(resultCode);
hideProgressBar();
logInDialog(resultData);
- } else if(resultCode == ProviderAPI.LOGOUT_SUCCESSFUL) {
+ } else if(resultCode == ProviderAPI.SUCCESSFUL_LOGOUT) {
changeStatusMessage(resultCode);
hideProgressBar();
invalidateOptionsMenu();
authed_eip = false;
- preferences.edit().putBoolean(EIP.AUTHED_EIP, authed_eip).commit();
+ preferences.edit().putBoolean(Constants.AUTHED_EIP, authed_eip).apply();
} else if(resultCode == ProviderAPI.LOGOUT_FAILED) {
changeStatusMessage(resultCode);
@@ -453,46 +467,55 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
hideProgressBar();
setResult(RESULT_OK);
- Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
- ResultReceiver eip_receiver = new ResultReceiver(new Handler()){
- protected void onReceiveResult(int resultCode, Bundle resultData){
- super.onReceiveResult(resultCode, resultData);
- String request = resultData.getString(EIP.REQUEST_TAG);
- if (resultCode == Activity.RESULT_OK){
- if(authed_eip)
- eipStart();
- else
- eipStatus.setText("Certificate updated");
- }
- }
- };
- updateEIP.putExtra(EIP.RECEIVER_TAG, eip_receiver);
- updateEIP.setAction(EIP.ACTION_UPDATE_EIP_SERVICE);
- startService(updateEIP);
+
+ updateEipService();
} else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE) {
changeStatusMessage(resultCode);
hideProgressBar();
setResult(RESULT_CANCELED);
}
+ else if(resultCode == ProviderAPI.CORRECTLY_DOWNLOADED_EIP_SERVICE) {
+ setResult(RESULT_OK);
+
+ updateEipService();
+ } else if(resultCode == ProviderAPI.INCORRECTLY_DOWNLOADED_EIP_SERVICE) {
+ setResult(RESULT_CANCELED);
+ }
}
+ private void updateEipService() {
+ Intent updateEIP = new Intent(getApplicationContext(), EIP.class);
+ updateEIP.setAction(Constants.ACTION_UPDATE_EIP_SERVICE);
+ ResultReceiver receiver = new ResultReceiver(new Handler()) {
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ String request = resultData.getString(Constants.REQUEST_TAG);
+ if(request.equalsIgnoreCase(Constants.ACTION_UPDATE_EIP_SERVICE)) {
+ if(resultCode == Activity.RESULT_OK) {
+ if(authed_eip && eip_fragment != null) eip_fragment.startEipFromScratch();
+ }
+ }
+ }
+ };
+ //updateEIP.putExtra(Constants.RECEIVER_TAG, receiver);
+ startService(updateEIP);
+ }
+
private void changeStatusMessage(final int previous_result_code) {
// TODO Auto-generated method stub
ResultReceiver eip_status_receiver = new ResultReceiver(new Handler()){
protected void onReceiveResult(int resultCode, Bundle resultData){
super.onReceiveResult(resultCode, resultData);
- String request = resultData.getString(EIP.REQUEST_TAG);
- if(eipStatus == null) eipStatus = (TextView) findViewById(R.id.eipStatus);
- if (request.equalsIgnoreCase(EIP.ACTION_IS_EIP_RUNNING)){
+ String request = resultData.getString(Constants.REQUEST_TAG);
+ if (request.equalsIgnoreCase(Constants.ACTION_IS_EIP_RUNNING)){
if (resultCode == Activity.RESULT_OK){
switch(previous_result_code){
- case ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL: eipStatus.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.SRP_AUTHENTICATION_FAILED: eipStatus.setText(R.string.authentication_failed_message); break;
- case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.authed_secured_status); break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eipStatus.setText(R.string.log_out_failed_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
+ case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.authed_secured_status); break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
}
}
@@ -500,13 +523,13 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
switch(previous_result_code){
- case ProviderAPI.SRP_AUTHENTICATION_SUCCESSFUL: eipStatus.setText(R.string.succesful_authentication_message); break;
- case ProviderAPI.SRP_AUTHENTICATION_FAILED: eipStatus.setText(R.string.authentication_failed_message); break;
- case ProviderAPI.SRP_REGISTRATION_FAILED: eipStatus.setText(R.string.registration_failed_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGIN: eip_fragment.status_message.setText(R.string.succesful_authentication_message); break;
+ case ProviderAPI.FAILED_LOGIN: eip_fragment.status_message.setText(R.string.authentication_failed_message); break;
+ case ProviderAPI.FAILED_SIGNUP: eip_fragment.status_message.setText(R.string.registration_failed_message); break;
case ProviderAPI.CORRECTLY_DOWNLOADED_CERTIFICATE: break;
- case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eipStatus.setText(R.string.incorrectly_downloaded_certificate_message); break;
- case ProviderAPI.LOGOUT_SUCCESSFUL: eipStatus.setText(R.string.logged_out_message); break;
- case ProviderAPI.LOGOUT_FAILED: eipStatus.setText(R.string.log_out_failed_message); break;
+ case ProviderAPI.INCORRECTLY_DOWNLOADED_CERTIFICATE: eip_fragment.status_message.setText(R.string.incorrectly_downloaded_certificate_message); break;
+ case ProviderAPI.SUCCESSFUL_LOGOUT: eip_fragment.status_message.setText(R.string.logged_out_message); break;
+ case ProviderAPI.LOGOUT_FAILED: eip_fragment.status_message.setText(R.string.log_out_failed_message); break;
}
}
}
@@ -517,11 +540,10 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
}
private void hideProgressBar() {
- if(mProgressBar == null)
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
-
- mProgressBar.setProgress(0);
- mProgressBar.setVisibility(ProgressBar.GONE);
+ if(eip_fragment != null) {
+ eip_fragment.progress_bar.setProgress(0);
+ eip_fragment.progress_bar.setVisibility(ProgressBar.GONE);
+ }
}
/**
@@ -544,24 +566,8 @@ public class Dashboard extends Activity implements LogInDialog.LogInDialogInterf
private void eipIsRunning(ResultReceiver eip_receiver){
// TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
Intent eip_intent = new Intent(this, EIP.class);
- eip_intent.setAction(EIP.ACTION_IS_EIP_RUNNING);
- eip_intent.putExtra(EIP.RECEIVER_TAG, eip_receiver);
+ eip_intent.setAction(Constants.ACTION_IS_EIP_RUNNING);
+ eip_intent.putExtra(Constants.RECEIVER_TAG, eip_receiver);
startService(eip_intent);
}
-
- private void eipStop(){
- EipServiceFragment eipFragment = (EipServiceFragment) getFragmentManager().findFragmentByTag(EipServiceFragment.TAG);
- eipFragment.stopEIP();
- }
-
- private void eipStart() {
- EipServiceFragment eipFragment = (EipServiceFragment) getFragmentManager().findFragmentByTag(EipServiceFragment.TAG);
- eipFragment.startEipFromScratch();
- }
-
- protected void showProgressBar() {
- if(mProgressBar == null)
- mProgressBar = (ProgressBar) findViewById(R.id.eipProgress);
- mProgressBar.setVisibility(ProgressBar.VISIBLE);
- }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
index f78002b0..a44253c6 100644
--- a/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/DownloadFailedDialog.java
@@ -16,9 +16,6 @@
*/
package se.leap.bitmaskclient;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.NewProviderDialog.NewProviderDialogInterface;
-import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
diff --git a/app/src/main/java/se/leap/bitmaskclient/EIP.java b/app/src/main/java/se/leap/bitmaskclient/EIP.java
deleted file mode 100644
index 2f06def3..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/EIP.java
+++ /dev/null
@@ -1,524 +0,0 @@
-/**
- * Copyright (c) 2013 LEAP Encryption Access Project and contributers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package se.leap.bitmaskclient;
-
-import android.app.Activity;
-import android.app.IntentService;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.ResultReceiver;
-import android.util.Log;
-import de.blinkt.openvpn.LaunchVPN;
-import de.blinkt.openvpn.VpnProfile;
-import de.blinkt.openvpn.activities.DisconnectVPN;
-import de.blinkt.openvpn.core.ConfigParser;
-import de.blinkt.openvpn.core.ConfigParser.ConfigParseError;
-import de.blinkt.openvpn.core.ProfileManager;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
-import java.io.IOException;
-import java.io.StringReader;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.NoSuchElementException;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.Vector;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import se.leap.bitmaskclient.Dashboard;
-import se.leap.bitmaskclient.Provider;
-import se.leap.bitmaskclient.R;
-
-/**
- * EIP is the abstract base class for interacting with and managing the Encrypted
- * Internet Proxy connection. Connections are started, stopped, and queried through
- * this IntentService.
- * Contains logic for parsing eip-service.json from the provider, configuring and selecting
- * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- * @author Parménides GV <parmegv@sdf.org>
- */
-public final class EIP extends IntentService {
-
- public final static String AUTHED_EIP = "authed eip";
- public final static String ACTION_CHECK_CERT_VALIDITY = "se.leap.bitmaskclient.CHECK_CERT_VALIDITY";
- public final static String ACTION_START_EIP = "se.leap.bitmaskclient.START_EIP";
- public final static String ACTION_STOP_EIP = "se.leap.bitmaskclient.STOP_EIP";
- public final static String ACTION_UPDATE_EIP_SERVICE = "se.leap.bitmaskclient.UPDATE_EIP_SERVICE";
- public final static String ACTION_IS_EIP_RUNNING = "se.leap.bitmaskclient.IS_RUNNING";
- public final static String ACTION_REBUILD_PROFILES = "se.leap.bitmaskclient.REBUILD_PROFILES";
- public final static String EIP_NOTIFICATION = "EIP_NOTIFICATION";
- public final static String STATUS = "eip status";
- public final static String DATE_FROM_CERTIFICATE = "date from certificate";
- public final static String ALLOWED_ANON = "allow_anonymous";
- public final static String ALLOWED_REGISTERED = "allow_registration";
- public final static String CERTIFICATE = "cert";
- public final static String PRIVATE_KEY = "private_key";
- public final static String KEY = "eip";
- public final static String PARSED_SERIAL = "eip_parsed_serial";
- public final static String SERVICE_API_PATH = "config/eip-service.json";
- public final static String RECEIVER_TAG = "receiverTag";
- public final static String REQUEST_TAG = "requestTag";
- public final static String TAG = EIP.class.getSimpleName();
- private static SharedPreferences preferences;
-
- private static Context context;
- private static ResultReceiver mReceiver;
- private static boolean mBound = false;
-
- private static JSONObject eipDefinition = null;
-
- private static OVPNGateway activeGateway = null;
-
- protected static ConnectionStatus lastConnectionStatusLevel;
- protected static boolean mIsDisconnecting = false;
- protected static boolean mIsStarting = false;
-
- public static SimpleDateFormat certificate_date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US);
- public EIP(){
- super("LEAPEIP");
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- context = getApplicationContext();
-
- preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
- }
-
- @Override
- public void onDestroy() {
-
- mBound = false;
-
- super.onDestroy();
- }
-
-
- @Override
- protected void onHandleIntent(Intent intent) {
- String action = intent.getAction();
- mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
-
- if ( action == ACTION_START_EIP )
- startEIP();
- else if ( action == ACTION_STOP_EIP )
- stopEIP();
- else if ( action == ACTION_IS_EIP_RUNNING )
- isRunning();
- else if ( action == ACTION_UPDATE_EIP_SERVICE )
- updateEIPService();
- else if ( action == ACTION_CHECK_CERT_VALIDITY )
- checkCertValidity();
- else if ( action == ACTION_REBUILD_PROFILES )
- updateGateways();
- }
-
- /**
- * Initiates an EIP connection by selecting a gateway and preparing and sending an
- * Intent to {@link se.leap.openvpn.LaunchVPN}.
- * It also sets up early routes.
- */
- private void startEIP() {
- earlyRoutes();
- activeGateway = selectGateway();
-
- if(activeGateway != null && activeGateway.mVpnProfile != null) {
- mReceiver = EipServiceFragment.getReceiver();
- launchActiveGateway();
- }
- }
-
- /**
- * Early routes are routes that block traffic until a new
- * VpnService is started properly.
- */
- private void earlyRoutes() {
- Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class);
- void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(void_vpn_launcher);
- }
-
- /**
- * Choose a gateway to connect to based on timezone from system locale data
- *
- * @return The gateway to connect to
- */
- private OVPNGateway selectGateway() {
- String closest_location = closestGateway();
- String chosen_host = chooseHost(closest_location);
-
- return new OVPNGateway(chosen_host);
- }
-
- private String closestGateway() {
- TreeMap<Integer, Set<String>> offsets = calculateOffsets();
- return offsets.isEmpty() ? "" : offsets.firstEntry().getValue().iterator().next();
- }
-
- private TreeMap<Integer, Set<String>> calculateOffsets() {
- TreeMap<Integer, Set<String>> offsets = new TreeMap<Integer, Set<String>>();
-
- int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
-
- JSONObject locations = availableLocations();
- Iterator<String> locations_names = locations.keys();
- while(locations_names.hasNext()) {
- try {
- String location_name = locations_names.next();
- JSONObject location = locations.getJSONObject(location_name);
-
- int dist = timezoneDistance(localOffset, location.optInt("timezone"));
-
- Set<String> set = (offsets.get(dist) != null) ?
- offsets.get(dist) : new HashSet<String>();
-
- set.add(location_name);
- offsets.put(dist, set);
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- return offsets;
- }
-
- private JSONObject availableLocations() {
- JSONObject locations = null;
- try {
- if(eipDefinition == null) updateEIPService();
- locations = eipDefinition.getJSONObject("locations");
- } catch (JSONException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
-
- return locations;
- }
-
- private int timezoneDistance(int local_timezone, int remote_timezone) {
- // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
- int dist = Math.abs(local_timezone - remote_timezone);
-
- // Farther than 12 timezones and it's shorter around the "back"
- if (dist > 12)
- dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
-
- return dist;
- }
-
- private String chooseHost(String location) {
- String chosen_host = "";
- try {
- JSONArray gateways = eipDefinition.getJSONArray("gateways");
- for (int i = 0; i < gateways.length(); i++) {
- JSONObject gw = gateways.getJSONObject(i);
- if ( gw.getString("location").equalsIgnoreCase(location) || location.isEmpty()){
- chosen_host = eipDefinition.getJSONObject("locations").getJSONObject(gw.getString("location")).getString("name");
- break;
- }
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return chosen_host;
- }
-
- private void launchActiveGateway() {
- Intent intent = new Intent(this,LaunchVPN.class);
- intent.setAction(Intent.ACTION_MAIN);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(LaunchVPN.EXTRA_KEY, activeGateway.mVpnProfile.getUUID().toString() );
- intent.putExtra(LaunchVPN.EXTRA_NAME, activeGateway.mVpnProfile.getName() );
- intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
- intent.putExtra(RECEIVER_TAG, mReceiver);
- startActivity(intent);
- }
-
- /**
- * Disconnects the EIP connection gracefully through the bound service or forcefully
- * if there is no bound service. Sends a message to the requesting ResultReceiver.
- */
- private void stopEIP() {
- if(isConnected()) {
- Intent disconnect_vpn = new Intent(this, DisconnectVPN.class);
- disconnect_vpn.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(disconnect_vpn);
- mIsDisconnecting = true;
- lastConnectionStatusLevel = ConnectionStatus.UNKNOWN_LEVEL; // Wait for the decision of the user
- Log.d(TAG, "mIsDisconnecting = true");
- }
-
- tellToReceiver(ACTION_STOP_EIP, Activity.RESULT_OK);
- }
-
- private void tellToReceiver(String action, int resultCode) {
- if (mReceiver != null){
- Bundle resultData = new Bundle();
- resultData.putString(REQUEST_TAG, action);
- mReceiver.send(resultCode, resultData);
- }
- }
-
- /**
- * Checks the last stored status notified by ics-openvpn
- * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
- * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
- */
-
- private void isRunning() {
- int resultCode = Activity.RESULT_CANCELED;
- boolean is_connected = isConnected();
-
- resultCode = (is_connected) ? Activity.RESULT_OK : Activity.RESULT_CANCELED;
-
- tellToReceiver(ACTION_IS_EIP_RUNNING, resultCode);
- }
-
- protected static boolean isConnected() {
- return lastConnectionStatusLevel != null && lastConnectionStatusLevel.equals(ConnectionStatus.LEVEL_CONNECTED) && !mIsDisconnecting;
- }
-
- /**
- * Loads eip-service.json from SharedPreferences and calls {@link updateGateways()}
- * to parse gateway definitions.
- * TODO Implement API call to refresh eip-service.json from the provider
- */
- private void updateEIPService() {
- try {
- String eip_definition_string = preferences.getString(KEY, "");
- if(eip_definition_string.isEmpty() == false) {
- eipDefinition = new JSONObject(eip_definition_string);
- }
- deleteAllVpnProfiles();
- updateGateways();
- if(mReceiver != null) mReceiver.send(Activity.RESULT_OK, Bundle.EMPTY);
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- private void deleteAllVpnProfiles() {
- ProfileManager vpl = ProfileManager.getInstance(context);
- Collection<VpnProfile> profiles = vpl.getProfiles();
- profiles.removeAll(profiles);
- }
-
- /**
- * Walk the list of gateways defined in eip-service.json and parse them into
- * OVPNGateway objects.
- * TODO Store the OVPNGateways (as Serializable) in SharedPreferences
- */
- private void updateGateways(){
- JSONArray gatewaysDefined = null;
- try {
- if(eipDefinition == null) updateEIPService();
- gatewaysDefined = eipDefinition.getJSONArray("gateways");
- for ( int i=0 ; i < gatewaysDefined.length(); i++ ){
- JSONObject gw = null;
- gw = gatewaysDefined.getJSONObject(i);
-
- if ( gw.getJSONObject("capabilities").getJSONArray("transport").toString().contains("openvpn") )
- new OVPNGateway(gw);
- }
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- preferences.edit().putInt(PARSED_SERIAL, eipDefinition.optInt(Provider.API_RETURN_SERIAL)).commit();
- }
-
- private void checkCertValidity() {
- String certificate = preferences.getString(CERTIFICATE, "");
- checkCertValidity(certificate);
- }
-
- private void checkCertValidity(String certificate_string) {
- if(!certificate_string.isEmpty()) {
- X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificate_string);
-
- Calendar offset_date = calculateOffsetCertificateValidity(certificate);
- Bundle result = new Bundle();
- result.putString(REQUEST_TAG, ACTION_CHECK_CERT_VALIDITY);
- try {
- Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
- certificate.checkValidity(offset_date.getTime());
- mReceiver.send(Activity.RESULT_OK, result);
- Log.d(TAG, "Valid certificate");
- } catch(CertificateExpiredException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result);
- Log.d(TAG, "Updating certificate");
- } catch(CertificateNotYetValidException e) {
- mReceiver.send(Activity.RESULT_CANCELED, result);
- }
- }
- }
-
- private Calendar calculateOffsetCertificateValidity(X509Certificate certificate) {
- String current_date = certificate_date_format.format(Calendar.getInstance().getTime()).toString();
-
- String date_string = preferences.getString(DATE_FROM_CERTIFICATE, current_date);
-
- Calendar offset_date = Calendar.getInstance();
- try {
- Date date = certificate_date_format.parse(date_string);
- long difference = Math.abs(date.getTime() - certificate.getNotAfter().getTime())/2;
- long current_date_millis = offset_date.getTimeInMillis();
- offset_date.setTimeInMillis(current_date_millis + difference);
- Log.d(TAG, "certificate not after = " + certificate.getNotAfter());
- } catch(ParseException e) {
- e.printStackTrace();
- }
-
- return offset_date;
- }
-
- /**
- * OVPNGateway provides objects defining gateways and their options and metadata.
- * Each instance contains a VpnProfile for OpenVPN specific data and member
- * variables describing capabilities and location
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- */
- private class OVPNGateway {
-
- private String TAG = "OVPNGateway";
-
- private String mName;
- private VpnProfile mVpnProfile;
- private JSONObject mGateway;
- private HashMap<String,Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>();
-
-
- /**
- * Attempts to retrieve a VpnProfile by name and build an OVPNGateway around it.
- * FIXME This needs to become a findGatewayByName() method
- *
- * @param name The hostname of the gateway to inflate
- */
- private OVPNGateway(String name){
- mName = name;
-
- this.loadVpnProfile();
- }
-
- private void loadVpnProfile() {
- ProfileManager vpl = ProfileManager.getInstance(context);
- try {
- if ( mName == null )
- mVpnProfile = vpl.getProfiles().iterator().next();
- else
- mVpnProfile = vpl.getProfileByName(mName);
- } catch (NoSuchElementException e) {
- updateEIPService();
- this.loadVpnProfile(); // FIXME catch infinite loops
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
- * and create a VpnProfile belonging to it.
- *
- * @param gateway The JSON OpenVPN gateway definition to parse
- */
- protected OVPNGateway(JSONObject gateway){
-
- mGateway = gateway;
-
- // Currently deletes VpnProfile for host, if there already is one, and builds new
- ProfileManager vpl = ProfileManager.getInstance(context);
- Collection<VpnProfile> profiles = vpl.getProfiles();
- for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
- VpnProfile p = it.next();
-
- if ( p.mName.equalsIgnoreCase( mName ) ) {
- it.remove();
- vpl.removeProfile(context, p);
- }
- }
-
- this.createVPNProfile();
-
- vpl.addProfile(mVpnProfile);
- vpl.saveProfile(context, mVpnProfile);
- vpl.saveProfileList(context);
- }
-
- /**
- * Create and attach the VpnProfile to our gateway object
- */
- protected void createVPNProfile(){
- try {
- ConfigParser cp = new ConfigParser();
-
- JSONObject openvpn_configuration = eipDefinition.getJSONObject("openvpn_configuration");
- VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, openvpn_configuration, mGateway);
- String configuration = vpn_configuration_generator.generate();
-
- cp.parseConfig(new StringReader(configuration));
- mVpnProfile = cp.convertProfile();
- mVpnProfile.mName = mName = locationAsName();
- Log.v(TAG,"Created VPNProfile");
-
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ConfigParseError e) {
- // FIXME We didn't get a VpnProfile! Error handling! and log level
- Log.v(TAG,"Error creating VPNProfile");
- e.printStackTrace();
- } catch (IOException e) {
- // FIXME We didn't get a VpnProfile! Error handling! and log level
- Log.v(TAG,"Error creating VPNProfile");
- e.printStackTrace();
- }
- }
-
-
- public String locationAsName() {
- try {
- return eipDefinition.getJSONObject("locations").getJSONObject(mGateway.getString("location")).getString("name");
- } catch (JSONException e) {
- Log.v(TAG,"Couldn't read gateway name for profile creation! Returning original name = " + mName);
- e.printStackTrace();
- return (mName != null) ? mName : "";
- }
- }
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
new file mode 100644
index 00000000..420da7a1
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/EipFragment.java
@@ -0,0 +1,362 @@
+package se.leap.bitmaskclient;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.ResultReceiver;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import java.util.Observable;
+import java.util.Observer;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import butterknife.OnCheckedChanged;
+import de.blinkt.openvpn.activities.DisconnectVPN;
+import se.leap.bitmaskclient.eip.Constants;
+import se.leap.bitmaskclient.eip.EIP;
+import se.leap.bitmaskclient.eip.EipStatus;
+import se.leap.bitmaskclient.eip.VoidVpnService;
+
+public class EipFragment extends Fragment implements Observer {
+
+ public static String TAG = EipFragment.class.getSimpleName();
+
+ protected static final String IS_PENDING = TAG + ".is_pending";
+ protected static final String IS_CONNECTED = TAG + ".is_connected";
+ protected static final String STATUS_MESSAGE = TAG + ".status_message";
+ public static final String START_ON_BOOT = "start on boot";
+
+ private View view;
+ @InjectView(R.id.eipSwitch)
+ Switch eip_switch;
+ @InjectView(R.id.status_message)
+ TextView status_message;
+ @InjectView(R.id.eipProgress)
+ ProgressBar progress_bar;
+
+ private static Activity parent_activity;
+ private static EIPReceiver mEIPReceiver;
+ private static EipStatus eip_status;
+ private boolean is_starting_to_connect;
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ parent_activity = activity;
+
+ Dashboard dashboard = (Dashboard) parent_activity;
+ Intent provider_API_command = dashboard.prepareProviderAPICommand();
+ provider_API_command.setAction(ProviderAPI.DOWNLOAD_EIP_SERVICE);
+ parent_activity.startService(provider_API_command);
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ eip_status = EipStatus.getInstance();
+ eip_status.addObserver(this);
+ mEIPReceiver = new EIPReceiver(new Handler());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ view = inflater.inflate(R.layout.eip_service_fragment, container, false);
+ ButterKnife.inject(this, view);
+
+ if (eip_status.isConnecting())
+ eip_switch.setVisibility(View.VISIBLE);
+
+ Log.d(TAG, "onCreateView, eip_switch is checked? " + eip_switch.isChecked());
+
+ Bundle arguments = getArguments();
+ if(arguments != null && arguments.containsKey(START_ON_BOOT) && arguments.getBoolean(START_ON_BOOT))
+ startEipFromScratch();
+
+ if (savedInstanceState != null) {
+ status_message.setText(savedInstanceState.getString(STATUS_MESSAGE));
+ if(savedInstanceState.getBoolean(IS_PENDING))
+ eip_status.setConnecting();
+ else if(savedInstanceState.getBoolean(IS_CONNECTED)) {
+ eip_status.setConnectedOrDisconnected();
+ }
+ }
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ eipCommand(Constants.ACTION_CHECK_CERT_VALIDITY);
+ handleNewState(eip_status);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putBoolean(IS_PENDING, eip_status.isConnecting());
+ outState.putBoolean(IS_CONNECTED, eip_status.isConnected());
+ Log.d(TAG, "status message onSaveInstanceState = " + status_message.getText().toString());
+ outState.putString(STATUS_MESSAGE, status_message.getText().toString());
+ super.onSaveInstanceState(outState);
+ }
+
+ protected void saveEipStatus() {
+ boolean eip_is_on = false;
+ Log.d(TAG, "saveEipStatus");
+ if(eip_switch.isChecked()) {
+ eip_is_on = true;
+ }
+
+ if(parent_activity != null)
+ Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
+ }
+
+ @OnCheckedChanged(R.id.eipSwitch)
+ void handleSwitch(boolean isChecked) {
+ if(isChecked)
+ handleSwitchOn();
+ else
+ handleSwitchOff();
+
+ saveEipStatus();
+ }
+
+ private void handleSwitchOn() {
+ if(canStartEIP())
+ startEipFromScratch();
+ else if(canLogInToStartEIP()) {
+ Log.d(TAG, "Can Log In to start EIP");
+ Dashboard dashboard = (Dashboard) parent_activity;
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(IS_PENDING, true);
+ dashboard.logInDialog(bundle);
+ }
+ }
+
+ private boolean canStartEIP() {
+ boolean certificateExists = !Dashboard.preferences.getString(Constants.CERTIFICATE, "").isEmpty();
+ boolean isAllowedAnon = Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false);
+ return (isAllowedAnon || certificateExists) && !eip_status.isConnected() && !eip_status.isConnecting();
+ }
+
+ private boolean canLogInToStartEIP() {
+ boolean isAllowedRegistered = Dashboard.preferences.getBoolean(Constants.ALLOWED_REGISTERED, false);
+ boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty();
+ Log.d(TAG, "Allow registered? " + isAllowedRegistered);
+ Log.d(TAG, "Is logged in? " + isLoggedIn);
+ return isAllowedRegistered && !isLoggedIn && !eip_status.isConnecting() && !eip_status.isConnected();
+ }
+
+ private void handleSwitchOff() {
+ if(eip_status.isConnecting()) {
+ askPendingStartCancellation();
+ } else if(eip_status.isConnected()) {
+ askToStopEIP();
+ }
+ }
+
+ private void askPendingStartCancellation() {
+ AlertDialog.Builder alertBuilder = new AlertDialog.Builder(parent_activity);
+ alertBuilder.setTitle(parent_activity.getString(R.string.eip_cancel_connect_title))
+ .setMessage(parent_activity.getString(R.string.eip_cancel_connect_text))
+ .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ askToStopEIP();
+ }
+ })
+ .setNegativeButton(parent_activity.getString(R.string.no), new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ eip_switch.setChecked(true);
+ }
+ })
+ .show();
+ }
+
+ public void startEipFromScratch() {
+ is_starting_to_connect = true;
+ progress_bar.setVisibility(View.VISIBLE);
+ eip_switch.setVisibility(View.VISIBLE);
+ String status = parent_activity.getString(R.string.eip_status_start_pending);
+ status_message.setText(status);
+
+ if(!eip_switch.isChecked()) {
+ eip_switch.setChecked(true);
+ saveEipStatus();
+ }
+ eipCommand(Constants.ACTION_START_EIP);
+ }
+
+ private void stopEIP() {
+ if(eip_status.isConnecting())
+ VoidVpnService.stop();
+ Intent disconnect_vpn = new Intent(parent_activity, DisconnectVPN.class);
+ parent_activity.startActivityForResult(disconnect_vpn, EIP.DISCONNECT);
+ eip_status.setDisconnecting();
+ }
+
+ protected void askToStopEIP() {
+ hideProgressBar();
+
+ String status = parent_activity.getString(R.string.eip_state_not_connected);
+ status_message.setText(status);
+ eipCommand(Constants.ACTION_STOP_EIP);
+ }
+
+ /**
+ * Send a command to EIP
+ *
+ * @param action A valid String constant from EIP class representing an Intent
+ * filter for the EIP class
+ */
+ private void eipCommand(String action){
+ // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
+ Intent vpn_intent = new Intent(parent_activity.getApplicationContext(), EIP.class);
+ vpn_intent.setAction(action);
+ vpn_intent.putExtra(Constants.RECEIVER_TAG, mEIPReceiver);
+ parent_activity.startService(vpn_intent);
+ }
+
+ @Override
+ public void update (Observable observable, Object data) {
+ if(observable instanceof EipStatus) {
+ eip_status = (EipStatus) observable;
+ final EipStatus eip_status = (EipStatus) observable;
+ parent_activity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ handleNewState(eip_status);
+ }
+ });
+ }
+ }
+
+ private void handleNewState(EipStatus eip_status) {
+ Log.d(TAG, "handleNewState: " + eip_status.toString());
+ if(eip_status.wantsToDisconnect())
+ setDisconnectedUI();
+ else if(eip_status.isConnecting() || is_starting_to_connect)
+ setInProgressUI(eip_status);
+ else if (eip_status.isConnected())
+ setConnectedUI();
+ else if (eip_status.isDisconnected() && !eip_status.isConnecting())
+ setDisconnectedUI();
+ }
+
+ private void setConnectedUI() {
+ hideProgressBar();
+ Log.d(TAG, "setConnectedUi? " + eip_status.isConnected());
+ adjustSwitch();
+ is_starting_to_connect = false;
+ status_message.setText(parent_activity.getString(R.string.eip_state_connected));
+ }
+
+ private void setDisconnectedUI(){
+ hideProgressBar();
+ adjustSwitch();
+ status_message.setText(parent_activity.getString(R.string.eip_state_not_connected));
+ }
+
+ private void adjustSwitch() {
+ if(eip_status.isConnected() || eip_status.isConnecting() || is_starting_to_connect) {
+ Log.d(TAG, "adjustSwitch, isConnected || isConnecting, is checked");
+ if(!eip_switch.isChecked()) {
+ eip_switch.setChecked(true);
+ }
+ } else {
+ Log.d(TAG, "adjustSwitch, !isConnected && !isConnecting? " + eip_status.toString());
+
+ if(eip_switch.isChecked()) {
+ eip_switch.setChecked(false);
+ }
+ }
+ }
+
+ private void setInProgressUI(EipStatus eip_status) {
+ int localizedResId = eip_status.getLocalizedResId();
+ String logmessage = eip_status.getLogMessage();
+ String prefix = parent_activity.getString(localizedResId);
+
+ status_message.setText(prefix + " " + logmessage);
+ is_starting_to_connect = false;
+ adjustSwitch();
+ }
+
+ private void hideProgressBar() {
+ if(progress_bar != null)
+ progress_bar.setVisibility(View.GONE);
+ }
+
+ protected class EIPReceiver extends ResultReceiver {
+
+ protected EIPReceiver(Handler handler){
+ super(handler);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ super.onReceiveResult(resultCode, resultData);
+
+ String request = resultData.getString(Constants.REQUEST_TAG);
+
+ if (request.equals(Constants.ACTION_START_EIP)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+
+ break;
+ }
+ } else if (request.equals(Constants.ACTION_STOP_EIP)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ stopEIP();
+ break;
+ case Activity.RESULT_CANCELED:
+ break;
+ }
+ } else if (request.equals(Constants.EIP_NOTIFICATION)) {
+ switch (resultCode){
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+ break;
+ }
+ } else if (request.equals(Constants.ACTION_CHECK_CERT_VALIDITY)) {
+ switch (resultCode) {
+ case Activity.RESULT_OK:
+ break;
+ case Activity.RESULT_CANCELED:
+ Dashboard dashboard = (Dashboard) parent_activity;
+
+ progress_bar.setVisibility(View.VISIBLE);
+ status_message.setText(getString(R.string.updating_certificate_message));
+ if(LeapSRPSession.getToken().isEmpty() && !Dashboard.preferences.getBoolean(Constants.ALLOWED_ANON, false)) {
+ dashboard.logInDialog(Bundle.EMPTY);
+ } else {
+ Intent provider_API_command = dashboard.prepareProviderAPICommand();
+ provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
+ parent_activity.startService(provider_API_command);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+
+ public static EIPReceiver getReceiver() {
+ return mEIPReceiver;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java b/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
deleted file mode 100644
index 6d223dd6..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/EipServiceFragment.java
+++ /dev/null
@@ -1,424 +0,0 @@
-package se.leap.bitmaskclient;
-
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver;
-import se.leap.bitmaskclient.ProviderAPIResultReceiver.Receiver;
-import se.leap.bitmaskclient.Dashboard;
-
-import de.blinkt.openvpn.activities.LogWindow;
-import de.blinkt.openvpn.core.VpnStatus;
-import de.blinkt.openvpn.core.VpnStatus.ConnectionStatus;
-import de.blinkt.openvpn.core.VpnStatus.StateListener;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Fragment;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.ResultReceiver;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.CompoundButton;
-import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-public class EipServiceFragment extends Fragment implements StateListener, OnCheckedChangeListener {
-
- protected static final String IS_EIP_PENDING = "is_eip_pending";
- public static final String START_ON_BOOT = "start on boot";
-
- private View eipFragment;
- private static Switch eipSwitch;
- private View eipDetail;
- private TextView eipStatus;
-
- private static EIPReceiver mEIPReceiver;
-
-
- public static String TAG = "se.leap.bitmask.EipServiceFragment";
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
-
- eipFragment = inflater.inflate(R.layout.eip_service_fragment, container, false);
- eipDetail = ((RelativeLayout) eipFragment.findViewById(R.id.eipDetail));
- eipDetail.setVisibility(View.VISIBLE);
-
- View eipSettings = eipFragment.findViewById(R.id.eipSettings);
- eipSettings.setVisibility(View.GONE); // FIXME too!
-
- if (EIP.mIsStarting)
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
-
- eipStatus = (TextView) eipFragment.findViewById(R.id.eipStatus);
-
- eipSwitch = (Switch) eipFragment.findViewById(R.id.eipSwitch);
- eipSwitch.setOnCheckedChangeListener(this);
-
- if(getArguments() != null && getArguments().containsKey(START_ON_BOOT) && getArguments().getBoolean(START_ON_BOOT))
- startEipFromScratch();
-
- return eipFragment;
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- mEIPReceiver = new EIPReceiver(new Handler());
-
- if (savedInstanceState != null)
- EIP.mIsStarting = savedInstanceState.getBoolean(IS_EIP_PENDING);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- VpnStatus.addStateListener(this);
-
- eipCommand(EIP.ACTION_CHECK_CERT_VALIDITY);
- }
-
- @Override
- public void onPause() {
- super.onPause();
-
- VpnStatus.removeStateListener(this);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- outState.putBoolean(IS_EIP_PENDING, EIP.mIsStarting);
- }
-
- protected void saveEipStatus() {
- boolean eip_is_on = false;
- Log.d("bitmask", "saveEipStatus");
- if(eipSwitch.isChecked()) {
- eip_is_on = true;
- }
-
- if(getActivity() != null)
- Dashboard.preferences.edit().putBoolean(Dashboard.START_ON_BOOT, eip_is_on).commit();
- }
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (buttonView.equals(eipSwitch)){
- handleSwitch(isChecked);
- }
- }
-
- private void handleSwitch(boolean isChecked) {
- if(isChecked)
- handleSwitchOn();
- else
- handleSwitchOff();
-
- saveEipStatus();
- }
-
- private void handleSwitchOn() {
- if(canStartEIP())
- startEipFromScratch();
- else if(canLogInToStartEIP()) {
- Log.d(TAG, "Can Log In to start EIP");
- Dashboard dashboard = (Dashboard) getActivity();
- dashboard.logInDialog(Bundle.EMPTY);
- }
- }
-
- private boolean canStartEIP() {
- boolean certificateExists = !Dashboard.preferences.getString(EIP.CERTIFICATE, "").isEmpty();
- boolean isAllowedAnon = Dashboard.preferences.getBoolean(EIP.ALLOWED_ANON, false);
- return (isAllowedAnon || certificateExists) && !EIP.mIsStarting && !EIP.isConnected();
- }
-
- private boolean canLogInToStartEIP() {
- boolean isAllowedRegistered = Dashboard.preferences.getBoolean(EIP.ALLOWED_REGISTERED, false);
- boolean isLoggedIn = !LeapSRPSession.getToken().isEmpty();
- Log.d(TAG, "Allow registered? " + isAllowedRegistered);
- Log.d(TAG, "Is logged in? " + isLoggedIn);
- return isAllowedRegistered && !isLoggedIn && !EIP.mIsStarting && !EIP.isConnected();
- }
-
- private void handleSwitchOff() {
- if(EIP.mIsStarting) {
- askPendingStartCancellation();
- } else if(EIP.isConnected()) {
- Log.d(TAG, "Stopping EIP");
- stopEIP();
- }
- }
-
- private void askPendingStartCancellation() {
- AlertDialog.Builder alertBuilder = new AlertDialog.Builder(getActivity());
- alertBuilder.setTitle(getResources().getString(R.string.eip_cancel_connect_title))
- .setMessage(getResources().getString(R.string.eip_cancel_connect_text))
- .setPositiveButton((R.string.yes), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- stopEIP();
- }
- })
- .setNegativeButton(getResources().getString(R.string.no), new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Log.d(TAG, "askPendingStartCancellation checks the switch to true");
- eipSwitch.setChecked(true);
- }
- })
- .show();
- }
-
- public void startEipFromScratch() {
- EIP.mIsStarting = true;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- String status = getResources().getString(R.string.eip_status_start_pending);
- setEipStatus(status);
-
- if(!eipSwitch.isChecked()) {
- Log.d(TAG, "startEipFromScratch checks the switch to true");
- eipSwitch.setChecked(true);
- saveEipStatus();
- }
- eipCommand(EIP.ACTION_START_EIP);
- }
-
- protected void stopEIP() {
- EIP.mIsStarting = false;
- View eipProgressBar = getActivity().findViewById(R.id.eipProgress);
- if(eipProgressBar != null)
- eipProgressBar.setVisibility(View.GONE);
-
- String status = getResources().getString(R.string.eip_state_not_connected);
- setEipStatus(status);
- eipCommand(EIP.ACTION_STOP_EIP);
- }
-
- /**
- * Send a command to EIP
- *
- * @param action A valid String constant from EIP class representing an Intent
- * filter for the EIP class
- */
- private void eipCommand(String action){
- // TODO validate "action"...how do we get the list of intent-filters for a class via Android API?
- Intent vpn_intent = new Intent(getActivity().getApplicationContext(), EIP.class);
- vpn_intent.setAction(action);
- vpn_intent.putExtra(EIP.RECEIVER_TAG, mEIPReceiver);
- getActivity().startService(vpn_intent);
- }
-
- @Override
- public void updateState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- boolean isNewLevel = EIP.lastConnectionStatusLevel != level;
- boolean justDecidedOnDisconnect = EIP.lastConnectionStatusLevel == ConnectionStatus.UNKNOWN_LEVEL;
- Log.d(TAG, "update state with level " + level);
- if(!justDecidedOnDisconnect && (isNewLevel || level == ConnectionStatus.LEVEL_CONNECTED)) {
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- EIP.lastConnectionStatusLevel = level;
- handleNewState(state, logmessage, localizedResId, level);
- }
- });
- } else if(justDecidedOnDisconnect && level == ConnectionStatus.LEVEL_CONNECTED) {
- EIP.lastConnectionStatusLevel = ConnectionStatus.LEVEL_NOTCONNECTED;
- updateState(state, logmessage, localizedResId, level);
- } // else if(isNewLevel || level == ConnectionStatus.LEVEL_AUTH_FAILED)
- // handleNewState(state, logmessage, localizedResId, level);
- }
-
- private void handleNewState(final String state, final String logmessage, final int localizedResId, final ConnectionStatus level) {
- if (level == ConnectionStatus.LEVEL_CONNECTED)
- setConnectedUI();
- else if (isDisconnectedLevel(level) && !EIP.mIsStarting)
- setDisconnectedUI();
- else if (level == ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET)
- setNoServerReplyUI(localizedResId, logmessage);
- else if (level == ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED)
- setServerReplyUI(state, localizedResId, logmessage);
- // else if (level == ConnectionStatus.LEVEL_AUTH_FAILED)
- // handleSwitchOn();
- }
-
- private boolean isDisconnectedLevel(final ConnectionStatus level) {
- return level == ConnectionStatus.LEVEL_NOTCONNECTED || level == ConnectionStatus.LEVEL_AUTH_FAILED;
- }
-
- private void setConnectedUI() {
- hideProgressBar();
- Log.d(TAG, "mIsDisconnecting = false in setConnectedUI");
- EIP.mIsStarting = false; //TODO This should be done in the onReceiveResult from START_EIP command, but right now LaunchVPN isn't notifying anybody the resultcode of the request so we need to listen the states with this listener.
- EIP.mIsDisconnecting = false; //TODO See comment above
- String status = getString(R.string.eip_state_connected);
- setEipStatus(status);
- adjustSwitch();
- }
-
- private void setDisconnectedUI(){
- hideProgressBar();
- EIP.mIsStarting = false; //TODO See comment in setConnectedUI()
- Log.d(TAG, "mIsDisconnecting = false in setDisconnectedUI");
- EIP.mIsDisconnecting = false; //TODO See comment in setConnectedUI()
-
- String status = getString(R.string.eip_state_not_connected);
- setEipStatus(status);
- adjustSwitch();
- }
-
- private void adjustSwitch() {
- if(EIP.isConnected()) {
- if(!eipSwitch.isChecked()) {
- eipSwitch.setChecked(true);
- }
- } else {
- if(eipSwitch.isChecked()) {
- eipSwitch.setChecked(false);
- }
- }
- }
-
- private void setNoServerReplyUI(int localizedResId, String logmessage) {
- if(eipStatus != null) {
- String prefix = getString(localizedResId);
- setEipStatus(prefix + " " + logmessage);
- }
- }
-
- private void setServerReplyUI(String state, int localizedResId, String logmessage) {
- if(eipStatus != null)
- if(state.equals("AUTH") || state.equals("GET_CONFIG")) {
- String prefix = getString(localizedResId);
- setEipStatus(prefix + " " + logmessage);
- }
- }
-
- protected void setEipStatus(String status) {
- if(eipStatus == null)
- eipStatus = (TextView) getActivity().findViewById(R.id.eipStatus);
- eipStatus.setText(status);
- }
-
- private void hideProgressBar() {
- if(getActivity() != null && getActivity().findViewById(R.id.eipProgress) != null)
- getActivity().findViewById(R.id.eipProgress).setVisibility(View.GONE);
- }
-
- /**
- * Inner class for handling messages related to EIP status and control requests
- *
- * @author Sean Leonard <meanderingcode@aetherislands.net>
- */
- protected class EIPReceiver extends ResultReceiver {
-
- protected EIPReceiver(Handler handler){
- super(handler);
- }
-
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- super.onReceiveResult(resultCode, resultData);
-
- String request = resultData.getString(EIP.REQUEST_TAG);
- boolean checked = false;
-
- if (request == EIP.ACTION_IS_EIP_RUNNING) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = true;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- break;
- }
- } else if (request == EIP.ACTION_START_EIP) {
- switch (resultCode){
- case Activity.RESULT_OK:
- Log.d(TAG, "Action start eip = Result OK");
- checked = true;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.VISIBLE);
- EIP.mIsStarting = false;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- eipFragment.findViewById(R.id.eipProgress).setVisibility(View.GONE);
- break;
- }
- } else if (request == EIP.ACTION_STOP_EIP) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = false;
- break;
- case Activity.RESULT_CANCELED:
- checked = true;
- break;
- }
- } else if (request == EIP.EIP_NOTIFICATION) {
- switch (resultCode){
- case Activity.RESULT_OK:
- checked = true;
- break;
- case Activity.RESULT_CANCELED:
- checked = false;
- break;
- }
- } else if (request == EIP.ACTION_CHECK_CERT_VALIDITY) {
- checked = eipSwitch.isChecked();
-
- switch (resultCode) {
- case Activity.RESULT_OK:
- break;
- case Activity.RESULT_CANCELED:
- Dashboard dashboard = (Dashboard) getActivity();
-
- dashboard.showProgressBar();
- String status = getResources().getString(R.string.updating_certificate_message);
- setEipStatus(status);
-
- if(LeapSRPSession.getToken().isEmpty() && !Dashboard.preferences.getBoolean(EIP.ALLOWED_ANON, false)) {
- dashboard.logInDialog(Bundle.EMPTY);
- } else {
-
- Intent provider_API_command = new Intent(getActivity(), ProviderAPI.class);
- if (dashboard.providerAPI_result_receiver == null) {
- dashboard.providerAPI_result_receiver = new ProviderAPIResultReceiver(new Handler());
- dashboard.providerAPI_result_receiver.setReceiver(dashboard);
- }
-
- provider_API_command.setAction(ProviderAPI.DOWNLOAD_CERTIFICATE);
- provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, dashboard.providerAPI_result_receiver);
- getActivity().startService(provider_API_command);
- }
- break;
- }
- }
- }
- }
-
-
- public static EIPReceiver getReceiver() {
- return mEIPReceiver;
- }
-
- public static boolean isEipSwitchChecked() {
- return eipSwitch.isChecked();
- }
-
- public void checkEipSwitch(boolean checked) {
- eipSwitch.setChecked(checked);
- // Log.d(TAG, "checkEipSwitch");
- // onCheckedChanged(eipSwitch, checked);
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
index c4844be9..49af9274 100644
--- a/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
+++ b/app/src/main/java/se/leap/bitmaskclient/FragmentManagerEnhanced.java
@@ -45,6 +45,10 @@ public class FragmentManagerEnhanced {
transaction.replace(containerViewId, fragment, tag).commit();
}
+ public FragmentTransaction beginTransaction() {
+ return generic_fragment_manager.beginTransaction();
+ }
+
public Fragment findFragmentByTag(String tag) {
return generic_fragment_manager.findFragmentByTag(tag);
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
index a953a710..989dc395 100644
--- a/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
+++ b/app/src/main/java/se/leap/bitmaskclient/LeapSRPSession.java
@@ -17,13 +17,14 @@
package se.leap.bitmaskclient;
+import org.jboss.security.srp.SRPParameters;
+
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Arrays;
-import org.jboss.security.srp.SRPParameters;
/**
* Implements all SRP algorithm logic.
diff --git a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
index eb196d46..07ed6c8f 100644
--- a/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/OnBootReceiver.java
@@ -3,8 +3,8 @@ package se.leap.bitmaskclient;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.util.Log;
+import se.leap.bitmaskclient.eip.Constants;
public class OnBootReceiver extends BroadcastReceiver {
@@ -14,7 +14,7 @@ public class OnBootReceiver extends BroadcastReceiver {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
if (!context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getString(Provider.KEY, "").isEmpty() && context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Context.MODE_PRIVATE).getBoolean(Dashboard.START_ON_BOOT, false)) {
Intent dashboard_intent = new Intent(context, Dashboard.class);
- dashboard_intent.setAction(EIP.ACTION_START_EIP);
+ dashboard_intent.setAction(Constants.ACTION_START_EIP);
dashboard_intent.putExtra(Dashboard.ON_BOOT, true);
dashboard_intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(dashboard_intent);
diff --git a/app/src/main/java/se/leap/bitmaskclient/Provider.java b/app/src/main/java/se/leap/bitmaskclient/Provider.java
index 8d6385e0..d7ff6633 100644
--- a/app/src/main/java/se/leap/bitmaskclient/Provider.java
+++ b/app/src/main/java/se/leap/bitmaskclient/Provider.java
@@ -16,32 +16,32 @@
*/
package se.leap.bitmaskclient;
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.Locale;
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Parcel;
+import android.os.Parcelable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import android.content.Context;
-import android.app.Activity;
-import android.content.SharedPreferences;
+import java.io.File;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Locale;
/**
* @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
*
*/
-public final class Provider implements Serializable {
+public final class Provider implements Parcelable {
- private static final long serialVersionUID = 6003835972151761353L;
-
- private static Provider instance = null;
-
- // We'll access our preferences here
- private static SharedPreferences preferences = null;
- // Represents our Provider's provider.json
- private static JSONObject definition = null;
+ private JSONObject definition; // Represents our Provider's provider.json
+ private URL main_url;
final public static String
API_URL = "api_uri",
@@ -69,71 +69,64 @@ public final class Provider implements Serializable {
private static final String API_TERM_DEFAULT_LANGUAGE = "default_language";
protected static final String[] API_EIP_TYPES = {"openvpn"};
- private static final String PREFS_EIP_NAME = null;
+ public Provider(URL main_url) {
+ this.main_url = main_url;
+ }
+ public Provider(File provider_file) {
-
- // What, no individual fields?! We're going to gamble on org.json.JSONObject and JSONArray
- // Supporting multiple API versions will probably break this paradigm,
- // Forcing me to write a real constructor and rewrite getters/setters
- // Also will refactor if i'm instantiating the same local variables all the time
-
- /**
- *
- */
- private Provider() {}
-
- protected static Provider getInstance(){
- if(instance==null){
- instance = new Provider();
- }
- return instance;
- }
+ }
+ public static final Parcelable.Creator<Provider> CREATOR
+ = new Parcelable.Creator<Provider>() {
+ public Provider createFromParcel(Parcel in) {
+ return new Provider(in);
+ }
- protected void init(Activity activity) {
-
- // Load our preferences from SharedPreferences
- // If there's nothing there, we will end up returning a rather empty object
- // to whoever called getInstance() and they can run the First Run Wizard
- //preferences = context.getgetPreferences(0); // 0 == MODE_PRIVATE, but we don't extend Android's classes...
-
- // Load SharedPreferences
- preferences = activity.getSharedPreferences(Dashboard.SHARED_PREFERENCES,Context.MODE_PRIVATE);
- // Inflate our provider.json data
- try {
- definition = new JSONObject( preferences.getString(Provider.KEY, "") );
- } catch (JSONException e) {
- // TODO: handle exception
-
- // FIXME!! We want "real" data!!
- }
- }
+ public Provider[] newArray(int size) {
+ return new Provider[size];
+ }
+ };
+
+ private Provider(Parcel in) {
+ try {
+ main_url = new URL(in.readString());
+ String definition_string = in.readString();
+ if(definition_string != null)
+ definition = new JSONObject((definition_string));
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ protected void define(JSONObject provider_json) {
+ definition = provider_json;
+ }
+
+ protected JSONObject definition() { return definition; }
protected String getDomain(){
- String domain = "Null";
- try {
- domain = definition.getString(API_TERM_DOMAIN);
- } catch (JSONException e) {
- domain = "Null";
- e.printStackTrace();
- }
- return domain;
+ return main_url.getHost();
}
+
+ protected URL mainUrl() {
+ return main_url;
+ }
protected String getName(){
// Should we pass the locale in, or query the system here?
String lang = Locale.getDefault().getLanguage();
- String name = "Null"; // Should it actually /be/ null, for error conditions?
+ String name = "";
try {
- name = definition.getJSONObject(API_TERM_NAME).getString(lang);
+ if(definition != null)
+ name = definition.getJSONObject(API_TERM_NAME).getString(lang);
+ else throw new JSONException("Provider not defined");
} catch (JSONException e) {
- // TODO: Nesting try/catch blocks? Crazy
- // Maybe you should actually handle exception?
- try {
- name = definition.getJSONObject(API_TERM_NAME).getString( definition.getString(API_TERM_DEFAULT_LANGUAGE) );
- } catch (JSONException e2) {
- // TODO: Will you handle the exception already?
- }
+ if(main_url != null) {
+ String host = main_url.getHost();
+ name = host.substring(0, host.indexOf("."));
+ }
}
return name;
@@ -157,58 +150,60 @@ public final class Provider implements Serializable {
}
protected boolean hasEIP() {
- JSONArray services = null;
try {
- services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"]
+ JSONArray services = definition.getJSONArray(API_TERM_SERVICES); // returns ["openvpn"]
+ for (int i=0;i<API_EIP_TYPES.length+1;i++){
+ try {
+ // Walk the EIP types array looking for matches in provider's service definitions
+ if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) )
+ return true;
+ } catch (NullPointerException e){
+ e.printStackTrace();
+ return false;
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ return false;
+ }
+ }
} catch (Exception e) {
// TODO: handle exception
}
- for (int i=0;i<API_EIP_TYPES.length+1;i++){
- try {
- // Walk the EIP types array looking for matches in provider's service definitions
- if ( Arrays.asList(API_EIP_TYPES).contains( services.getString(i) ) )
- return true;
- } catch (NullPointerException e){
- e.printStackTrace();
- return false;
- } catch (JSONException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- return false;
- }
- }
return false;
}
-
- protected String getEIPType() {
- // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
- // This will have to hook into some saved choice of EIP transport
- if ( instance.hasEIP() )
- return "OpenVPN";
- else
- return null;
- }
-
- protected JSONObject getEIP() {
- // FIXME!!!!! We won't always be providing /only/ OpenVPN, will we?
- // This will have to hook into some saved choice of EIP transport, cluster, gateway
- // with possible "choose at random" preference
- if ( instance.hasEIP() ){
- // TODO Might need an EIP class, but we've only got OpenVPN type right now,
- // and only one gateway for our only provider...
- // TODO We'll try to load from preferences, have to call ProviderAPI if we've got nothin...
- JSONObject eipObject = null;
- try {
- eipObject = new JSONObject( preferences.getString(PREFS_EIP_NAME, "") );
- } catch (JSONException e) {
- // TODO ConfigHelper.rescueJSON()
- // Still nothing?
- // TODO ProviderAPI.getEIP()
- e.printStackTrace();
- }
-
- return eipObject;
- } else
- return null;
- }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int i) {
+ parcel.writeString(main_url.toString());
+ if(definition != null)
+ parcel.writeString(definition.toString());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if(o instanceof Provider) {
+ Provider p = (Provider) o;
+ return p.mainUrl().getHost().equals(mainUrl().getHost());
+ } else return false;
+ }
+
+ public JSONObject toJson() {
+ JSONObject json = new JSONObject();
+ try {
+ json.put(Provider.MAIN_URL, main_url);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return json;
+ }
+
+ @Override
+ public int hashCode() {
+ return mainUrl().getHost().hashCode();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
index 43bba085..c63e2edb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderListAdapter.java
@@ -1,7 +1,5 @@
package se.leap.bitmaskclient;
-import java.util.List;
-
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
@@ -9,7 +7,15 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TwoLineListItem;
-public class ProviderListAdapter<T> extends ArrayAdapter<T> {
+import com.pedrogomez.renderers.AdapteeCollection;
+import com.pedrogomez.renderers.RendererAdapter;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+public class ProviderListAdapter extends RendererAdapter<Provider> {
private static boolean[] hidden = null;
public void hide(int position) {
@@ -23,10 +29,23 @@ public class ProviderListAdapter<T> extends ArrayAdapter<T> {
notifyDataSetChanged();
notifyDataSetInvalidated();
}
+
+ public void showAllProviders() {
+ for(int i = 0; i < hidden.length; i++)
+ hidden[i] = false;
+ notifyDataSetChanged();
+ notifyDataSetInvalidated();
+ }
- public void unHideAll() {
- for (int provider_index = 0; provider_index < hidden.length; provider_index++)
- hidden[provider_index] = false;
+ public void hideAllBut(int position) {
+ for (int i = 0; i < hidden.length; i++) {
+ if (i != position)
+ hidden[i] = true;
+ else
+ hidden[i] = false;
+ }
+ notifyDataSetChanged();
+ notifyDataSetInvalidated();
}
private int getRealPosition(int position) {
@@ -60,55 +79,52 @@ public class ProviderListAdapter<T> extends ArrayAdapter<T> {
return (hidden.length - getHiddenCount());
}
- public ProviderListAdapter(Context mContext, int layout, List<T> objects) {
- super(mContext, layout, objects);
- if(hidden == null) {
- hidden = new boolean[objects.size()];
- for (int i = 0; i < objects.size(); i++)
- hidden[i] = false;
- }
- }
-
- public ProviderListAdapter(Context mContext, int layout, List<T> objects, boolean show_all_providers) {
- super(mContext, layout, objects);
- if(show_all_providers) {
- hidden = new boolean[objects.size()];
- for (int i = 0; i < objects.size(); i++)
- hidden[i] = false;
- }
- }
+ public ProviderListAdapter(LayoutInflater layoutInflater, RendererBuilder rendererBuilder,
+ AdapteeCollection<Provider> collection) {
+ super(layoutInflater, rendererBuilder, collection);
+ hidden = new boolean[collection.size()];
+ for (int i = 0; i < collection.size(); i++)
+ hidden[i] = false;
+ }
@Override
- public void add(T item) {
+ public void add(Provider item) {
super.add(item);
- boolean[] new_hidden = new boolean[hidden.length+1];
- System.arraycopy(hidden, 0, new_hidden, 0, hidden.length);
- new_hidden[hidden.length] = false;
- hidden = new_hidden;
+ if(getCollection().size() > hidden.length) {
+ boolean[] new_hidden = new boolean[hidden.length + 1];
+ System.arraycopy(hidden, 0, new_hidden, 0, hidden.length);
+ new_hidden[hidden.length] = false;
+ hidden = new_hidden;
+ }
}
@Override
- public void remove(T item) {
+ public void remove(Provider item) {
super.remove(item);
boolean[] new_hidden = new boolean[hidden.length-1];
System.arraycopy(hidden, 0, new_hidden, 0, hidden.length-1);
hidden = new_hidden;
}
- @Override
- public View getView(int index, View convertView, ViewGroup parent) {
- TwoLineListItem row;
- int position = getRealPosition(index);
- if (convertView == null) {
- LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- row = (TwoLineListItem)inflater.inflate(R.layout.provider_list_item, null);
- } else {
- row = (TwoLineListItem)convertView;
- }
- ProviderListContent.ProviderItem data = ProviderListContent.ITEMS.get(position);
- row.getText1().setText(data.domain());
- row.getText2().setText(data.name());
+ protected int indexOf(Provider item) {
+ int index = 0;
+ ProviderManager provider_manager = (ProviderManager) getCollection();
+ Set<Provider> providers = provider_manager.providers();
+ for (Provider provider : providers) {
+ if (provider.equals(item)) {
+ break;
+ } else index++;
+ }
+ return index;
+ }
- return row;
- }
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return super.getView(getRealPosition(position), convertView, parent);
+ }
+
+ public void saveProviders() {
+ ProviderManager provider_manager = (ProviderManager) getCollection();
+ provider_manager.saveCustomProvidersToFile();
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java b/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java
deleted file mode 100644
index db414d87..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/ProviderListFragment.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * Copyright (c) 2013 LEAP Encryption Access Project and contributers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package se.leap.bitmaskclient;
-
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.ProviderListContent.ProviderItem;
-import android.app.Activity;
-import android.app.ListFragment;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ListView;
-
-/**
- * A list fragment representing a list of Providers. This fragment
- * also supports tablet devices by allowing list items to be given an
- * 'activated' state upon selection. This helps indicate which item is
- * currently being viewed in a {@link DashboardFragment}.
- * <p>
- * Activities containing this fragment MUST implement the {@link Callbacks}
- * interface.
- */
-public class ProviderListFragment extends ListFragment {
-
- public static String TAG = "provider_list_fragment";
- public static String SHOW_ALL_PROVIDERS = "show_all_providers";
- public static String TOP_PADDING = "top padding from providerlistfragment";
- private ProviderListAdapter<ProviderItem> content_adapter;
-
- /**
- * The serialization (saved instance state) Bundle key representing the
- * activated item position. Only used on tablets.
- */
- private static final String STATE_ACTIVATED_POSITION = "activated_position";
-
- /**
- * The fragment's current callback object, which is notified of list item
- * clicks.
- */
- private Callbacks mCallbacks = sDummyCallbacks;
-
- /**
- * The current activated item position. Only used on tablets.
- */
- private int mActivatedPosition = ListView.INVALID_POSITION;
-
- /**
- * A callback interface that all activities containing this fragment must
- * implement. This mechanism allows activities to be notified of item
- * selections.
- */
- public interface Callbacks {
- /**
- * Callback for when an item has been selected.
- */
- public void onItemSelected(String id);
- }
-
- /**
- * A dummy implementation of the {@link Callbacks} interface that does
- * nothing. Used only when this fragment is not attached to an activity.
- */
- private static Callbacks sDummyCallbacks = new Callbacks() {
- @Override
- public void onItemSelected(String id) {
- }
- };
-
- /**
- * Mandatory empty constructor for the fragment manager to instantiate the
- * fragment (e.g. upon screen orientation changes).
- */
- public ProviderListFragment() {
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if(getArguments().containsKey(SHOW_ALL_PROVIDERS))
- content_adapter = new ProviderListAdapter<ProviderListContent.ProviderItem>(
- getActivity(),
- R.layout.provider_list_item,
- ProviderListContent.ITEMS, getArguments().getBoolean(SHOW_ALL_PROVIDERS));
- else
- content_adapter = new ProviderListAdapter<ProviderListContent.ProviderItem>(
- getActivity(),
- R.layout.provider_list_item,
- ProviderListContent.ITEMS);
-
-
- setListAdapter(content_adapter);
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
- return inflater.inflate(R.layout.provider_list_fragment, container, false);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- // Restore the previously serialized activated item position.
- if (savedInstanceState != null
- && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
- setActivatedPosition(savedInstanceState.getInt(STATE_ACTIVATED_POSITION));
- }
- if(getArguments() != null && getArguments().containsKey(TOP_PADDING)) {
- int topPadding = getArguments().getInt(TOP_PADDING);
- View current_view = getView();
- getView().setPadding(current_view.getPaddingLeft(), topPadding, current_view.getPaddingRight(), current_view.getPaddingBottom());
- }
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
-
- // Activities containing this fragment must implement its callbacks.
- if (!(activity instanceof Callbacks)) {
- throw new IllegalStateException("Activity must implement fragment's callbacks.");
- }
-
- mCallbacks = (Callbacks) activity;
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
-
- // Reset the active callbacks interface to the dummy implementation.
- mCallbacks = sDummyCallbacks;
- }
-
- @Override
- public void onListItemClick(ListView listView, View view, int position, long id) {
- super.onListItemClick(listView, view, position, id);
-
- // Notify the active callbacks interface (the activity, if the
- // fragment is attached to one) that an item has been selected.
- mCallbacks.onItemSelected(ProviderListContent.ITEMS.get(position).name());
-
- for(int item_position = 0; item_position < listView.getCount(); item_position++) {
- if(item_position != position)
- content_adapter.hide(item_position);
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- if (mActivatedPosition != ListView.INVALID_POSITION) {
- // Serialize and persist the activated item position.
- outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
- }
- }
-
- public void notifyAdapter() {
- content_adapter.notifyDataSetChanged();
- }
- /**
- * Turns on activate-on-click mode. When this mode is on, list items will be
- * given the 'activated' state when touched.
- */
- public void setActivateOnItemClick(boolean activateOnItemClick) {
- // When setting CHOICE_MODE_SINGLE, ListView will automatically
- // give items the 'activated' state when touched.
- getListView().setChoiceMode(activateOnItemClick
- ? ListView.CHOICE_MODE_SINGLE
- : ListView.CHOICE_MODE_NONE);
- }
-
- private void setActivatedPosition(int position) {
- if (position == ListView.INVALID_POSITION) {
- getListView().setItemChecked(mActivatedPosition, false);
- } else {
- getListView().setItemChecked(position, true);
- }
-
- mActivatedPosition = position;
- }
-
- public void removeLastItem() {
- unhideAll();
- content_adapter.remove(content_adapter.getItem(content_adapter.getCount()-1));
- content_adapter.notifyDataSetChanged();
- }
-
- public void addItem(ProviderItem provider) {
- content_adapter.add(provider);
- content_adapter.notifyDataSetChanged();
- }
-
- public void hideAllBut(int position) {
- int real_count = content_adapter.getCount();
- for(int i = 0; i < real_count;)
- if(i != position) {
- content_adapter.hide(i);
- position--;
- real_count--;
- } else {
- i++;
- }
- }
-
- public void unhideAll() {
- if(content_adapter != null) {
- content_adapter.unHideAll();
- content_adapter.notifyDataSetChanged();
- }
- }
-
- /**
- * @return a new instance of this ListFragment.
- */
- public static ProviderListFragment newInstance() {
- return new ProviderListFragment();
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
new file mode 100644
index 00000000..69bd7c1e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderManager.java
@@ -0,0 +1,179 @@
+package se.leap.bitmaskclient;
+
+import android.content.res.AssetManager;
+
+import com.pedrogomez.renderers.AdapteeCollection;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+public class ProviderManager implements AdapteeCollection<Provider> {
+
+ private AssetManager assets_manager;
+ private File external_files_dir;
+ private Set<Provider> default_providers;
+ private Set<Provider> custom_providers;
+
+ private static ProviderManager instance;
+
+ final protected static String URLS = "urls";
+
+ public static ProviderManager getInstance(AssetManager assets_manager, File external_files_dir) {
+ if(instance == null)
+ instance = new ProviderManager(assets_manager);
+
+ instance.addCustomProviders(external_files_dir);
+ return instance;
+ }
+
+ public ProviderManager(AssetManager assets_manager) {
+ this.assets_manager = assets_manager;
+ addDefaultProviders(assets_manager);
+ }
+
+ private void addDefaultProviders(AssetManager assets_manager) {
+ try {
+ default_providers = providersFromAssets(URLS, assets_manager.list(URLS));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Set<Provider> providersFromAssets(String directory, String[] relative_file_paths) {
+ Set<Provider> providers = new HashSet<Provider>();
+ try {
+ for(String file : relative_file_paths) {
+ String main_url = extractMainUrlFromInputStream(assets_manager.open(directory + "/" + file));
+ providers.add(new Provider(new URL(main_url)));
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return providers;
+ }
+
+
+ private void addCustomProviders(File external_files_dir) {
+ this.external_files_dir = external_files_dir;
+ custom_providers = external_files_dir.isDirectory() ?
+ providersFromFiles(external_files_dir.list()) :
+ new HashSet<Provider>();
+ }
+
+ private Set<Provider> providersFromFiles(String[] files) {
+ Set<Provider> providers = new HashSet<Provider>();
+ try {
+ for(String file : files) {
+ String main_url = extractMainUrlFromInputStream(new FileInputStream(external_files_dir.getAbsolutePath() + "/" + file));
+ providers.add(new Provider(new URL(main_url)));
+ }
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return providers;
+ }
+
+ private String extractMainUrlFromInputStream(InputStream input_stream_file_contents) {
+ String main_url = "";
+ byte[] bytes = new byte[0];
+ try {
+ bytes = new byte[input_stream_file_contents.available()];
+ if(input_stream_file_contents.read(bytes) > 0) {
+ JSONObject file_contents = new JSONObject(new String(bytes));
+ main_url = file_contents.getString(Provider.MAIN_URL);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ return main_url;
+ }
+
+ public Set<Provider> providers() {
+ Set<Provider> all_providers = new HashSet<Provider>();
+ all_providers.addAll(default_providers);
+ all_providers.addAll(custom_providers);
+ return all_providers;
+ }
+
+ @Override
+ public int size() {
+ return providers().size();
+ }
+
+ @Override
+ public Provider get(int index) {
+ Iterator<Provider> iterator = providers().iterator();
+ while(iterator.hasNext() && index > 0) {
+ iterator.next();
+ index--;
+ }
+ return iterator.next();
+ }
+
+ @Override
+ public void add(Provider element) {
+ if(!default_providers.contains(element))
+ custom_providers.add(element);
+ }
+
+ @Override
+ public void remove(Provider element) {
+ custom_providers.remove(element);
+ }
+
+ @Override
+ public void addAll(Collection<Provider> elements) {
+ custom_providers.addAll(elements);
+ }
+
+ @Override
+ public void removeAll(Collection<Provider> elements) {
+ custom_providers.removeAll(elements);
+ default_providers.removeAll(elements);
+ }
+
+ @Override
+ public void clear() {
+ default_providers.clear();
+ custom_providers.clear();
+ }
+
+ protected void saveCustomProvidersToFile() {
+ try {
+ for (Provider provider : custom_providers) {
+ File provider_file = new File(external_files_dir, provider.getName() + ".json");
+ if(!provider_file.exists()) {
+ FileWriter writer = new FileWriter(provider_file);
+ writer.write(provider.toJson().toString());
+ writer.close();
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java b/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java
new file mode 100644
index 00000000..6e194e84
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderRenderer.java
@@ -0,0 +1,57 @@
+package se.leap.bitmaskclient;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.pedrogomez.renderers.Renderer;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import butterknife.OnItemClick;
+import butterknife.OnItemSelected;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+public class ProviderRenderer extends Renderer<Provider> {
+ private final Context context;
+
+ @InjectView(R.id.provider_name)
+ TextView name;
+ @InjectView(R.id.provider_domain)
+ TextView domain;
+
+ public ProviderRenderer(Context context) {
+ this.context = context;
+ }
+
+ @Override
+ protected View inflate(LayoutInflater inflater, ViewGroup parent) {
+ View view = inflater.inflate(R.layout.provider_list_item, parent, false);
+ ButterKnife.inject(this, view);
+ return view;
+ }
+
+ @Override
+ protected void setUpView(View rootView) {
+ /*
+ * Empty implementation substituted with the usage of ButterKnife library by Jake Wharton.
+ */
+ }
+
+ @Override
+ protected void hookListeners(View rootView) {
+ //Empty
+ }
+
+ @Override
+ public void render() {
+ Provider provider = getContent();
+ name.setText(provider.getName());
+ domain.setText(provider.getDomain());
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java b/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java
new file mode 100644
index 00000000..7366e68e
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/ProviderRendererBuilder.java
@@ -0,0 +1,25 @@
+package se.leap.bitmaskclient;
+
+import android.content.Context;
+
+import com.pedrogomez.renderers.Renderer;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+/**
+ * Created by parmegv on 4/12/14.
+ */
+ public class ProviderRendererBuilder extends RendererBuilder<Provider> {
+ public ProviderRendererBuilder(Collection<Renderer<Provider>> prototypes) {
+ super(prototypes);
+ }
+ @Override
+ protected Class getPrototypeClass(Provider content) {
+ return ProviderRenderer.class;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/LogInDialog.java b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java
index 5a0c9a6d..22e0f128 100644
--- a/app/src/main/java/se/leap/bitmaskclient/LogInDialog.java
+++ b/app/src/main/java/se/leap/bitmaskclient/SessionDialog.java
@@ -16,22 +16,19 @@
*/
package se.leap.bitmaskclient;
-import se.leap.bitmaskclient.R;
-import android.R.color;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
-import android.content.res.ColorStateList;
import android.os.Bundle;
-import android.provider.CalendarContract.Colors;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.BounceInterpolator;
import android.widget.EditText;
import android.widget.TextView;
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+
/**
* Implements the log in dialog, currently without progress dialog.
*
@@ -42,47 +39,56 @@ import android.widget.TextView;
* @author parmegv
*
*/
-public class LogInDialog extends SessionDialogInterface {
+public class SessionDialog extends DialogFragment{
- final public static String TAG = LogInDialog.class.getSimpleName();
+ final public static String TAG = SessionDialog.class.getSimpleName();
+
+ final public static String USERNAME = "username";
+ final public static String PASSWORD = "password";
+ final public static String USERNAME_MISSING = "username missing";
+ final public static String PASSWORD_INVALID_LENGTH = "password_invalid_length";
+
+ @InjectView(R.id.user_message)
+ TextView user_message;
+ @InjectView(R.id.username_entered)
+ EditText username_field;
+ @InjectView(R.id.password_entered)
+ EditText password_field;
+
+ private static SessionDialog dialog;
private static boolean is_eip_pending = false;
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
- View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
+ View view = inflater.inflate(R.layout.session_dialog, null);
+ ButterKnife.inject(this, view);
- final TextView user_message = (TextView)log_in_dialog_view.findViewById(R.id.user_message);
- if(getArguments() != null && getArguments().containsKey(getResources().getString(R.string.user_message))) {
- user_message.setText(getArguments().getString(getResources().getString(R.string.user_message)));
- } else {
- user_message.setVisibility(View.GONE);
- }
-
- final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
- if(getArguments() != null && getArguments().containsKey(USERNAME)) {
- String username = getArguments().getString(USERNAME);
- username_field.setText(username);
- }
- if (getArguments() != null && getArguments().containsKey(USERNAME_MISSING)) {
- username_field.setError(getResources().getString(R.string.username_ask));
- }
-
- final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) {
password_field.requestFocus();
}
- if (getArguments() != null && getArguments().containsKey(PASSWORD_INVALID_LENGTH)) {
- password_field.setError(getResources().getString(R.string.error_not_valid_password_user_message));
- }
- if(getArguments() != null && getArguments().getBoolean(EipServiceFragment.IS_EIP_PENDING, false)) {
- is_eip_pending = true;
+
+ Bundle arguments = getArguments();
+ if (arguments != null) {
+ is_eip_pending = arguments.getBoolean(EipFragment.IS_PENDING, false);
+ if (arguments.containsKey(PASSWORD_INVALID_LENGTH))
+ password_field.setError(getString(R.string.error_not_valid_password_user_message));
+ if (arguments.containsKey(USERNAME)) {
+ String username = arguments.getString(USERNAME);
+ username_field.setText(username);
}
-
+ if (arguments.containsKey(USERNAME_MISSING)) {
+ username_field.setError(getString(R.string.username_ask));
+ }
+ if(arguments.containsKey(getString(R.string.user_message)))
+ user_message.setText(arguments.getString(getString(R.string.user_message)));
+ else
+ user_message.setVisibility(View.GONE);
+ }
- builder.setView(log_in_dialog_view)
+ builder.setView(view)
.setPositiveButton(R.string.login_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
String username = username_field.getText().toString();
@@ -107,35 +113,37 @@ public class LogInDialog extends SessionDialogInterface {
return builder.create();
}
+
/**
- * Interface used to communicate LogInDialog with Dashboard.
+ * Interface used to communicate SessionDialog with Dashboard.
*
* @author parmegv
*
*/
- public interface LogInDialogInterface {
+ public interface SessionDialogInterface {
public void logIn(String username, String password);
- public void cancelAuthedEipOn();
public void signUp(String username, String password);
public void cancelLoginOrSignup();
}
- LogInDialogInterface interface_with_Dashboard;
+ SessionDialogInterface interface_with_Dashboard;
/**
* @return a new instance of this DialogFragment.
*/
public static DialogFragment newInstance() {
- LogInDialog dialog_fragment = new LogInDialog();
- return dialog_fragment;
+ if(dialog == null)
+ dialog = new SessionDialog();
+
+ return dialog;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
- interface_with_Dashboard = (LogInDialogInterface) activity;
+ interface_with_Dashboard = (SessionDialogInterface) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement LogInDialogListener");
@@ -146,6 +154,6 @@ public class LogInDialog extends SessionDialogInterface {
public void onCancel(DialogInterface dialog) {
super.onCancel(dialog);
if(is_eip_pending)
- interface_with_Dashboard.cancelAuthedEipOn();
+ interface_with_Dashboard.cancelLoginOrSignup();
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/SessionDialogInterface.java b/app/src/main/java/se/leap/bitmaskclient/SessionDialogInterface.java
deleted file mode 100644
index 7b08a4d1..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/SessionDialogInterface.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Copyright (c) 2013 LEAP Encryption Access Project and contributers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-package se.leap.bitmaskclient;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.os.Bundle;
-
-/**
- * @author parmegv
- */
-public abstract class SessionDialogInterface extends DialogFragment {
- final public static String USERNAME = "username";
- final public static String PASSWORD = "password";
- final public static String USERNAME_MISSING = "username missing";
- final public static String PASSWORD_INVALID_LENGTH = "password_invalid_length";
-
- @Override
- public void onAttach(Activity activity) { super.onAttach(activity); }
-
- @Override
- public void onCancel(DialogInterface dialog) { super.onCancel(dialog); }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java b/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java
deleted file mode 100644
index 3cb41f4f..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/SignUpDialog.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/**
- * Copyright (c) 2013 LEAP Encryption Access Project and contributers
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package se.leap.bitmaskclient;
-
-import se.leap.bitmaskclient.R;
-import android.R.color;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.DialogFragment;
-import android.content.DialogInterface;
-import android.content.res.ColorStateList;
-import android.os.Bundle;
-import android.provider.CalendarContract.Colors;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.BounceInterpolator;
-import android.widget.EditText;
-import android.widget.TextView;
-
-/**
- * Implements the sign up dialog, currently without progress dialog.
- *
- * It returns to the previous fragment when finished, and sends username and password to the registration method.
- *
- * It also notifies the user if the password is not valid.
- *
- * @author parmegv
- *
- */
-public class SignUpDialog extends SessionDialogInterface {
-
- final public static String TAG = SignUpDialog.class.getSimpleName();
-
- private static boolean is_eip_pending = false;
-
- public AlertDialog onCreateDialog(Bundle savedInstanceState) {
- AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- LayoutInflater inflater = getActivity().getLayoutInflater();
- View log_in_dialog_view = inflater.inflate(R.layout.log_in_dialog, null);
-
- final TextView user_message = (TextView)log_in_dialog_view.findViewById(R.id.user_message);
- if(getArguments() != null && getArguments().containsKey(getResources().getString(R.string.user_message))) {
- user_message.setText(getArguments().getString(getResources().getString(R.string.user_message)));
- } else {
- user_message.setVisibility(View.GONE);
- }
-
- final EditText username_field = (EditText)log_in_dialog_view.findViewById(R.id.username_entered);
- if(getArguments() != null && getArguments().containsKey(USERNAME)) {
- String username = getArguments().getString(USERNAME);
- username_field.setText(username);
- }
- if (getArguments() != null && getArguments().containsKey(USERNAME_MISSING)) {
- username_field.setError(getResources().getString(R.string.username_ask));
- }
-
- final EditText password_field = (EditText)log_in_dialog_view.findViewById(R.id.password_entered);
- if(!username_field.getText().toString().isEmpty() && password_field.isFocusable()) {
- password_field.requestFocus();
- }
- if (getArguments() != null && getArguments().containsKey(PASSWORD_INVALID_LENGTH)) {
- password_field.setError(getResources().getString(R.string.error_not_valid_password_user_message));
- }
- if(getArguments() != null && getArguments().getBoolean(EipServiceFragment.IS_EIP_PENDING, false)) {
- is_eip_pending = true;
- }
-
-
- builder.setView(log_in_dialog_view)
- .setPositiveButton(R.string.signup_button, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- String username = username_field.getText().toString();
- String password = password_field.getText().toString();
- dialog.dismiss();
- interface_with_Dashboard.signUp(username, password);
- }
- })
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int id) {
- dialog.cancel();
- interface_with_Dashboard.cancelLoginOrSignup();
- }
- });
-
- return builder.create();
- }
-
- /**
- * Interface used to communicate SignUpDialog with Dashboard.
- *
- * @author parmegv
- *
- */
- public interface SignUpDialogInterface {
- public void signUp(String username, String password);
- public void cancelAuthedEipOn();
- public void cancelLoginOrSignup();
- }
-
- SignUpDialogInterface interface_with_Dashboard;
-
- /**
- * @return a new instance of this DialogFragment.
- */
- public static DialogFragment newInstance() {
- SignUpDialog dialog_fragment = new SignUpDialog();
- return dialog_fragment;
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- try {
- interface_with_Dashboard = (SignUpDialogInterface) activity;
- } catch (ClassCastException e) {
- throw new ClassCastException(activity.toString()
- + " must implement SignUpDialogListener");
- }
- }
-
- @Override
- public void onCancel(DialogInterface dialog) {
- if(is_eip_pending)
- interface_with_Dashboard.cancelAuthedEipOn();
- super.onCancel(dialog);
- }
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
new file mode 100644
index 00000000..12c2e015
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Constants.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+/**
+ *
+ * Constants for intent passing, shared preferences
+ *
+ * @author Parménides GV <parmegv@sdf.org>
+ *
+ */
+public interface Constants {
+
+ public final static String TAG = Constants.class.getSimpleName();
+
+ public final static String AUTHED_EIP = TAG + ".AUTHED_EIP";
+ public final static String ACTION_CHECK_CERT_VALIDITY = TAG + ".CHECK_CERT_VALIDITY";
+ public final static String ACTION_START_EIP = TAG + ".START_EIP";
+ public final static String ACTION_STOP_EIP = TAG + ".STOP_EIP";
+ public final static String ACTION_UPDATE_EIP_SERVICE = TAG + ".UPDATE_EIP_SERVICE";
+ public final static String ACTION_IS_EIP_RUNNING = TAG + ".IS_RUNNING";
+ public final static String EIP_NOTIFICATION = TAG + ".EIP_NOTIFICATION";
+ public final static String ALLOWED_ANON = "allow_anonymous";
+ public final static String ALLOWED_REGISTERED = "allow_registration";
+ public final static String CERTIFICATE = "cert";
+ public final static String PRIVATE_KEY = TAG + ".PRIVATE_KEY";
+ public final static String KEY = TAG + ".KEY";
+ public final static String RECEIVER_TAG = TAG + ".RECEIVER_TAG";
+ public final static String REQUEST_TAG = TAG + ".REQUEST_TAG";
+ public final static String START_BLOCKING_VPN_PROFILE = TAG + ".START_BLOCKING_VPN_PROFILE";
+ public final static String PROVIDER_CONFIGURED = TAG + ".PROVIDER_CONFIGURED";
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
new file mode 100644
index 00000000..3d3070c8
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EIP.java
@@ -0,0 +1,251 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.app.Activity;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import de.blinkt.openvpn.LaunchVPN;
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ProfileManager;
+import se.leap.bitmaskclient.Dashboard;
+import se.leap.bitmaskclient.EipFragment;
+
+import static se.leap.bitmaskclient.eip.Constants.ACTION_CHECK_CERT_VALIDITY;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_IS_EIP_RUNNING;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_START_EIP;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_STOP_EIP;
+import static se.leap.bitmaskclient.eip.Constants.ACTION_UPDATE_EIP_SERVICE;
+import static se.leap.bitmaskclient.eip.Constants.CERTIFICATE;
+import static se.leap.bitmaskclient.eip.Constants.KEY;
+import static se.leap.bitmaskclient.eip.Constants.RECEIVER_TAG;
+import static se.leap.bitmaskclient.eip.Constants.REQUEST_TAG;
+
+/**
+ * EIP is the abstract base class for interacting with and managing the Encrypted
+ * Internet Proxy connection. Connections are started, stopped, and queried through
+ * this IntentService.
+ * Contains logic for parsing eip-service.json from the provider, configuring and selecting
+ * gateways, and controlling {@link de.blinkt.openvpn.core.OpenVPNService} connections.
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
+ */
+public final class EIP extends IntentService {
+
+ public final static String TAG = EIP.class.getSimpleName();
+ public final static String SERVICE_API_PATH = "config/eip-service.json";
+
+ public static final int DISCONNECT = 15;
+
+ private static Context context;
+ private static ResultReceiver mReceiver;
+ private static SharedPreferences preferences;
+
+ private static JSONObject eip_definition;
+ private static List<Gateway> gateways = new ArrayList<Gateway>();
+ private static ProfileManager profile_manager;
+ private static Gateway gateway;
+
+ public EIP(){
+ super(TAG);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ context = getApplicationContext();
+ profile_manager = ProfileManager.getInstance(context);
+
+ preferences = getSharedPreferences(Dashboard.SHARED_PREFERENCES, MODE_PRIVATE);
+ refreshEipDefinition();
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ String action = intent.getAction();
+ mReceiver = intent.getParcelableExtra(RECEIVER_TAG);
+
+ if ( action.equals(ACTION_START_EIP))
+ startEIP();
+ else if (action.equals(ACTION_STOP_EIP))
+ stopEIP();
+ else if (action.equals(ACTION_IS_EIP_RUNNING))
+ isRunning();
+ else if (action.equals(ACTION_UPDATE_EIP_SERVICE))
+ updateEIPService();
+ else if (action.equals(ACTION_CHECK_CERT_VALIDITY))
+ checkCertValidity();
+ }
+
+ /**
+ * Initiates an EIP connection by selecting a gateway and preparing and sending an
+ * Intent to {@link de.blinkt.openvpn.LaunchVPN}.
+ * It also sets up early routes.
+ */
+ private void startEIP() {
+ if(gateways.isEmpty())
+ updateEIPService();
+ earlyRoutes();
+
+ GatewaySelector gateway_selector = new GatewaySelector(gateways);
+ gateway = gateway_selector.select();
+ if(gateway != null && gateway.getProfile() != null) {
+ mReceiver = EipFragment.getReceiver();
+ launchActiveGateway();
+ }
+ tellToReceiver(ACTION_START_EIP, Activity.RESULT_OK);
+ }
+
+ /**
+ * Early routes are routes that block traffic until a new
+ * VpnService is started properly.
+ */
+ private void earlyRoutes() {
+ Intent void_vpn_launcher = new Intent(context, VoidVpnLauncher.class);
+ void_vpn_launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(void_vpn_launcher);
+ }
+
+ private void launchActiveGateway() {
+ Intent intent = new Intent(this,LaunchVPN.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(LaunchVPN.EXTRA_NAME, gateway.getProfile().getName());
+ intent.putExtra(LaunchVPN.EXTRA_HIDELOG, true);
+ startActivity(intent);
+ }
+
+ private void stopEIP() {
+ EipStatus eip_status = EipStatus.getInstance();
+ Log.d(TAG, "stopEip(): eip is connected? " + eip_status.isConnected());
+ int result_code = Activity.RESULT_CANCELED;
+ if(eip_status.isConnected() || eip_status.isConnecting())
+ result_code = Activity.RESULT_OK;
+
+ tellToReceiver(ACTION_STOP_EIP, result_code);
+ }
+
+ /**
+ * Checks the last stored status notified by ics-openvpn
+ * Sends <code>Activity.RESULT_CANCELED</code> to the ResultReceiver that made the
+ * request if it's not connected, <code>Activity.RESULT_OK</code> otherwise.
+ */
+ private void isRunning() {
+ EipStatus eip_status = EipStatus.getInstance();
+ int resultCode = (eip_status.isConnected()) ?
+ Activity.RESULT_OK :
+ Activity.RESULT_CANCELED;
+ tellToReceiver(ACTION_IS_EIP_RUNNING, resultCode);
+ }
+
+ /**
+ * Loads eip-service.json from SharedPreferences, delete previous vpn profiles and add new gateways.
+ * TODO Implement API call to refresh eip-service.json from the provider
+ */
+ private void updateEIPService() {
+ refreshEipDefinition();
+ deleteAllVpnProfiles();
+ updateGateways();
+ tellToReceiver(ACTION_UPDATE_EIP_SERVICE, Activity.RESULT_OK);
+ }
+
+ private void refreshEipDefinition() {
+ try {
+ String eip_definition_string = preferences.getString(KEY, "");
+ if(!eip_definition_string.isEmpty()) {
+ eip_definition = new JSONObject(eip_definition_string);
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void deleteAllVpnProfiles() {
+ Collection<VpnProfile> profiles = profile_manager.getProfiles();
+ profiles.removeAll(profiles);
+ gateways.clear();
+ }
+
+ /**
+ * Walk the list of gateways defined in eip-service.json and parse them into
+ * Gateway objects.
+ * TODO Store the Gateways (as Serializable) in SharedPreferences
+ */
+ private void updateGateways(){
+ try {
+ if(eip_definition != null) {
+ JSONArray gatewaysDefined = eip_definition.getJSONArray("gateways");
+ for (int i = 0; i < gatewaysDefined.length(); i++) {
+ JSONObject gw = gatewaysDefined.getJSONObject(i);
+ if (isOpenVpnGateway(gw)) {
+ addGateway(new Gateway(eip_definition, context, gw));
+ }
+ }
+ }
+ } catch (JSONException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private boolean isOpenVpnGateway(JSONObject gateway) {
+ try {
+ String transport = gateway.getJSONObject("capabilities").getJSONArray("transport").toString();
+ return transport.contains("openvpn");
+ } catch (JSONException e) {
+ return false;
+ }
+ }
+
+ private void addGateway(Gateway gateway) {
+ profile_manager.addProfile(gateway.getProfile());
+ gateways.add(gateway);
+ }
+
+ private void checkCertValidity() {
+ VpnCertificateValidator validator = new VpnCertificateValidator();
+ int resultCode = validator.isValid(preferences.getString(CERTIFICATE, "")) ?
+ Activity.RESULT_OK :
+ Activity.RESULT_CANCELED;
+ tellToReceiver(ACTION_CHECK_CERT_VALIDITY, resultCode);
+ }
+
+ private void tellToReceiver(String action, int resultCode) {
+ if (mReceiver != null){
+ Bundle resultData = new Bundle();
+ resultData.putString(REQUEST_TAG, action);
+ mReceiver.send(resultCode, resultData);
+ }
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
new file mode 100644
index 00000000..4ac3bd6a
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/EipStatus.java
@@ -0,0 +1,138 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.util.Log;
+
+import java.util.Observable;
+
+import de.blinkt.openvpn.core.VpnStatus;
+
+public class EipStatus extends Observable implements VpnStatus.StateListener {
+ public static String TAG = EipStatus.class.getSimpleName();
+ private static EipStatus current_status;
+
+ private static VpnStatus.ConnectionStatus level = VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED;
+ private static boolean wants_to_disconnect = false;
+
+ private String state, log_message;
+ private int localized_res_id;
+
+ public static EipStatus getInstance() {
+ if(current_status == null) {
+ current_status = new EipStatus();
+ VpnStatus.addStateListener(current_status);
+ }
+ return current_status;
+ }
+
+ private EipStatus() { }
+
+ @Override
+ public void updateState(final String state, final String logmessage, final int localizedResId, final VpnStatus.ConnectionStatus level) {
+ current_status = getInstance();
+ current_status.setState(state);
+ current_status.setLogMessage(logmessage);
+ current_status.setLocalizedResId(localizedResId);
+ current_status.setLevel(level);
+ current_status.setChanged();
+ if(isConnected() || isDisconnected())
+ setConnectedOrDisconnected();
+ else if(isConnecting())
+ setConnecting();
+ Log.d(TAG, "update state with level " + level);
+ current_status.notifyObservers();
+ }
+
+ public boolean wantsToDisconnect() {
+ return wants_to_disconnect;
+ }
+
+ public boolean isConnecting() {
+ return
+ !isConnected() &&
+ !isDisconnected() &&
+ !isPaused();
+ }
+
+ public boolean isConnected() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_CONNECTED;
+ }
+
+ public boolean isDisconnected() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_NOTCONNECTED;
+ }
+
+ public boolean isPaused() {
+ return level == VpnStatus.ConnectionStatus.LEVEL_VPNPAUSED;
+ }
+
+ public void setConnecting() {
+ wants_to_disconnect = false;
+ current_status.setChanged();
+ current_status.notifyObservers();
+ }
+
+ public void setConnectedOrDisconnected() {
+ Log.d(TAG, "setConnectedOrDisconnected()");
+ wants_to_disconnect = false;
+ current_status.setChanged();
+ current_status.notifyObservers();
+ }
+
+ public void setDisconnecting() {
+ wants_to_disconnect = false;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getLogMessage() {
+ return log_message;
+ }
+
+ public int getLocalizedResId() {
+ return localized_res_id;
+ }
+
+ public VpnStatus.ConnectionStatus getLevel() {
+ return level;
+ }
+
+ private void setState(String state) {
+ this.state = state;
+ }
+
+ private void setLogMessage(String log_message) {
+ this.log_message = log_message;
+ }
+
+ private void setLocalizedResId(int localized_res_id) {
+ this.localized_res_id = localized_res_id;
+ }
+
+ private void setLevel(VpnStatus.ConnectionStatus level) {
+ EipStatus.level = level;
+ }
+
+ @Override
+ public String toString() {
+ return "State: " + state + " Level: " + level.toString();
+ }
+
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
new file mode 100644
index 00000000..3ee9443c
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/Gateway.java
@@ -0,0 +1,156 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Collection;
+import java.util.Iterator;
+
+import de.blinkt.openvpn.VpnProfile;
+import de.blinkt.openvpn.core.ConfigParser;
+import de.blinkt.openvpn.core.ProfileManager;
+import se.leap.bitmaskclient.Dashboard;
+
+/**
+ * Gateway provides objects defining gateways and their metadata.
+ * Each instance contains a VpnProfile for OpenVPN specific data and member
+ * variables describing capabilities and location (name)
+ *
+ * @author Sean Leonard <meanderingcode@aetherislands.net>
+ * @author Parménides GV <parmegv@sdf.org>
+ */
+public class Gateway {
+
+ private String TAG = Gateway.class.getSimpleName();
+
+ private String mName;
+ private int timezone;
+ private JSONObject general_configuration;
+ private Context context;
+ private VpnProfile mVpnProfile;
+ private JSONObject mGateway;
+
+ /**
+ * Build a gateway object from a JSON OpenVPN gateway definition in eip-service.json
+ * and create a VpnProfile belonging to it.
+ *
+ * @param gateway The JSON OpenVPN gateway definition to parse
+ */
+ protected Gateway(JSONObject eip_definition, Context context, JSONObject gateway){
+
+ mGateway = gateway;
+
+ this.context = context;
+ general_configuration = getGeneralConfiguration(eip_definition);
+ timezone = getTimezone(eip_definition);
+ mName = locationAsName(eip_definition);
+
+ // Currently deletes VpnProfile for host, if there already is one, and builds new
+ ProfileManager vpl = ProfileManager.getInstance(context);
+ Collection<VpnProfile> profiles = vpl.getProfiles();
+ for (Iterator<VpnProfile> it = profiles.iterator(); it.hasNext(); ){
+ VpnProfile p = it.next();
+
+ if ( p.mName.equalsIgnoreCase( mName ) ) {
+ it.remove();
+ vpl.removeProfile(context, p);
+ }
+ }
+
+ mVpnProfile = createVPNProfile();
+ mVpnProfile.mName = mName;
+
+ vpl.addProfile(mVpnProfile);
+ vpl.saveProfile(context, mVpnProfile);
+ vpl.saveProfileList(context);
+ }
+
+ private JSONObject getGeneralConfiguration(JSONObject eip_definition) {
+ try {
+ return eip_definition.getJSONObject("openvpn_configuration");
+ } catch (JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ private int getTimezone(JSONObject eip_definition) {
+ JSONObject location = getLocationInfo(eip_definition);
+ return location.optInt("timezone");
+ }
+
+ private String locationAsName(JSONObject eip_definition) {
+ JSONObject location = getLocationInfo(eip_definition);
+ return location.optString("name");
+ }
+
+ private JSONObject getLocationInfo(JSONObject eip_definition) {
+ try {
+ JSONObject locations = eip_definition.getJSONObject("locations");
+
+ return locations.getJSONObject(mGateway.getString("location"));
+ } catch (JSONException e) {
+ return new JSONObject();
+ }
+ }
+
+ /**
+ * Create and attach the VpnProfile to our gateway object
+ */
+ private VpnProfile createVPNProfile(){
+ try {
+ ConfigParser cp = new ConfigParser();
+
+ SharedPreferences preferences = context.getSharedPreferences(Dashboard.SHARED_PREFERENCES, Activity.MODE_PRIVATE);
+ VpnConfigGenerator vpn_configuration_generator = new VpnConfigGenerator(preferences, general_configuration, mGateway);
+ String configuration = vpn_configuration_generator.generate();
+
+ cp.parseConfig(new StringReader(configuration));
+ return cp.convertProfile();
+ } catch (ConfigParser.ConfigParseError e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ // FIXME We didn't get a VpnProfile! Error handling! and log level
+ Log.v(TAG,"Error creating VPNProfile");
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public VpnProfile getProfile() {
+ return mVpnProfile;
+ }
+
+ public int getTimezone() {
+ return timezone;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
new file mode 100644
index 00000000..39ae7ca6
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/GatewaySelector.java
@@ -0,0 +1,46 @@
+package se.leap.bitmaskclient.eip;
+
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class GatewaySelector {
+ List<Gateway> gateways;
+
+ public GatewaySelector(List<Gateway> gateways) {
+ this.gateways = gateways;
+ }
+
+ public Gateway select() {
+ return closestGateway();
+ }
+
+ private Gateway closestGateway() {
+ TreeMap<Integer, Set<Gateway>> offsets = calculateOffsets();
+ return offsets.isEmpty() ? null : offsets.firstEntry().getValue().iterator().next();
+ }
+
+ private TreeMap<Integer, Set<Gateway>> calculateOffsets() {
+ TreeMap<Integer, Set<Gateway>> offsets = new TreeMap<Integer, Set<Gateway>>();
+ int localOffset = Calendar.getInstance().get(Calendar.ZONE_OFFSET) / 3600000;
+ for(Gateway gateway : gateways) {
+ int dist = timezoneDistance(localOffset, gateway.getTimezone());
+ Set<Gateway> set = (offsets.get(dist) != null) ?
+ offsets.get(dist) : new HashSet<Gateway>();
+ set.add(gateway);
+ offsets.put(dist, set);
+ }
+ return offsets;
+ }
+
+ private int timezoneDistance(int local_timezone, int remote_timezone) {
+ // Distance along the numberline of Prime Meridian centric, assumes UTC-11 through UTC+12
+ int dist = Math.abs(local_timezone - remote_timezone);
+ // Farther than 12 timezones and it's shorter around the "back"
+ if (dist > 12)
+ dist = 12 - (dist -12); // Well i'll be. Absolute values make equations do funny things.
+ return dist;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
index 3b286fbf..d79d8003 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VoidVpnLauncher.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnLauncher.java
@@ -1,4 +1,4 @@
-package se.leap.bitmaskclient;
+package se.leap.bitmaskclient.eip;
import android.app.Activity;
import android.content.Intent;
@@ -8,7 +8,7 @@ import android.os.Bundle;
public class VoidVpnLauncher extends Activity {
private static final int VPN_USER_PERMISSION = 71;
-
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -28,7 +28,7 @@ public class VoidVpnLauncher extends Activity {
if(requestCode == VPN_USER_PERMISSION) {
if(resultCode == RESULT_OK) {
Intent void_vpn_service = new Intent(getApplicationContext(), VoidVpnService.class);
- void_vpn_service.setAction(VoidVpnService.START_BLOCKING_VPN_PROFILE);
+ void_vpn_service.setAction(Constants.START_BLOCKING_VPN_PROFILE);
startService(void_vpn_service);
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
index 7b597554..0c773208 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VoidVpnService.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VoidVpnService.java
@@ -1,19 +1,20 @@
-package se.leap.bitmaskclient;
+package se.leap.bitmaskclient.eip;
import android.content.Intent;
-import android.os.Process;
import android.net.VpnService;
-import android.util.Log;
+import android.os.ParcelFileDescriptor;
+
+import java.io.IOException;
public class VoidVpnService extends VpnService {
- static final String START_BLOCKING_VPN_PROFILE = "se.leap.bitmaskclient.START_BLOCKING_VPN_PROFILE";
static final String TAG = VoidVpnService.class.getSimpleName();
+ static ParcelFileDescriptor fd;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- String action = intent.getAction();
- if (action == START_BLOCKING_VPN_PROFILE) {
+ String action = intent != null ? intent.getAction() : "";
+ if (action == Constants.START_BLOCKING_VPN_PROFILE) {
new Thread(new Runnable() {
public void run() {
Builder builder = new Builder();
@@ -23,7 +24,7 @@ public class VoidVpnService extends VpnService {
builder.addRoute("192.168.1.0", 24);
builder.addDnsServer("10.42.0.1");
try {
- builder.establish();
+ fd = builder.establish();
} catch (Exception e) {
e.printStackTrace();
}
@@ -33,4 +34,20 @@ public class VoidVpnService extends VpnService {
}
return 0;
}
+
+ @Override
+ public void onRevoke() {
+ super.onRevoke();
+ }
+
+ public static boolean stop() {
+ try {
+ fd.close();
+ return true;
+ } catch (IOException | NullPointerException e) {
+ android.util.Log.d(TAG, "VoidVpnService didn't stop");
+ e.printStackTrace();
+ return false;
+ }
+ }
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
new file mode 100644
index 00000000..6487f6c1
--- /dev/null
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -0,0 +1,60 @@
+/**
+ * Copyright (c) 2013 LEAP Encryption Access Project and contributers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.eip;
+
+import android.util.Log;
+
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Calendar;
+
+import se.leap.bitmaskclient.ConfigHelper;
+
+public class VpnCertificateValidator {
+ public final static String TAG = VpnCertificateValidator.class.getSimpleName();
+
+ public boolean isValid(String certificate) {
+ if(!certificate.isEmpty()) {
+ X509Certificate certificate_x509 = ConfigHelper.parseX509CertificateFromString(certificate);
+ return isValid(certificate_x509);
+ } else return true;
+ }
+
+ private boolean isValid(X509Certificate certificate) {
+ Calendar offset_date = calculateOffsetCertificateValidity(certificate);
+ try {
+ Log.d(TAG, "offset_date = " + offset_date.getTime().toString());
+ certificate.checkValidity(offset_date.getTime());
+ return true;
+ } catch(CertificateExpiredException e) {
+ return false;
+ } catch(CertificateNotYetValidException e) {
+ return false;
+ }
+ }
+
+ private Calendar calculateOffsetCertificateValidity(X509Certificate certificate) {
+ Log.d(TAG, "certificate not after = " + certificate.getNotAfter());
+ long preventive_time = Math.abs(certificate.getNotBefore().getTime() - certificate.getNotAfter().getTime())/2;
+ long current_date_millis = Calendar.getInstance().getTimeInMillis();
+
+ Calendar limit_date = Calendar.getInstance();
+ limit_date.setTimeInMillis(current_date_millis + preventive_time);
+ return limit_date;
+ }
+}
diff --git a/app/src/main/java/se/leap/bitmaskclient/VpnConfigGenerator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
index ef049a3c..0c8e9a04 100644
--- a/app/src/main/java/se/leap/bitmaskclient/VpnConfigGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnConfigGenerator.java
@@ -14,18 +14,18 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-package se.leap.bitmaskclient;
+package se.leap.bitmaskclient.eip;
import android.content.SharedPreferences;
import android.util.Log;
-import java.util.Iterator;
-import java.util.Vector;
+
import org.json.JSONArray;
-import org.json.JSONObject;
import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Iterator;
import se.leap.bitmaskclient.Provider;
-import se.leap.bitmaskclient.EIP;
public class VpnConfigGenerator {
@@ -39,7 +39,7 @@ public class VpnConfigGenerator {
public VpnConfigGenerator(SharedPreferences preferences, JSONObject general_configuration, JSONObject gateway) {
this.general_configuration = general_configuration;
this.gateway = gateway;
- this.preferences = preferences;
+ VpnConfigGenerator.preferences = preferences;
}
public String generate() {
@@ -57,7 +57,6 @@ public class VpnConfigGenerator {
String common_options = "";
try {
Iterator keys = general_configuration.keys();
- Vector<Vector<String>> value = new Vector<Vector<String>>();
while ( keys.hasNext() ){
String key = keys.next().toString();
@@ -121,14 +120,14 @@ public class VpnConfigGenerator {
String key =
"<key>"
+ new_line
- + preferences.getString(EIP.PRIVATE_KEY, "")
+ + preferences.getString(Constants.PRIVATE_KEY, "")
+ new_line
+ "</key>";
String openvpn_cert =
"<cert>"
+ new_line
- + preferences.getString(EIP.CERTIFICATE, "")
+ + preferences.getString(Constants.CERTIFICATE, "")
+ new_line
+ "</cert>";
diff --git a/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png
new file mode 100644
index 00000000..0fd15563
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
new file mode 100644
index 00000000..a9eac0ca
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.png b/app/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.png
new file mode 100644
index 00000000..b5f88c80
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_edit_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_edit_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_edit_white_24dp.png
new file mode 100644
index 00000000..730416c9
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_edit_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png
new file mode 100644
index 00000000..30122adf
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_filter_list_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
new file mode 100644
index 00000000..93b3c219
--- /dev/null
+++ b/app/src/main/res/drawable-hdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png
new file mode 100644
index 00000000..e80681ae
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_delete_white_24dp.png
new file mode 100644
index 00000000..e4ea52ef
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png b/app/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png
new file mode 100644
index 00000000..bae3480c
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_edit_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_edit_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_edit_white_24dp.png
new file mode 100644
index 00000000..85cff0b9
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_edit_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_filter_list_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_filter_list_white_24dp.png
new file mode 100644
index 00000000..49cec669
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_filter_list_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
new file mode 100644
index 00000000..4d019722
--- /dev/null
+++ b/app/src/main/res/drawable-mdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
new file mode 100644
index 00000000..76e07f09
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png
new file mode 100644
index 00000000..cdb230c2
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png b/app/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png
new file mode 100644
index 00000000..4c95bd57
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_edit_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_edit_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_edit_white_24dp.png
new file mode 100644
index 00000000..7f0ea51b
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_edit_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_filter_list_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_filter_list_white_24dp.png
new file mode 100644
index 00000000..d4ca77bf
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_filter_list_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
new file mode 100644
index 00000000..dd536bca
--- /dev/null
+++ b/app/src/main/res/drawable-xhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
new file mode 100644
index 00000000..0eb9d8b0
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png
new file mode 100644
index 00000000..0e95e9b1
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.png
new file mode 100644
index 00000000..6ed4351c
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_edit_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png
new file mode 100644
index 00000000..34ec7092
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_edit_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_filter_list_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_filter_list_white_24dp.png
new file mode 100644
index 00000000..802b3cd5
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_filter_list_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
new file mode 100644
index 00000000..9963c6a0
--- /dev/null
+++ b/app/src/main/res/drawable-xxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
new file mode 100644
index 00000000..7b2a480a
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_close_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png
new file mode 100644
index 00000000..ccf8c716
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_delete_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png
new file mode 100644
index 00000000..0c0fd76f
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_edit_grey600_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png
new file mode 100644
index 00000000..9380370f
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_edit_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_filter_list_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_filter_list_white_24dp.png
new file mode 100644
index 00000000..511008ce
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_filter_list_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
new file mode 100644
index 00000000..bb521c14
--- /dev/null
+++ b/app/src/main/res/drawable-xxxhdpi/ic_share_white_24dp.png
Binary files differ
diff --git a/app/src/main/res/layout-xlarge/client_dashboard.xml b/app/src/main/res/layout-xlarge/dashboard.xml
index bd644e1e..bd644e1e 100644
--- a/app/src/main/res/layout-xlarge/client_dashboard.xml
+++ b/app/src/main/res/layout-xlarge/dashboard.xml
diff --git a/app/src/main/res/layout-xlarge/eip_service_fragment.xml b/app/src/main/res/layout-xlarge/eip_service_fragment.xml
index e5c7f23d..38b6aca3 100644
--- a/app/src/main/res/layout-xlarge/eip_service_fragment.xml
+++ b/app/src/main/res/layout-xlarge/eip_service_fragment.xml
@@ -37,39 +37,20 @@
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp" />
- <RelativeLayout
- android:id="@+id/eipDetail"
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
android:layout_below="@+id/eipLabel"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
- android:visibility="gone" >
-
- <ImageView
- android:id="@+id/eipSettings"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_margin="10dp"
- android:contentDescription="@string/eip_settings_button_description"
- android:src="@drawable/ic_sysbar_quicksettings" />
-
- <TextView
- android:id="@+id/eipStatus"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:clickable="true"
- android:text="@string/status_unknown"
- android:textSize="16sp" />
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:clickable="true"
+ android:text="@string/eip_state_not_connected"
+ android:textSize="16sp" />
- </RelativeLayout>
</RelativeLayout>
diff --git a/app/src/main/res/layout-xlarge/provider_list_fragment.xml b/app/src/main/res/layout-xlarge/provider_list_fragment.xml
deleted file mode 100644
index 59dd37d1..00000000
--- a/app/src/main/res/layout-xlarge/provider_list_fragment.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingLeft="12dp"
- android:paddingRight="12dp"
- android:paddingTop="12dp" >
-
- <ListView
- android:id="@id/android:list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:drawSelectorOnTop="false" />
-
-</LinearLayout>
diff --git a/app/src/main/res/layout-xlarge/log_in_dialog.xml b/app/src/main/res/layout-xlarge/session_dialog.xml
index 3a9eebb8..3a9eebb8 100644
--- a/app/src/main/res/layout-xlarge/log_in_dialog.xml
+++ b/app/src/main/res/layout-xlarge/session_dialog.xml
diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml
index ccb1ea26..2669caa3 100644
--- a/app/src/main/res/layout/about.xml
+++ b/app/src/main/res/layout/about.xml
@@ -35,6 +35,17 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="all"
+ android:text="@string/copyright_blinktgui" />
+
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="10sp" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autoLink="all"
android:text="@string/repository_url_text" />
<TextView
diff --git a/app/src/main/res/layout/configuration_wizard_activity.xml b/app/src/main/res/layout/configuration_wizard_activity.xml
index f3d0e48b..a5bca1e9 100644
--- a/app/src/main/res/layout/configuration_wizard_activity.xml
+++ b/app/src/main/res/layout/configuration_wizard_activity.xml
@@ -5,6 +5,12 @@
android:layout_height="match_parent"
tools:context=".ConfigurationWizard" >
+ <ListView
+ android:id="@+id/provider_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false" />
+
<ProgressBar
android:id="@+id/progressbar_configuration_wizard"
style="?android:attr/progressBarStyleHorizontal"
@@ -22,5 +28,4 @@
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_centerHorizontal="true"
android:textColor="@android:color/holo_blue_bright" />
-
</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/client_dashboard.xml b/app/src/main/res/layout/dashboard.xml
index f33ac285..67a1122f 100644
--- a/app/src/main/res/layout/client_dashboard.xml
+++ b/app/src/main/res/layout/dashboard.xml
@@ -25,6 +25,7 @@
android:id="@+id/providerName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textSize="28sp"
android:layout_marginLeft="10dp"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
diff --git a/app/src/main/res/layout/eip_service_fragment.xml b/app/src/main/res/layout/eip_service_fragment.xml
index 5992a873..64d22147 100644
--- a/app/src/main/res/layout/eip_service_fragment.xml
+++ b/app/src/main/res/layout/eip_service_fragment.xml
@@ -22,8 +22,8 @@
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginRight="10dp" />
-
- <ProgressBar
+
+ <ProgressBar
android:id="@+id/eipProgress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -34,38 +34,18 @@
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp" />
- <RelativeLayout
- android:id="@+id/eipDetail"
- android:layout_width="match_parent"
+ <TextView
+ android:id="@+id/status_message"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_below="@+id/eipLabel"
+ android:layout_below="@id/eipProgress"
+ android:layout_centerVertical="true"
+ android:paddingTop="5dp"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
- android:paddingTop="10dp"
- android:visibility="gone" >
-
- <ImageView
- android:id="@+id/eipSettings"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_margin="10dp"
- android:contentDescription="@string/eip_settings_button_description"
- android:src="@drawable/ic_sysbar_quicksettings" />
-
- <TextView
- android:id="@+id/eipStatus"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:clickable="true"
- android:text="@string/status_unknown" />
-
- </RelativeLayout>
+ android:clickable="true"
+ android:text="@string/eip_state_not_connected" />
</RelativeLayout>
diff --git a/app/src/main/res/layout/provider_list_fragment.xml b/app/src/main/res/layout/provider_list_fragment.xml
deleted file mode 100644
index 70dbae0d..00000000
--- a/app/src/main/res/layout/provider_list_fragment.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingLeft="8dp"
- android:paddingRight="8dp" >
-
- <ListView
- android:id="@id/android:list"
- android:layout_width="match_parent"
- android:layout_height="0dip"
- android:layout_weight="1"
- android:drawSelectorOnTop="false" />
-
-</LinearLayout>
diff --git a/app/src/main/res/layout/provider_list_item.xml b/app/src/main/res/layout/provider_list_item.xml
index 8746f6f8..68ba7e31 100644
--- a/app/src/main/res/layout/provider_list_item.xml
+++ b/app/src/main/res/layout/provider_list_item.xml
@@ -24,7 +24,7 @@
android:mode="twoLine"
>
- <TextView android:id="@android:id/text1"
+ <TextView android:id="@+id/provider_domain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="?android:attr/listPreferredItemPaddingLeft"
@@ -32,11 +32,11 @@
android:textAppearance="?android:attr/textAppearanceListItem"
/>
- <TextView android:id="@android:id/text2"
+ <TextView android:id="@+id/provider_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_below="@android:id/text1"
- android:layout_alignLeft="@android:id/text1"
+ android:layout_below="@id/provider_domain"
+ android:layout_alignLeft="@id/provider_domain"
android:textAppearance="?android:attr/textAppearanceSmall"
/>
diff --git a/app/src/main/res/layout/log_in_dialog.xml b/app/src/main/res/layout/session_dialog.xml
index c8a2f0a8..62215ae8 100644
--- a/app/src/main/res/layout/log_in_dialog.xml
+++ b/app/src/main/res/layout/session_dialog.xml
@@ -4,13 +4,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- tools:context=".LogInDialog" >
+ tools:context=".SessionDialog" >
<TextView
android:id="@+id/user_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
+ android:visibility="gone"
android:textAppearance="?android:attr/textAppearanceMedium" />
<EditText
diff --git a/app/src/main/res/menu/logmenu.xml b/app/src/main/res/menu/logmenu.xml
index a1d2a7b5..52ba4b7d 100644
--- a/app/src/main/res/menu/logmenu.xml
+++ b/app/src/main/res/menu/logmenu.xml
@@ -10,33 +10,33 @@
<item
android:id="@+id/toggle_time"
android:alphabeticShortcut="t"
- android:icon="@android:drawable/ic_menu_view"
+ android:icon="@drawable/ic_menu_view"
android:showAsAction="withText|ifRoom"
android:title="@string/logview_options" />
<item
android:id="@+id/clearlog"
- android:icon="@android:drawable/ic_menu_delete"
+ android:icon="@drawable/ic_menu_delete"
android:showAsAction="ifRoom|withText"
android:title="@string/clear_log"
android:titleCondensed="@string/clear"/>
<item
android:id="@+id/send"
- android:icon="@android:drawable/ic_menu_share"
+ android:icon="@drawable/ic_menu_share"
android:showAsAction="ifRoom|withText"
android:title="@string/send_logfile"
android:titleCondensed="@string/send"/>
<item
android:id="@+id/cancel"
- android:icon="@android:drawable/ic_menu_close_clear_cancel"
+ android:icon="@drawable/ic_menu_close_clear_cancel"
android:showAsAction="ifRoom|withText"
android:title="@string/cancel_connection_long"
android:titleCondensed="@string/cancel_connection"/>
<item
android:id="@+id/edit_vpn"
android:alphabeticShortcut="e"
- android:icon="@android:drawable/ic_menu_edit"
+ android:icon="@drawable/ic_menu_edit"
android:showAsAction="withText|ifRoom"
android:title="@string/edit_vpn"
android:visible="false"/>
diff --git a/app/src/main/res/values-v21/refs.xml b/app/src/main/res/values-v21/refs.xml
new file mode 100644
index 00000000..0d5d271a
--- /dev/null
+++ b/app/src/main/res/values-v21/refs.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ -->
+
+<resources>
+ <drawable name="ic_menu_close_clear_cancel">@drawable/ic_close_white_24dp</drawable>
+ <drawable name="ic_menu_share">@drawable/ic_share_white_24dp </drawable>
+ <drawable name="ic_menu_view">@drawable/ic_filter_list_white_24dp</drawable>
+ <drawable name="ic_menu_delete">@drawable/ic_delete_white_24dp</drawable>
+ <drawable name="ic_menu_edit">@drawable/ic_edit_white_24dp</drawable>
+</resources>
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
new file mode 100644
index 00000000..892b6cb0
--- /dev/null
+++ b/app/src/main/res/values-v21/styles.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ -->
+
+<resources>
+
+ <!-- http://www.google.de/design/spec/style/color.html#color-color-palette -->
+ <style name="appstyle" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="android:colorPrimary">@color/primary</item>
+ <item name="android:colorPrimaryDark">@color/primary_dark</item>
+ <item name="android:colorAccent">@color/accent</item>
+ </style>
+</resources>
diff --git a/app/src/main/res/values/colours.xml b/app/src/main/res/values/colours.xml
new file mode 100644
index 00000000..89fb41dd
--- /dev/null
+++ b/app/src/main/res/values/colours.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ -->
+
+<resources>
+ <!-- Indigo -->
+ <!-- OpenVPN colours #203155, #C66D0D -->
+ <color name="primary">#3F51B5</color>
+ <color name="primary_dark">#303F9F</color>
+ <color name="accent">#FFA726</color>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/values/refs.xml b/app/src/main/res/values/refs.xml
new file mode 100644
index 00000000..5e7f5e14
--- /dev/null
+++ b/app/src/main/res/values/refs.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (c) 2012-2014 Arne Schwabe
+ ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt
+ -->
+
+<resources>
+ <drawable name="ic_menu_close_clear_cancel">@android:drawable/ic_menu_close_clear_cancel</drawable>
+ <drawable name="ic_menu_share">@android:drawable/ic_menu_share </drawable>
+ <drawable name="ic_menu_save">@android:drawable/ic_menu_save</drawable>
+ <drawable name="ic_menu_view">@android:drawable/ic_menu_view</drawable>
+ <drawable name="ic_menu_delete">@android:drawable/ic_menu_delete</drawable>
+ <drawable name="ic_menu_edit">@android:drawable/ic_menu_edit</drawable>
+
+</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3666b39c..68e71886 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -60,7 +60,7 @@
<string name="signingup_message">Signing up</string>
<string name="logout_message">Logging out from this session.</string>
<string name="logged_out_message">Logged out.</string>
- <string name="log_out_failed_message">Didn\'t logged out.</string>
+ <string name="log_out_failed_message">Didn\'t log out.</string>
<string name="succesful_authentication_message">Authentication succeeded.</string>
<string name="authentication_failed_message">Authentication failed.</string>
<string name="registration_failed_message">Registration failed..</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 7b26a4a7..a60e29b8 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -5,6 +5,10 @@
-->
<resources>
+ <style name="appstyle" parent="android:Theme.DeviceDefault.Light">
+
+ </style>
+
<style name="item">
<item name="android:layout_width">match_parent</item>
@@ -23,7 +27,7 @@
<item name="android:paddingTop">10sp</item>
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
- <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
+ <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
<!-- <item name="android:singleLine">true</item> -->
</style>
diff --git a/app/src/main/res/values/untranslatable.xml b/app/src/main/res/values/untranslatable.xml
index 619a550f..82147ab5 100644
--- a/app/src/main/res/values/untranslatable.xml
+++ b/app/src/main/res/values/untranslatable.xml
@@ -2470,4 +2470,704 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
</resources> \ No newline at end of file