From d9c5e6d786c024ec2d2ba841e24802a5daa8d1b9 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Wed, 27 Apr 2016 14:55:43 +0200
Subject: [PATCH 01/24] android: Fix handling of redirects during IKE_AUTH
---
.../backend/android_service.c | 153 ++++++++++--------
1 file changed, 84 insertions(+), 69 deletions(-)
diff --git a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c
index b3e91d808..3db0f744d 100644
--- a/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c
+++ b/src/frontends/android/app/src/main/jni/libandroidbridge/backend/android_service.c
@@ -1,8 +1,8 @@
/*
- * Copyright (C) 2010-2015 Tobias Brunner
+ * Copyright (C) 2010-2016 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
- * Hochschule fuer Technik Rapperswil
+ * HSR Hochschule fuer Technik Rapperswil
*
* 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
@@ -430,6 +430,84 @@ CALLBACK(reestablish, job_requeue_t,
return JOB_REQUEUE_NONE;
}
+METHOD(listener_t, ike_updown, bool,
+ private_android_service_t *this, ike_sa_t *ike_sa, bool up)
+{
+ /* this callback is only registered during initiation, so if the IKE_SA
+ * goes down we assume some kind of authentication error, more specific
+ * errors are catched in the alert() handler */
+ if (this->ike_sa == ike_sa && !up)
+ {
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_AUTH_ERROR);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, ike_rekey, bool,
+ private_android_service_t *this, ike_sa_t *old, ike_sa_t *new)
+{
+ if (this->ike_sa == old)
+ {
+ this->ike_sa = new;
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, ike_reestablish_post_redirect, bool,
+ private_android_service_t *this, ike_sa_t *old, ike_sa_t *new,
+ bool initiated)
+{
+ if (this->ike_sa == old && initiated)
+ { /* if we get redirected during IKE_AUTH we just migrate to the new SA,
+ * we don't have a TUN device yet, so reinstalling it without DNS would
+ * fail (and using the DNS proxy is not required anyway) */
+ this->ike_sa = new;
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, ike_reestablish_pre, bool,
+ private_android_service_t *this, ike_sa_t *old, ike_sa_t *new)
+{
+ if (this->ike_sa == old)
+ {
+ /* enable DNS proxy so hosts are properly resolved while the TUN device
+ * is still active */
+ this->lock->write_lock(this->lock);
+ this->use_dns_proxy = TRUE;
+ this->lock->unlock(this->lock);
+ /* if DNS servers are installed that are only reachable through the VPN
+ * the DNS proxy doesn't help, so uninstall DNS servers */
+ if (!setup_tun_device_without_dns(this))
+ {
+ DBG1(DBG_DMN, "failed to setup TUN device without DNS");
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_GENERIC_ERROR);
+ }
+ }
+ return TRUE;
+}
+
+METHOD(listener_t, ike_reestablish_post, bool,
+ private_android_service_t *this, ike_sa_t *old, ike_sa_t *new,
+ bool initiated)
+{
+ if (this->ike_sa == old && initiated)
+ {
+ this->ike_sa = new;
+ /* re-register hook to detect initiation failures */
+ this->public.listener.ike_updown = _ike_updown;
+ /* if the IKE_SA got deleted by the responder we get the child_down()
+ * event on the old IKE_SA after this hook has been called, so they
+ * get ignored and thus we trigger the event here */
+ charonservice->update_status(charonservice,
+ CHARONSERVICE_CHILD_STATE_DOWN);
+ }
+ return TRUE;
+}
+
METHOD(listener_t, child_updown, bool,
private_android_service_t *this, ike_sa_t *ike_sa, child_sa_t *child_sa,
bool up)
@@ -440,6 +518,9 @@ METHOD(listener_t, child_updown, bool,
{
/* disable the hooks registered to catch initiation failures */
this->public.listener.ike_updown = NULL;
+ /* enable hooks to handle reauthentications */
+ this->public.listener.ike_reestablish_pre = _ike_reestablish_pre;
+ this->public.listener.ike_reestablish_post = _ike_reestablish_post;
/* CHILD_SA is up so we can disable the DNS proxy we enabled to
* reestablish the SA */
this->lock->write_lock(this->lock);
@@ -465,21 +546,6 @@ METHOD(listener_t, child_updown, bool,
return TRUE;
}
-METHOD(listener_t, ike_updown, bool,
- private_android_service_t *this, ike_sa_t *ike_sa, bool up)
-{
- /* this callback is only registered during initiation, so if the IKE_SA
- * goes down we assume some kind of authentication error, more specific
- * errors are catched in the alert() handler */
- if (this->ike_sa == ike_sa && !up)
- {
- charonservice->update_status(charonservice,
- CHARONSERVICE_AUTH_ERROR);
- return FALSE;
- }
- return TRUE;
-}
-
METHOD(listener_t, alert, bool,
private_android_service_t *this, ike_sa_t *ike_sa, alert_t alert,
va_list args)
@@ -554,56 +620,6 @@ METHOD(listener_t, alert, bool,
return TRUE;
}
-METHOD(listener_t, ike_rekey, bool,
- private_android_service_t *this, ike_sa_t *old, ike_sa_t *new)
-{
- if (this->ike_sa == old)
- {
- this->ike_sa = new;
- }
- return TRUE;
-}
-
-METHOD(listener_t, ike_reestablish_pre, bool,
- private_android_service_t *this, ike_sa_t *old, ike_sa_t *new)
-{
- if (this->ike_sa == old)
- {
- /* enable DNS proxy so hosts are properly resolved while the TUN device
- * is still active */
- this->lock->write_lock(this->lock);
- this->use_dns_proxy = TRUE;
- this->lock->unlock(this->lock);
- /* if DNS servers are installed that are only reachable through the VPN
- * the DNS proxy doesn't help, so uninstall DNS servers */
- if (!setup_tun_device_without_dns(this))
- {
- DBG1(DBG_DMN, "failed to setup TUN device without DNS");
- charonservice->update_status(charonservice,
- CHARONSERVICE_GENERIC_ERROR);
- }
- }
- return TRUE;
-}
-
-METHOD(listener_t, ike_reestablish_post, bool,
- private_android_service_t *this, ike_sa_t *old, ike_sa_t *new,
- bool initiated)
-{
- if (this->ike_sa == old && initiated)
- {
- this->ike_sa = new;
- /* re-register hook to detect initiation failures */
- this->public.listener.ike_updown = _ike_updown;
- /* if the IKE_SA got deleted by the responder we get the child_down()
- * event on the old IKE_SA after this hook has been called, so they
- * get ignored and thus we trigger the event here */
- charonservice->update_status(charonservice,
- CHARONSERVICE_CHILD_STATE_DOWN);
- }
- return TRUE;
-}
-
static void add_auth_cfg_pw(private_android_service_t *this,
peer_cfg_t *peer_cfg, bool byod)
{
@@ -824,8 +840,7 @@ android_service_t *android_service_create(android_creds_t *creds,
.public = {
.listener = {
.ike_rekey = _ike_rekey,
- .ike_reestablish_pre = _ike_reestablish_pre,
- .ike_reestablish_post = _ike_reestablish_post,
+ .ike_reestablish_post = _ike_reestablish_post_redirect,
.ike_updown = _ike_updown,
.child_updown = _child_updown,
.alert = _alert,
From 1bd213db79f6ed1666f22c82ca99added38eeb89 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Wed, 27 Apr 2016 15:11:54 +0200
Subject: [PATCH 02/24] android: Use relative path for strongSwan sources
This avoids issues with recursion, which could have happened if the
strongswan directory was a symlink.
---
.../android/app/src/main/jni/Android.mk | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/frontends/android/app/src/main/jni/Android.mk b/src/frontends/android/app/src/main/jni/Android.mk
index 9e81a14c5..5bbfeaa1b 100644
--- a/src/frontends/android/app/src/main/jni/Android.mk
+++ b/src/frontends/android/app/src/main/jni/Android.mk
@@ -16,12 +16,14 @@ endif
strongswan_PLUGINS := $(strongswan_CHARON_PLUGINS) \
$(strongswan_BYOD_PLUGINS)
-include $(LOCAL_PATH)/strongswan/Android.common.mk
+strongswan_DIR := ../../../../../../../
# includes
-strongswan_PATH := $(LOCAL_PATH)/strongswan
+strongswan_PATH := $(LOCAL_PATH)/$(strongswan_DIR)
openssl_PATH := $(LOCAL_PATH)/openssl/include
+include $(strongswan_PATH)/Android.common.mk
+
# CFLAGS (partially from a configure run using droid-gcc)
strongswan_CFLAGS := \
-Wall \
@@ -67,15 +69,15 @@ endif
strongswan_BUILD := \
openssl \
libandroidbridge \
- strongswan/src/libipsec \
- strongswan/src/libcharon \
- strongswan/src/libstrongswan
+ $(strongswan_DIR)/src/libipsec \
+ $(strongswan_DIR)/src/libcharon \
+ $(strongswan_DIR)/src/libstrongswan
ifneq ($(strongswan_USE_BYOD),)
strongswan_BUILD += \
- strongswan/src/libtnccs \
- strongswan/src/libtncif \
- strongswan/src/libimcv
+ $(strongswan_DIR)/src/libtnccs \
+ $(strongswan_DIR)/src/libtncif \
+ $(strongswan_DIR)/src/libimcv
endif
include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
From 3256fe9ebb0c530a2ecf4ac977a32c8e099dc3ca Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Wed, 27 Apr 2016 15:21:03 +0200
Subject: [PATCH 03/24] android: Update README.ndk
---
src/frontends/android/README.ndk | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/src/frontends/android/README.ndk b/src/frontends/android/README.ndk
index 7c8cd309e..22150dd55 100644
--- a/src/frontends/android/README.ndk
+++ b/src/frontends/android/README.ndk
@@ -1,13 +1,14 @@
-To build this within the NDK several things have to be added in the
-app/src/main/jni/ folder:
+To build this within the NDK the following things have to be done:
- - strongswan: The strongSwan sources. This can either be an extracted tarball,
- or a symlink to the Git repository. To build from the repository the sources
- have to be prepared first (see HACKING for a list of required tools):
+ - By default the strongSwan sources of the current Git tree are used. They have
+ to be prepared first (see HACKING for a list of required tools):
./autogen.sh && ./configure && make && make distclean
- - openssl: The OpenSSL sources. Since the sources need to be changed to be
- built on Android (and especially in the NDK), we provide a modified mirror
- of the official Android OpenSSL version on git.strongswan.org.
+ It is also possible to use the sources from a different directory (e.g. an
+ extracted tarball) by setting strongswan_DIR in app/src/main/jni/Android.mk.
+ - The OpenSSL or BoringSSL sources are expected in app/src/main/jni/openssl.
+ Since the sources need to be changed to be built on Android (and especially
+ in the NDK) we provide a modified mirror of the official Android repositories
+ on git.strongswan.org.
From 7c5fec3a5a9a62a5c42d20c5347196f58909fd7c Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Thu, 28 Apr 2016 09:21:06 +0200
Subject: [PATCH 04/24] android: Use Fragment class from the support library to
avoid deprecation warnings
For instance, onAttach() with an Activitiy as first argument was deprecated
with API level 23. However, the overload with a Context as first argument
does obviously not get called on older API levels. Luckily, the classes
provided by the support library handle that for us.
---
.../android/ui/ImcStateFragment.java | 6 ++--
.../strongswan/android/ui/LogFragment.java | 24 ++++++-------
.../strongswan/android/ui/MainActivity.java | 8 ++---
.../ui/RemediationInstructionFragment.java | 2 +-
.../ui/RemediationInstructionsActivity.java | 10 +++---
.../ui/RemediationInstructionsFragment.java | 20 +++++------
.../android/ui/VpnProfileListFragment.java | 34 +++++++++----------
.../android/ui/VpnStateFragment.java | 2 +-
8 files changed, 53 insertions(+), 53 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ImcStateFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ImcStateFragment.java
index 7328693f1..bf5eaf64d 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ImcStateFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/ImcStateFragment.java
@@ -15,9 +15,6 @@
package org.strongswan.android.ui;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -25,6 +22,9 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.view.GestureDetector;
import android.view.LayoutInflater;
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
index 8740e0c46..2f6240726 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/LogFragment.java
@@ -15,24 +15,24 @@
package org.strongswan.android.ui;
+import android.os.Bundle;
+import android.os.FileObserver;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.strongswan.android.R;
+import org.strongswan.android.logic.CharonVpnService;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.StringReader;
-import org.strongswan.android.R;
-import org.strongswan.android.logic.CharonVpnService;
-
-import android.app.Fragment;
-import android.os.Bundle;
-import android.os.FileObserver;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
public class LogFragment extends Fragment implements Runnable
{
private String mLogFilePath;
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java
index 3d5324056..a6c668104 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java
@@ -18,9 +18,6 @@
package org.strongswan.android.ui;
import android.app.Dialog;
-import android.app.Fragment;
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -31,6 +28,9 @@ import android.net.VpnService;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -317,7 +317,7 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
*/
public void removeFragmentByTag(String tag)
{
- FragmentManager fm = getFragmentManager();
+ FragmentManager fm = getSupportFragmentManager();
Fragment login = fm.findFragmentByTag(tag);
if (login != null)
{
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
index 04c288bcf..9223479b6 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
@@ -18,8 +18,8 @@ package org.strongswan.android.ui;
import org.strongswan.android.R;
import org.strongswan.android.logic.imc.RemediationInstruction;
-import android.app.ListFragment;
import android.os.Bundle;
+import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsActivity.java
index 7e7e870be..7765a2e02 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsActivity.java
@@ -38,7 +38,7 @@ public class RemediationInstructionsActivity extends AppCompatActivity implement
{ /* only update if we're not restoring */
return;
}
- RemediationInstructionsFragment frag = (RemediationInstructionsFragment)getFragmentManager().findFragmentById(R.id.remediation_instructions_fragment);
+ RemediationInstructionsFragment frag = (RemediationInstructionsFragment)getSupportFragmentManager().findFragmentById(R.id.remediation_instructions_fragment);
if (frag != null)
{ /* two-pane layout, update fragment */
Bundle extras = getIntent().getExtras();
@@ -49,7 +49,7 @@ public class RemediationInstructionsActivity extends AppCompatActivity implement
{ /* one-pane layout, create fragment */
frag = new RemediationInstructionsFragment();
frag.setArguments(getIntent().getExtras());
- getFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
+ getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, frag).commit();
}
}
@@ -60,7 +60,7 @@ public class RemediationInstructionsActivity extends AppCompatActivity implement
{
case android.R.id.home:
/* one-pane layout, pop possible fragment from stack, finish otherwise */
- if (!getFragmentManager().popBackStackImmediate())
+ if (!getSupportFragmentManager().popBackStackImmediate())
{
finish();
}
@@ -74,7 +74,7 @@ public class RemediationInstructionsActivity extends AppCompatActivity implement
@Override
public void onRemediationInstructionSelected(RemediationInstruction instruction)
{
- RemediationInstructionFragment frag = (RemediationInstructionFragment)getFragmentManager().findFragmentById(R.id.remediation_instruction_fragment);
+ RemediationInstructionFragment frag = (RemediationInstructionFragment)getSupportFragmentManager().findFragmentById(R.id.remediation_instruction_fragment);
if (frag != null)
{ /* two-pane layout, update directly */
@@ -87,7 +87,7 @@ public class RemediationInstructionsActivity extends AppCompatActivity implement
args.putParcelable(RemediationInstructionFragment.ARG_REMEDIATION_INSTRUCTION, instruction);
frag.setArguments(args);
- getFragmentManager().beginTransaction().replace(R.id.fragment_container, frag).addToBackStack(null).commit();
+ getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, frag).addToBackStack(null).commit();
getSupportActionBar().setTitle(instruction.getTitle());
}
}
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsFragment.java
index 86467dc35..a2beffd94 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionsFragment.java
@@ -15,17 +15,17 @@
package org.strongswan.android.ui;
-import java.util.ArrayList;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.View;
+import android.widget.ListView;
import org.strongswan.android.R;
import org.strongswan.android.logic.imc.RemediationInstruction;
import org.strongswan.android.ui.adapter.RemediationInstructionAdapter;
-import android.app.Activity;
-import android.app.ListFragment;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ListView;
+import java.util.ArrayList;
public class RemediationInstructionsFragment extends ListFragment
{
@@ -65,13 +65,13 @@ public class RemediationInstructionsFragment extends ListFragment
}
@Override
- public void onAttach(Activity activity)
+ public void onAttach(Context context)
{
- super.onAttach(activity);
+ super.onAttach(context);
- if (activity instanceof OnRemediationInstructionSelectedListener)
+ if (context instanceof OnRemediationInstructionSelectedListener)
{
- mListener = (OnRemediationInstructionSelectedListener)activity;
+ mListener = (OnRemediationInstructionSelectedListener)context;
}
}
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java
index a23df054b..d8d99ff00 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileListFragment.java
@@ -17,21 +17,12 @@
package org.strongswan.android.ui;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-
-import org.strongswan.android.R;
-import org.strongswan.android.data.VpnProfile;
-import org.strongswan.android.data.VpnProfileDataSource;
-import org.strongswan.android.ui.adapter.VpnProfileAdapter;
-
import android.app.Activity;
-import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
import android.util.AttributeSet;
import android.view.ActionMode;
import android.view.LayoutInflater;
@@ -46,6 +37,15 @@ import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.Toast;
+import org.strongswan.android.R;
+import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.data.VpnProfileDataSource;
+import org.strongswan.android.ui.adapter.VpnProfileAdapter;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
public class VpnProfileListFragment extends Fragment
{
private static final int ADD_REQUEST = 1;
@@ -66,10 +66,10 @@ public class VpnProfileListFragment extends Fragment
}
@Override
- public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState)
+ public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState)
{
- super.onInflate(activity, attrs, savedInstanceState);
- TypedArray a = activity.obtainStyledAttributes(attrs, R.styleable.Fragment);
+ super.onInflate(context, attrs, savedInstanceState);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Fragment);
mReadOnly = a.getBoolean(R.styleable.Fragment_read_only, false);
a.recycle();
}
@@ -126,13 +126,13 @@ public class VpnProfileListFragment extends Fragment
}
@Override
- public void onAttach(Activity activity)
+ public void onAttach(Context context)
{
- super.onAttach(activity);
+ super.onAttach(context);
- if (activity instanceof OnVpnProfileSelectedListener)
+ if (context instanceof OnVpnProfileSelectedListener)
{
- mListener = (OnVpnProfileSelectedListener)activity;
+ mListener = (OnVpnProfileSelectedListener)context;
}
}
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java
index c49e5cc7e..10bf2c322 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnStateFragment.java
@@ -17,7 +17,6 @@
package org.strongswan.android.ui;
-import android.app.Fragment;
import android.app.ProgressDialog;
import android.app.Service;
import android.content.ComponentName;
@@ -27,6 +26,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
+import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
From 353526601aab6c5c5b78790a856d3beb0cc815c9 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Thu, 28 Apr 2016 17:00:27 +0200
Subject: [PATCH 05/24] android: Fix display of remediation instructions with
support library
Because the support library creates its own layout manually and uses
different IDs than the list_content layout we can't use the method we
used previously (and which is actually recommended in the docs).
---
.../ui/RemediationInstructionFragment.java | 17 ++++++++++++-----
.../main/res/layout/remediation_instruction.xml | 6 +++---
2 files changed, 15 insertions(+), 8 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
index 9223479b6..c17c552a2 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/RemediationInstructionFragment.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2013-2016 Tobias Brunner
* Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -15,17 +15,18 @@
package org.strongswan.android.ui;
-import org.strongswan.android.R;
-import org.strongswan.android.logic.imc.RemediationInstruction;
-
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
+import android.widget.FrameLayout;
import android.widget.TextView;
+import org.strongswan.android.R;
+import org.strongswan.android.logic.imc.RemediationInstruction;
+
public class RemediationInstructionFragment extends ListFragment
{
public static final String ARG_REMEDIATION_INSTRUCTION = "instruction";
@@ -37,7 +38,13 @@ public class RemediationInstructionFragment extends ListFragment
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
- return inflater.inflate(R.layout.remediation_instruction, container, false);
+ /* while the documentation recommends to include "@android:layout/list_content" to retain
+ * the default functionality, this does not actually work with the ListFragment provided by
+ * the support library as it builds the view manually and uses different IDs */
+ View layout = inflater.inflate(R.layout.remediation_instruction, container, false);
+ FrameLayout list = (FrameLayout)layout.findViewById(R.id.list_container);
+ list.addView(super.onCreateView(inflater, list, savedInstanceState));
+ return layout;
}
@Override
diff --git a/src/frontends/android/app/src/main/res/layout/remediation_instruction.xml b/src/frontends/android/app/src/main/res/layout/remediation_instruction.xml
index 09c0d43a3..04fffaa4e 100644
--- a/src/frontends/android/app/src/main/res/layout/remediation_instruction.xml
+++ b/src/frontends/android/app/src/main/res/layout/remediation_instruction.xml
@@ -1,6 +1,6 @@
@@ -25,6 +25,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- strongswan:read_only="true" />
+ app:read_only="true" />
\ No newline at end of file
From 79ba4b285f4aa836346a7afe2bdb206e05d72304 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Fri, 29 Apr 2016 18:15:29 +0200
Subject: [PATCH 16/24] android: Add TextInputLayout child class that displays
a helper text below the text field
Also hides the error message if the text is changed.
---
.../ui/widget/TextInputLayoutHelper.java | 180 ++++++++++++++++++
.../android/app/src/main/res/values/attrs.xml | 7 +-
2 files changed, 185 insertions(+), 2 deletions(-)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/widget/TextInputLayoutHelper.java
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/widget/TextInputLayoutHelper.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/widget/TextInputLayoutHelper.java
new file mode 100644
index 000000000..45b0ae592
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/widget/TextInputLayoutHelper.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2016 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * 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 2 of the License, or (at your
+ * option) any later version. See .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui.widget;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.support.annotation.Nullable;
+import android.support.design.widget.TextInputLayout;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.view.ViewPropertyAnimatorListenerAdapter;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.strongswan.android.R;
+
+/**
+ * Layout that extends {@link android.support.design.widget.TextInputLayout} with a helper text
+ * displayed below the text field when it receives the focus. Also, any error message shown with
+ * {@link #setError(CharSequence)} is hidden when the text field is changed (this mirrors the
+ * behavior of {@link android.widget.EditText}).
+ */
+public class TextInputLayoutHelper extends TextInputLayout
+{
+ private LinearLayout mHelperContainer;
+ private TextView mHelperText;
+
+ public TextInputLayoutHelper(Context context)
+ {
+ this(context, null);
+ }
+
+ public TextInputLayoutHelper(Context context, AttributeSet attrs)
+ {
+ this(context, attrs, 0);
+ }
+
+ public TextInputLayoutHelper(Context context, AttributeSet attrs, int defStyleAttr)
+ {
+ super(context, attrs);
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TextInputLayoutHelper);
+ String helper = a.getString(R.styleable.TextInputLayoutHelper_helper_text);
+ a.recycle();
+ if (helper != null)
+ {
+ mHelperContainer = new LinearLayout(context);
+ mHelperContainer.setOrientation(LinearLayout.HORIZONTAL);
+ mHelperContainer.setVisibility(View.INVISIBLE);
+ addView(mHelperContainer, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+
+ mHelperText = new TextView(context);
+ mHelperText.setText(helper);
+ mHelperText.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
+ a = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.textColorSecondary});
+ mHelperText.setTextColor(a.getColor(0, mHelperText.getCurrentTextColor()));
+ a.recycle();
+
+ mHelperContainer.addView(mHelperText, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+
+ @Override
+ public void addView(View child, int index, ViewGroup.LayoutParams params)
+ {
+ super.addView(child, index, params);
+ if (child instanceof EditText)
+ {
+ EditText text = (EditText)child;
+ text.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
+
+ @Override
+ public void afterTextChanged(Editable s)
+ {
+ if (getError() != null)
+ {
+ setError(null);
+ }
+ }
+ });
+ if (mHelperContainer != null)
+ {
+ text.setOnFocusChangeListener(new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus)
+ {
+ showHelper(hasFocus);
+ }
+ });
+ ViewCompat.setPaddingRelative(mHelperContainer, ViewCompat.getPaddingStart(text),
+ 0, ViewCompat.getPaddingEnd(text), text.getPaddingBottom());
+ }
+ }
+ }
+
+ @Override
+ public void setError(@Nullable CharSequence error)
+ {
+ super.setError(error);
+ if (mHelperContainer != null)
+ {
+ if (error == null)
+ { /* this frees up space used by the now invisible error message */
+ setErrorEnabled(false);
+ }
+ else
+ { /* re-add the helper as the error message should be displayed directly under the textbox */
+ removeView(mHelperContainer);
+ addView(mHelperContainer, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+ }
+ }
+ }
+
+ /**
+ * Set the helper text to be displayed below the text field.
+ *
+ * @attr ref R.styleable#TextInputLayoutHelper_helper_text
+ */
+ public void setHelperText(CharSequence text)
+ {
+ mHelperText.setText(text);
+ }
+
+ private void showHelper(boolean show)
+ {
+ if (show == (mHelperContainer.getVisibility() == View.VISIBLE))
+ {
+ return;
+ }
+ if (show)
+ {
+ ViewCompat.animate(mHelperContainer)
+ .alpha(1f)
+ .setDuration(200)
+ .setListener(new ViewPropertyAnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(View view)
+ {
+ view.setVisibility(View.VISIBLE);
+ }
+ }).start();
+ }
+ else
+ {
+ ViewCompat.animate(mHelperContainer)
+ .alpha(0f)
+ .setDuration(200)
+ .setListener(new ViewPropertyAnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(View view)
+ {
+ view.setVisibility(View.INVISIBLE);
+ }
+ }).start();
+ }
+ }
+}
diff --git a/src/frontends/android/app/src/main/res/values/attrs.xml b/src/frontends/android/app/src/main/res/values/attrs.xml
index 6c3480b0e..592555c8c 100644
--- a/src/frontends/android/app/src/main/res/values/attrs.xml
+++ b/src/frontends/android/app/src/main/res/values/attrs.xml
@@ -1,7 +1,7 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ android:padding="10dp"
+ android:animateLayoutChanges="true" >
+
+
+
+
+
+
-
-
-
-
-
+ android:layout_height="wrap_content" >
-
+
-
+
-
+ android:layout_marginTop="4dp"
+ app:helper_text="@string/profile_password_hint" >
+
+
+
+
@@ -95,12 +107,15 @@
android:id="@+id/user_certificate_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginBottom="4dp"
android:orientation="vertical" >
-
+ android:layout_marginTop="8dp"
+ app:helper_text="@string/profile_name_hint" >
-
+
+
+
+ android:layout_marginLeft="4dp"
+ android:textSize="20sp"
+ android:text="@string/profile_advanced_label" />
-
-
-
+ app:helper_text="@string/profile_mtu_hint" >
+
+
-
+
+
+ app:helper_text="@string/profile_port_hint" >
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/frontends/android/app/src/main/res/values-de/strings.xml b/src/frontends/android/app/src/main/res/values-de/strings.xml
index 076bea3d3..c2357eb5c 100644
--- a/src/frontends/android/app/src/main/res/values-de/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-de/strings.xml
@@ -49,30 +49,34 @@
Speichern
Abbrechen
- Profilname:
- (Server-Adresse verwenden)
- Server:
- Typ:
- Benutzername:
- Passwort:
- (anfordern wenn benötigt)
- Benutzer-Zertifikat:
+ Profilname (optional)
+ Standardwert ist der konfigurierte Server
+ Standardwert ist \"%1$s\"
+ Server
+ IP-Adresse oder Hostname des VPN Servers
+ VPN-Typ
+ Benutzername
+ Passwort (optional)
+ Leer lassen, um bei Bedarf danach gefragt zu werden
+ Benutzer-Zertifikat
Benutzer-Zertifikat auswählen
Wählen Sie ein bestimmtes Benutzer-Zertifikat
- CA-Zertifikat:
+ CA-Zertifikat
Automatisch wählen
CA-Zertifikat auswählen
Wählen Sie ein bestimmtes CA-Zertifikat
+ Erweiterte Einstellungen
Erweiterte Einstellungen anzeigen
- MTU:
- Server Port:
- (Standardwert verwenden)
- Split-Tunneling:
+ MTU des VPN Tunnel-Device
+ Falls der Standardwert in einem bestimmten Netzwerk nicht geeignet ist
+ Server Port
+ UDP-Port zu dem verbunden wird, falls dieser vom Standard-Port abweicht
+ Split-Tunneling
Blockiere IPv4 Verkehr der nicht für das VPN bestimmt ist
Blockiere IPv6 Verkehr der nicht für das VPN bestimmt ist
- Bitte geben Sie hier die Server-Adresse ein
- Bitte geben Sie hier Ihren Benutzernamen ein
+ Ein Wert wird benötigt, um die Verbindung aufbauen zu können
+ Bitte geben Sie Ihren Benutzernamen ein
Kein CA-Zertifikat ausgewählt
Bitte wählen Sie eines aus oder aktivieren Sie Automatisch wählen
Bitte geben Sie eine Nummer von %1$d - %2$d ein
diff --git a/src/frontends/android/app/src/main/res/values-pl/strings.xml b/src/frontends/android/app/src/main/res/values-pl/strings.xml
index 6a1e3460f..c8c159537 100644
--- a/src/frontends/android/app/src/main/res/values-pl/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-pl/strings.xml
@@ -49,36 +49,40 @@
Zapisz
Anuluj
- Nazwa profilu:
- (użyj adresu serwer)
- Serwer:
- Typ:
- Użytkownik:
- Hasło:
- (w razie potrzeby zapromptuj)
- Certyfikat użytkownika:
+ Nazwa profilu (opcjonalny)
+ Defaults to the configured server
+ Defaults to \"%1$s\"
+ Serwer
+ IP address or hostname of the VPN server
+ Typ VPN
+ Użytkownik
+ Hasło (opcjonalny)
+ Leave blank to get prompted on demand
+ Certyfikat użytkownika
Wybierz certyfikat użytkownika
>Wybierz określony certyfikat użytkownika
- Certyfikat CA:
+ Certyfikat CA
Wybierz automatycznie
Wybierz certyfikat CA
Wybierz określony certyfikat CA
+ Advanced settings
Show advanced settings
- MTU:
- Server port:
- (use default)
- Split tunneling:
+ MTU of the VPN tunnel device
+ In case the default value is unsuitable for a particular network
+ Server port
+ UDP port to connect to, if different from the default
+ Split tunneling
Block IPv4 traffic not destined for the VPN
Block IPv6 traffic not destined for the VPN
- Wprowadź adres serwer
+ A value is required to initiate the connection
Wprowadź swoją nazwę użytkownika
Nie wybrano żadnego certyfikatu CA
Wybierz lub uaktywnij jeden Wybierz automatycznie
Please enter a number in the range from %1$d - %2$d
EAP-TNC may affect your privacy
Device data is sent to the server operator
- <p>Trusted Network Connect (TNC) allows server operators to assess the health of a client device.</p><p>For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.</p><b>Any data will be sent only after verifying the server\'s identity.</b>
+ Trusted Network Connect (TNC) allows server operators to assess the health of a client device.
For that purpose the server operator may request data such as a unique identifier, a list of installed packages, system settings, or cryptographic checksums of files.
Any data will be sent only after verifying the server\'s identity.]]>
Certyfikaty CA
diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml
index 098bdf923..0550fb292 100644
--- a/src/frontends/android/app/src/main/res/values-ru/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml
@@ -46,29 +46,33 @@
Сохранить
Отмена
- Название профиля:
- (адрес cервер)
- Сервер:
- Тип:
- Логин:
- Пароль:
- (спросить если нужно)
- Сертификат пользователя:
+ Название профиля (необязательный)
+ Defaults to the configured server
+ Defaults to \"%1$s\"
+ Сервер
+ IP address or hostname of the VPN server
+ VPN Тип
+ Логин
+ Пароль (необязательный)
+ Leave blank to get prompted on demand
+ Сертификат пользователя
Выбрать сертификат пользователя
Выбрать сертификат пользователя
- Сертификат CA:
+ Сертификат CA
Выбрать автоматически
Выбрать сертификат CA
Выбрать CA сертификат
+ Advanced settings
Show advanced settings
- MTU:
- Server port:
- (use default)
- Split tunneling:
+ MTU of the VPN tunnel device
+ In case the default value is unsuitable for a particular network
+ Server port
+ UDP port to connect to, if different from the default
+ Split tunneling
Block IPv4 traffic not destined for the VPN
Block IPv6 traffic not destined for the VPN
- Пожалуйста введите адрес cервер
+ A value is required to initiate the connection
Пожалуйста введите имя пользователя
Не выбран сертификат CA
Пожалуйста выберите один Выбрать автоматически
@@ -127,4 +131,3 @@
Соединить
-
diff --git a/src/frontends/android/app/src/main/res/values-ua/strings.xml b/src/frontends/android/app/src/main/res/values-ua/strings.xml
index d6b9772a2..a52074876 100644
--- a/src/frontends/android/app/src/main/res/values-ua/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ua/strings.xml
@@ -47,30 +47,34 @@
Зберегти
Відміна
- Назва профілю:
- (використовувати адресу cервер)
- Сервер:
- Тип:
- Логін:
- Пароль:
- (запитати якщо потрібно)
- Сертифікат користувача:
+ Назва профілю (необов\'язковий)
+ Defaults to the configured server
+ Defaults to \"%1$s\"
+ Сервер
+ IP address or hostname of the VPN server
+ VPN Тип
+ Логін
+ Пароль (необов\'язковий)
+ Leave blank to get prompted on demand
+ Сертифікат користувача
Виберіть сертифікат користувача
Вибрати спеціальний сертифікат користувача
- Сертифікат CA:
+ Сертифікат CA
Вибрати автоматично
Вибрати сертифікат CA
Вибрати спеціальний сертифікат CA
+ Advanced settings
Show advanced settings
- MTU:
- Server port:
- (use default)
- Split tunneling:
+ MTU of the VPN tunnel device
+ In case the default value is unsuitable for a particular network
+ Server port
+ UDP port to connect to, if different from the default
+ Split tunneling
Block IPv4 traffic not destined for the VPN
Block IPv6 traffic not destined for the VPN
- Введіть адресу cервер тут
- Введіть ім\'я користувача тут
+ A value is required to initiate the connection
+ Введіть ім\'я користувача
Не вибрано сертифікат CA
Будь ласка виберіть один Вибрати автоматично
Please enter a number in the range from %1$d - %2$d
@@ -128,4 +132,3 @@
Підключити
-
diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml
index be90dc98d..76886cd6d 100644
--- a/src/frontends/android/app/src/main/res/values/strings.xml
+++ b/src/frontends/android/app/src/main/res/values/strings.xml
@@ -49,30 +49,34 @@
Save
Cancel
- Profile Name:
- (use server address)
- Server:
- Type:
- Username:
- Password:
- (prompt when needed)
- User certificate:
+ Profile name (optional)
+ Defaults to the configured server
+ Defaults to \"%1$s\"
+ Server
+ IP address or hostname of the VPN server
+ VPN Type
+ Username
+ Password (optional)
+ Leave blank to get prompted on demand
+ User certificate
Select user certificate
Select a specific user certificate
- CA certificate:
+ CA certificate
Select automatically
Select CA certificate
Select a specific CA certificate
+ Advanced settings
Show advanced settings
- MTU:
- Server port:
- (use default)
- Split tunneling:
+ MTU of the VPN tunnel device
+ In case the default value is unsuitable for a particular network
+ Server port
+ UDP port to connect to, if different from the default
+ Split tunneling
Block IPv4 traffic not destined for the VPN
Block IPv6 traffic not destined for the VPN
- Please enter the server address here
- Please enter your username here
+ A value is required to initiate the connection
+ Please enter your username
No CA certificate selected
Please select one or activate Select automatically
Please enter a number in the range from %1$d - %2$d
From be05310e7ab14c5fb3f1cbe8a850b9e9ea88198d Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Mon, 2 May 2016 18:04:03 +0200
Subject: [PATCH 18/24] android: Use TextInputLayout in login dialog
---
.../app/src/main/res/layout/login_dialog.xml | 56 ++++++++++---------
1 file changed, 30 insertions(+), 26 deletions(-)
diff --git a/src/frontends/android/app/src/main/res/layout/login_dialog.xml b/src/frontends/android/app/src/main/res/layout/login_dialog.xml
index 0262af0a3..adfaac5e2 100644
--- a/src/frontends/android/app/src/main/res/layout/login_dialog.xml
+++ b/src/frontends/android/app/src/main/res/layout/login_dialog.xml
@@ -1,9 +1,9 @@
-
+ android:padding="10dp" >
-
-
-
+ android:layout_height="wrap_content" >
-
+
-
+
+
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp" >
+
+
+
+
From c5fee223056c375c33cc32c871a86cc1966dd4a1 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Sat, 30 Apr 2016 12:25:49 +0200
Subject: [PATCH 19/24] android: Make remote identity configurable in the GUI
---
.../android/ui/VpnProfileDetailActivity.java | 12 +++++++++++-
.../main/res/layout/profile_detail_view.xml | 18 +++++++++++++++++-
.../app/src/main/res/values-de/strings.xml | 3 +++
.../app/src/main/res/values-pl/strings.xml | 3 +++
.../app/src/main/res/values-ru/strings.xml | 3 +++
.../app/src/main/res/values-ua/strings.xml | 3 +++
.../app/src/main/res/values/strings.xml | 3 +++
7 files changed, 43 insertions(+), 2 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
index fe523e158..6710342f0 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -90,6 +90,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private RelativeLayout mTncNotice;
private CheckBox mShowAdvanced;
private ViewGroup mAdvancedSettings;
+ private EditText mRemoteId;
+ private TextInputLayoutHelper mRemoteIdWrap;
private EditText mMTU;
private TextInputLayoutHelper mMTUWrap;
private EditText mPort;
@@ -131,6 +133,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
+ mRemoteId = (EditText)findViewById(R.id.remote_id);
+ mRemoteIdWrap = (TextInputLayoutHelper) findViewById(R.id.remote_id_wrap);
mMTU = (EditText)findViewById(R.id.mtu);
mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap);
mPort = (EditText)findViewById(R.id.port);
@@ -151,10 +155,12 @@ public class VpnProfileDetailActivity extends AppCompatActivity
if (TextUtils.isEmpty(mGateway.getText()))
{
mNameWrap.setHelperText(getString(R.string.profile_name_hint));
+ mRemoteIdWrap.setHelperText(getString(R.string.profile_remote_id_hint));
}
else
{
mNameWrap.setHelperText(String.format(getString(R.string.profile_name_hint_gateway), mGateway.getText()));
+ mRemoteIdWrap.setHelperText(String.format(getString(R.string.profile_remote_id_hint_gateway), mGateway.getText()));
}
}
});
@@ -384,7 +390,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
if (!show && mProfile != null)
{
Integer st = mProfile.getSplitTunneling();
- show = mProfile.getMTU() != null || mProfile.getPort() != null || (st != null && st != 0);
+ show = mProfile.getRemoteId() != null || mProfile.getMTU() != null ||
+ mProfile.getPort() != null || (st != null && st != 0);
}
mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
@@ -483,6 +490,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
}
String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
mProfile.setCertificateAlias(certAlias);
+ String remote_id = mRemoteId.getText().toString().trim();
+ mProfile.setRemoteId(remote_id.isEmpty() ? null : remote_id);
mProfile.setMTU(getInteger(mMTU));
mProfile.setPort(getInteger(mPort));
int st = 0;
@@ -511,6 +520,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mVpnType = mProfile.getVpnType();
mUsername.setText(mProfile.getUsername());
mPassword.setText(mProfile.getPassword());
+ mRemoteId.setText(mProfile.getRemoteId());
mMTU.setText(mProfile.getMTU() != null ? mProfile.getMTU().toString() : null);
mPort.setText(mProfile.getPort() != null ? mProfile.getPort().toString() : null);
mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
diff --git a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
index 4a1cc6175..737c2f9a3 100644
--- a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
@@ -180,10 +180,26 @@
android:text="@string/profile_advanced_label" />
+
+
+
+
+
+
Wählen Sie ein bestimmtes CA-Zertifikat
Erweiterte Einstellungen
Erweiterte Einstellungen anzeigen
+ Server-Identität
+ Standardwert ist der konfigurierte Server. Eigene Werte werden explizit and den Server gesendet und während der Authentifizierung erzwungen
+ Standardwert ist \"%1$s\". Eigene Werte werden explizit and den Server gesendet und während der Authentifizierung erzwungen
MTU des VPN Tunnel-Device
Falls der Standardwert in einem bestimmten Netzwerk nicht geeignet ist
Server Port
diff --git a/src/frontends/android/app/src/main/res/values-pl/strings.xml b/src/frontends/android/app/src/main/res/values-pl/strings.xml
index c8c159537..cfd877f50 100644
--- a/src/frontends/android/app/src/main/res/values-pl/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-pl/strings.xml
@@ -67,6 +67,9 @@
Wybierz określony certyfikat CA
Advanced settings
Show advanced settings
+ Server identity
+ Defaults to the configured server. Custom values are explicitly sent to the server and enforced during authentication
+ Defaults to \"%1$s\". Custom values are explicitly sent to the server and enforced during authentication
MTU of the VPN tunnel device
In case the default value is unsuitable for a particular network
Server port
diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml
index 0550fb292..cfd8129bd 100644
--- a/src/frontends/android/app/src/main/res/values-ru/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml
@@ -64,6 +64,9 @@
Выбрать CA сертификат
Advanced settings
Show advanced settings
+ Server identity
+ Defaults to the configured server. Custom values are explicitly sent to the server and enforced during authentication
+ Defaults to \"%1$s\". Custom values are explicitly sent to the server and enforced during authentication
MTU of the VPN tunnel device
In case the default value is unsuitable for a particular network
Server port
diff --git a/src/frontends/android/app/src/main/res/values-ua/strings.xml b/src/frontends/android/app/src/main/res/values-ua/strings.xml
index a52074876..85969898f 100644
--- a/src/frontends/android/app/src/main/res/values-ua/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ua/strings.xml
@@ -65,6 +65,9 @@
Вибрати спеціальний сертифікат CA
Advanced settings
Show advanced settings
+ Server identity
+ Defaults to the configured server. Custom values are explicitly sent to the server and enforced during authentication
+ Defaults to \"%1$s\". Custom values are explicitly sent to the server and enforced during authentication
MTU of the VPN tunnel device
In case the default value is unsuitable for a particular network
Server port
diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml
index 76886cd6d..d704da062 100644
--- a/src/frontends/android/app/src/main/res/values/strings.xml
+++ b/src/frontends/android/app/src/main/res/values/strings.xml
@@ -67,6 +67,9 @@
Select a specific CA certificate
Advanced settings
Show advanced settings
+ Server identity
+ Defaults to the configured server. Custom values are explicitly sent to the server and enforced during authentication
+ Defaults to \"%1$s\". Custom values are explicitly sent to the server and enforced during authentication
MTU of the VPN tunnel device
In case the default value is unsuitable for a particular network
Server port
From e7a12cc862bf41d363bd70b400f9cd2cf7e51e39 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Sat, 30 Apr 2016 13:11:49 +0200
Subject: [PATCH 20/24] android: Add auto-completion to remote ID and profile
name
This makes it easy to explicitly use the server's IP/hostname as remote
identity or use it in the profile name.
---
.../android/ui/VpnProfileDetailActivity.java | 83 ++++++++++++++++++-
.../main/res/layout/profile_detail_view.xml | 6 +-
2 files changed, 83 insertions(+), 6 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 6710342f0..2af208d6f 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -31,6 +31,8 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDialogFragment;
import android.text.Editable;
import android.text.Html;
+import android.text.SpannableString;
+import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
@@ -42,10 +44,12 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
+import android.widget.MultiAutoCompleteTextView;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -74,7 +78,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private TrustedCertificateEntry mUserCertEntry;
private VpnType mVpnType = VpnType.IKEV2_EAP;
private VpnProfile mProfile;
- private EditText mName;
+ private MultiAutoCompleteTextView mName;
private TextInputLayoutHelper mNameWrap;
private EditText mGateway;
private TextInputLayoutHelper mGatewayWrap;
@@ -90,7 +94,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private RelativeLayout mTncNotice;
private CheckBox mShowAdvanced;
private ViewGroup mAdvancedSettings;
- private EditText mRemoteId;
+ private MultiAutoCompleteTextView mRemoteId;
private TextInputLayoutHelper mRemoteIdWrap;
private EditText mMTU;
private TextInputLayoutHelper mMTUWrap;
@@ -112,7 +116,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
setContentView(R.layout.profile_detail_view);
- mName = (EditText)findViewById(R.id.name);
+ mName = (MultiAutoCompleteTextView)findViewById(R.id.name);
mNameWrap = (TextInputLayoutHelper)findViewById(R.id.name_wrap);
mGateway = (EditText)findViewById(R.id.gateway);
mGatewayWrap = (TextInputLayoutHelper) findViewById(R.id.gateway_wrap);
@@ -133,7 +137,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mShowAdvanced = (CheckBox)findViewById(R.id.show_advanced);
mAdvancedSettings = (ViewGroup)findViewById(R.id.advanced_settings);
- mRemoteId = (EditText)findViewById(R.id.remote_id);
+ mRemoteId = (MultiAutoCompleteTextView)findViewById(R.id.remote_id);
mRemoteIdWrap = (TextInputLayoutHelper) findViewById(R.id.remote_id_wrap);
mMTU = (EditText)findViewById(R.id.mtu);
mMTUWrap = (TextInputLayoutHelper) findViewById(R.id.mtu_wrap);
@@ -142,6 +146,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
+ final SpaceTokenizer spaceTokenizer = new SpaceTokenizer();
+ mName.setTokenizer(spaceTokenizer);
+ mRemoteId.setTokenizer(spaceTokenizer);
+ final ArrayAdapter completeAdapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line);
+ mName.setAdapter(completeAdapter);
+ mRemoteId.setAdapter(completeAdapter);
+
mGateway.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@@ -152,6 +163,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
@Override
public void afterTextChanged(Editable s)
{
+ completeAdapter.clear();
+ completeAdapter.add(mGateway.getText().toString());
if (TextUtils.isEmpty(mGateway.getText()))
{
mNameWrap.setHelperText(getString(R.string.profile_name_hint));
@@ -695,4 +708,66 @@ public class VpnProfileDetailActivity extends AppCompatActivity
}).create();
}
}
+
+ /**
+ * Tokenizer implementation that separates by white-space
+ */
+ public static class SpaceTokenizer implements MultiAutoCompleteTextView.Tokenizer
+ {
+ @Override
+ public int findTokenStart(CharSequence text, int cursor)
+ {
+ int i = cursor;
+
+ while (i > 0 && !Character.isWhitespace(text.charAt(i - 1)))
+ {
+ i--;
+ }
+ return i;
+ }
+
+ @Override
+ public int findTokenEnd(CharSequence text, int cursor)
+ {
+ int i = cursor;
+ int len = text.length();
+
+ while (i < len)
+ {
+ if (Character.isWhitespace(text.charAt(i)))
+ {
+ return i;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ return len;
+ }
+
+ @Override
+ public CharSequence terminateToken(CharSequence text)
+ {
+ int i = text.length();
+
+ if (i > 0 && Character.isWhitespace(text.charAt(i - 1)))
+ {
+ return text;
+ }
+ else
+ {
+ if (text instanceof Spanned)
+ {
+ SpannableString sp = new SpannableString(text + " ");
+ TextUtils.copySpansFrom((Spanned) text, 0, text.length(), Object.class, sp, 0);
+ return sp;
+ }
+ else
+ {
+ return text + " ";
+ }
+ }
+ }
+ }
}
diff --git a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
index 737c2f9a3..847228950 100644
--- a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
@@ -149,12 +149,13 @@
android:layout_marginTop="8dp"
app:helper_text="@string/profile_name_hint" >
-
@@ -186,12 +187,13 @@
android:layout_marginTop="10dp"
app:helper_text="@string/profile_remote_id_hint" >
-
From eb507a5a0dd13db1042ff05e5ae72c6fe0525170 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Sat, 30 Apr 2016 16:11:45 +0200
Subject: [PATCH 21/24] android: Add helper function to TrustedCertificateEntry
to get subjectAltNames
Duplicates (e.g. with different types) are filtered. If necessary we
could later perhaps add a prefix.
---
.../security/TrustedCertificateEntry.java | 47 +++++++++++++++++--
1 file changed, 43 insertions(+), 4 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java b/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java
index 143741faf..5e9873d1b 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/security/TrustedCertificateEntry.java
@@ -1,6 +1,6 @@
/*
- * Copyright (C) 2012 Tobias Brunner
- * Hochschule fuer Technik Rapperswil
+ * Copyright (C) 2012-2016 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
*
* 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
@@ -15,10 +15,15 @@
package org.strongswan.android.security;
-import java.security.cert.X509Certificate;
-
import android.net.http.SslCertificate;
+import java.security.cert.CertificateParsingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
public class TrustedCertificateEntry implements Comparable
{
private final X509Certificate mCert;
@@ -86,6 +91,40 @@ public class TrustedCertificateEntry implements Comparable getSubjectAltNames()
+ {
+ List list = new ArrayList<>();
+ try
+ {
+ Collection> sans = mCert.getSubjectAlternativeNames();
+ if (sans != null)
+ {
+ for (List> san : sans)
+ {
+ switch ((Integer)san.get(0))
+ {
+ case 1: /* rfc822Name */
+ case 2: /* dnSName */
+ case 7: /* iPAddress */
+ list.add((String)san.get(1));
+ break;
+ }
+ }
+ }
+ Collections.sort(list);
+ }
+ catch(CertificateParsingException ex)
+ {
+ ex.printStackTrace();
+ }
+ return list;
+ }
+
/**
* The alias associated with this certificate.
*
From cdcf754f6406b2179544c0a899588c01256e6758 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Sat, 30 Apr 2016 16:59:00 +0200
Subject: [PATCH 22/24] android: Add adapter for user ID selection
---
.../adapter/CertificateIdentitiesAdapter.java | 65 +++++++++++++++++++
.../app/src/main/res/values-de/strings.xml | 3 +
.../app/src/main/res/values-pl/strings.xml | 3 +
.../app/src/main/res/values-ru/strings.xml | 3 +
.../app/src/main/res/values-ua/strings.xml | 3 +
.../app/src/main/res/values/strings.xml | 3 +
6 files changed, 80 insertions(+)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/CertificateIdentitiesAdapter.java
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/CertificateIdentitiesAdapter.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/CertificateIdentitiesAdapter.java
new file mode 100644
index 000000000..c8e3df38b
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/CertificateIdentitiesAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 Tobias Brunner
+ * HSR Hochschule fuer Technik Rapperswil
+ *
+ * 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 2 of the License, or (at your
+ * option) any later version. See .
+ *
+ * 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.
+ */
+
+package org.strongswan.android.ui.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import org.strongswan.android.R;
+import org.strongswan.android.security.TrustedCertificateEntry;
+
+import java.util.List;
+
+public class CertificateIdentitiesAdapter extends ArrayAdapter
+{
+ TrustedCertificateEntry mCertificate;
+
+ public CertificateIdentitiesAdapter(Context context)
+ {
+ super(context, android.R.layout.simple_dropdown_item_1line);
+ extractIdentities();
+ }
+
+ /**
+ * Set a new certificate for this adapter.
+ *
+ * @param certificate the certificate to extract identities from (null to clear)
+ */
+ public void setCertificate(TrustedCertificateEntry certificate)
+ {
+ mCertificate = certificate;
+ clear();
+ extractIdentities();
+ }
+
+ private void extractIdentities()
+ {
+ if (mCertificate == null)
+ {
+ add(getContext().getString(R.string.profile_user_select_id_init));
+ }
+ else
+ {
+ add(String.format(getContext().getString(R.string.profile_user_select_id_default),
+ mCertificate.getCertificate().getSubjectDN().getName()));
+ addAll(mCertificate.getSubjectAltNames());
+ }
+ }
+}
diff --git a/src/frontends/android/app/src/main/res/values-de/strings.xml b/src/frontends/android/app/src/main/res/values-de/strings.xml
index b5def060a..15ccf24fd 100644
--- a/src/frontends/android/app/src/main/res/values-de/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-de/strings.xml
@@ -61,6 +61,9 @@
Benutzer-Zertifikat
Benutzer-Zertifikat auswählen
Wählen Sie ein bestimmtes Benutzer-Zertifikat
+ Benutzer-Identität
+ Wählen Sie zuerst ein Benutzer-Zertifikat
+ Standardwert (%1$s)
CA-Zertifikat
Automatisch wählen
CA-Zertifikat auswählen
diff --git a/src/frontends/android/app/src/main/res/values-pl/strings.xml b/src/frontends/android/app/src/main/res/values-pl/strings.xml
index cfd877f50..c7aadb994 100644
--- a/src/frontends/android/app/src/main/res/values-pl/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-pl/strings.xml
@@ -61,6 +61,9 @@
Certyfikat użytkownika
Wybierz certyfikat użytkownika
>Wybierz określony certyfikat użytkownika
+ User identity
+ Select a certificate first
+ Default (%1$s)
Certyfikat CA
Wybierz automatycznie
Wybierz certyfikat CA
diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml
index cfd8129bd..c0a6484f8 100644
--- a/src/frontends/android/app/src/main/res/values-ru/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml
@@ -58,6 +58,9 @@
Сертификат пользователя
Выбрать сертификат пользователя
Выбрать сертификат пользователя
+ User identity
+ Select a certificate first
+ Default (%1$s)
Сертификат CA
Выбрать автоматически
Выбрать сертификат CA
diff --git a/src/frontends/android/app/src/main/res/values-ua/strings.xml b/src/frontends/android/app/src/main/res/values-ua/strings.xml
index 85969898f..a852dfdba 100644
--- a/src/frontends/android/app/src/main/res/values-ua/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-ua/strings.xml
@@ -59,6 +59,9 @@
Сертифікат користувача
Виберіть сертифікат користувача
Вибрати спеціальний сертифікат користувача
+ User identity
+ Select a certificate first
+ Default (%1$s)
Сертифікат CA
Вибрати автоматично
Вибрати сертифікат CA
diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml
index d704da062..d70712181 100644
--- a/src/frontends/android/app/src/main/res/values/strings.xml
+++ b/src/frontends/android/app/src/main/res/values/strings.xml
@@ -61,6 +61,9 @@
User certificate
Select user certificate
Select a specific user certificate
+ User identity
+ Select a certificate first
+ Default (%1$s)
CA certificate
Select automatically
Select CA certificate
From 67fa05aa59edfdf94f54a009a4887f7bb75f0afc Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Sat, 30 Apr 2016 17:04:45 +0200
Subject: [PATCH 23/24] android: Allow selection of user identity in GUI
---
.../android/ui/VpnProfileDetailActivity.java | 40 ++++++++++++++++++-
.../main/res/layout/profile_detail_view.xml | 14 +++++++
2 files changed, 52 insertions(+), 2 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
index 2af208d6f..29124ced7 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
@@ -61,6 +61,7 @@ import org.strongswan.android.data.VpnType;
import org.strongswan.android.data.VpnType.VpnTypeFeature;
import org.strongswan.android.logic.TrustedCertificateManager;
import org.strongswan.android.security.TrustedCertificateEntry;
+import org.strongswan.android.ui.adapter.CertificateIdentitiesAdapter;
import org.strongswan.android.ui.widget.TextInputLayoutHelper;
import java.security.cert.X509Certificate;
@@ -75,6 +76,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private Long mId;
private TrustedCertificateEntry mCertEntry;
private String mUserCertLoading;
+ private CertificateIdentitiesAdapter mSelectUserIdAdapter;
+ private String mSelectedUserId;
private TrustedCertificateEntry mUserCertEntry;
private VpnType mVpnType = VpnType.IKEV2_EAP;
private VpnProfile mProfile;
@@ -89,6 +92,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private EditText mPassword;
private ViewGroup mUserCertificate;
private RelativeLayout mSelectUserCert;
+ private Spinner mSelectUserId;
private CheckBox mCheckAuto;
private RelativeLayout mSelectCert;
private RelativeLayout mTncNotice;
@@ -130,6 +134,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mUserCertificate = (ViewGroup)findViewById(R.id.user_certificate_group);
mSelectUserCert = (RelativeLayout)findViewById(R.id.select_user_certificate);
+ mSelectUserId = (Spinner)findViewById(R.id.select_user_id);
mCheckAuto = (CheckBox)findViewById(R.id.ca_auto);
mSelectCert = (RelativeLayout)findViewById(R.id.select_certificate);
@@ -205,6 +210,24 @@ public class VpnProfileDetailActivity extends AppCompatActivity
});
mSelectUserCert.setOnClickListener(new SelectUserCertOnClickListener());
+ mSelectUserIdAdapter = new CertificateIdentitiesAdapter(this);
+ mSelectUserId.setAdapter(mSelectUserIdAdapter);
+ mSelectUserId.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id)
+ {
+ if (mUserCertEntry != null)
+ { /* we don't store the subject DN as it is in the reverse order and the default anyway */
+ mSelectedUserId = position == 0 ? null : mSelectUserIdAdapter.getItem(position);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent)
+ {
+ mSelectedUserId = null;
+ }
+ });
mCheckAuto.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
@@ -265,6 +288,10 @@ public class VpnProfileDetailActivity extends AppCompatActivity
{
outState.putString(VpnProfileDataSource.KEY_USER_CERTIFICATE, mUserCertEntry.getAlias());
}
+ if (mSelectedUserId != null)
+ {
+ outState.putString(VpnProfileDataSource.KEY_LOCAL_ID, mSelectedUserId);
+ }
if (mCertEntry != null)
{
outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias());
@@ -326,6 +353,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
if (mVpnType.has(VpnTypeFeature.CERTIFICATE))
{
+ mSelectUserId.setEnabled(false);
if (mUserCertLoading != null)
{
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertLoading);
@@ -336,11 +364,15 @@ public class VpnProfileDetailActivity extends AppCompatActivity
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setError(null);
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(mUserCertEntry.getAlias());
((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(mUserCertEntry.getCertificate().getSubjectDN().toString());
+ mSelectUserIdAdapter.setCertificate(mUserCertEntry);
+ mSelectUserId.setSelection(mSelectUserIdAdapter.getPosition(mSelectedUserId));
+ mSelectUserId.setEnabled(true);
}
else
{
((TextView)mSelectUserCert.findViewById(android.R.id.text1)).setText(R.string.profile_user_select_certificate_label);
((TextView)mSelectUserCert.findViewById(android.R.id.text2)).setText(R.string.profile_user_select_certificate);
+ mSelectUserIdAdapter.setCertificate(null);
}
}
}
@@ -500,6 +532,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
if (mVpnType.has(VpnTypeFeature.CERTIFICATE))
{
mProfile.setUserCertificateAlias(mUserCertEntry.getAlias());
+ mProfile.setLocalId(mSelectedUserId);
}
String certAlias = mCheckAuto.isChecked() ? null : mCertEntry.getAlias();
mProfile.setCertificateAlias(certAlias);
@@ -520,7 +553,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
*/
private void loadProfileData(Bundle savedInstanceState)
{
- String useralias = null, alias = null;
+ String useralias = null, local_id = null, alias = null;
getSupportActionBar().setTitle(R.string.add_profile);
if (mId != null && mId != 0)
@@ -539,6 +572,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0 : false);
mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null ? (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0 : false);
useralias = mProfile.getUserCertificateAlias();
+ local_id = mProfile.getLocalId();
alias = mProfile.getCertificateAlias();
getSupportActionBar().setTitle(mProfile.getName());
}
@@ -553,11 +587,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mSelectVpnType.setSelection(mVpnType.ordinal());
/* check if the user selected a user certificate previously */
- useralias = savedInstanceState == null ? useralias: savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
+ useralias = savedInstanceState == null ? useralias : savedInstanceState.getString(VpnProfileDataSource.KEY_USER_CERTIFICATE);
+ local_id = savedInstanceState == null ? local_id : savedInstanceState.getString(VpnProfileDataSource.KEY_LOCAL_ID);
if (useralias != null)
{
UserCertificateLoader loader = new UserCertificateLoader(this, useralias);
mUserCertLoading = useralias;
+ mSelectedUserId = local_id;
loader.execute();
}
diff --git a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
index 847228950..08881b38c 100644
--- a/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
+++ b/src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
@@ -122,6 +122,20 @@
android:id="@+id/select_user_certificate"
layout="@layout/two_line_button" />
+
+
+
+
Date: Sat, 30 Apr 2016 17:14:34 +0200
Subject: [PATCH 24/24] android: Show selected user identity in profile list
This also readds the colons that were removed from the labels.
---
.../android/ui/adapter/VpnProfileAdapter.java | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/VpnProfileAdapter.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/VpnProfileAdapter.java
index f3bb271bc..58296a018 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/VpnProfileAdapter.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/VpnProfileAdapter.java
@@ -63,12 +63,18 @@ public class VpnProfileAdapter extends ArrayAdapter
TextView tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_name);
tv.setText(profile.getName());
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_gateway);
- tv.setText(getContext().getString(R.string.profile_gateway_label) + " " + profile.getGateway());
+ tv.setText(getContext().getString(R.string.profile_gateway_label) + ": " + profile.getGateway());
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_username);
if (profile.getVpnType().has(VpnTypeFeature.USER_PASS))
{ /* if the view is reused we make sure it is visible */
tv.setVisibility(View.VISIBLE);
- tv.setText(getContext().getString(R.string.profile_username_label) + " " + profile.getUsername());
+ tv.setText(getContext().getString(R.string.profile_username_label) + ": " + profile.getUsername());
+ }
+ else if (profile.getVpnType().has(VpnTypeFeature.CERTIFICATE) &&
+ profile.getLocalId() != null)
+ {
+ tv.setVisibility(View.VISIBLE);
+ tv.setText(getContext().getString(R.string.profile_user_select_id_label) + ": " + profile.getLocalId());
}
else
{
@@ -77,7 +83,7 @@ public class VpnProfileAdapter extends ArrayAdapter
tv = (TextView)vpnProfileView.findViewById(R.id.profile_item_certificate);
if (profile.getVpnType().has(VpnTypeFeature.CERTIFICATE))
{
- tv.setText(getContext().getString(R.string.profile_user_certificate_label) + " " + profile.getUserCertificateAlias());
+ tv.setText(getContext().getString(R.string.profile_user_certificate_label) + ": " + profile.getUserCertificateAlias());
tv.setVisibility(View.VISIBLE);
}
else