From 451498b47087f43f71348cc0615b8457d8e312fd Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 18 Apr 2017 16:05:00 +0200
Subject: [PATCH 01/43] android: Update Gradle plugin and wrapper
---
src/frontends/android/app/build.gradle | 2 +-
src/frontends/android/build.gradle | 2 +-
.../android/gradle/wrapper/gradle-wrapper.properties | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/frontends/android/app/build.gradle b/src/frontends/android/app/build.gradle
index 3c1c68a5b..d0ab256bd 100644
--- a/src/frontends/android/app/build.gradle
+++ b/src/frontends/android/app/build.gradle
@@ -2,7 +2,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
- buildToolsVersion "23.0.3"
+ buildToolsVersion '25.0.0'
defaultConfig {
applicationId "org.strongswan.android"
diff --git a/src/frontends/android/build.gradle b/src/frontends/android/build.gradle
index 3d5ba798f..e52ed0aca 100644
--- a/src/frontends/android/build.gradle
+++ b/src/frontends/android/build.gradle
@@ -3,7 +3,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:2.2.3'
+ classpath 'com.android.tools.build:gradle:2.3.3'
}
}
diff --git a/src/frontends/android/gradle/wrapper/gradle-wrapper.properties b/src/frontends/android/gradle/wrapper/gradle-wrapper.properties
index a6bd91c6c..8c08a361c 100644
--- a/src/frontends/android/gradle/wrapper/gradle-wrapper.properties
+++ b/src/frontends/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Sep 20 17:56:35 CEST 2016
+#Tue Apr 18 15:57:06 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
From 61098dd6a301f9c6f2aee6bc59a2785de1712004 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 18 Apr 2017 16:07:55 +0200
Subject: [PATCH 02/43] android: Move Simplified Chinese translation to
values-zg-rCN folder
---
.../app/src/main/res/{values-zh => values-zh-rCN}/arrays.xml | 0
.../app/src/main/res/{values-zh => values-zh-rCN}/strings.xml | 0
2 files changed, 0 insertions(+), 0 deletions(-)
rename src/frontends/android/app/src/main/res/{values-zh => values-zh-rCN}/arrays.xml (100%)
rename src/frontends/android/app/src/main/res/{values-zh => values-zh-rCN}/strings.xml (100%)
diff --git a/src/frontends/android/app/src/main/res/values-zh/arrays.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
similarity index 100%
rename from src/frontends/android/app/src/main/res/values-zh/arrays.xml
rename to src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
diff --git a/src/frontends/android/app/src/main/res/values-zh/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
similarity index 100%
rename from src/frontends/android/app/src/main/res/values-zh/strings.xml
rename to src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
From edad60ea77a873b689ac55d7b8f7ce245a02d600 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 18 Apr 2017 16:14:45 +0200
Subject: [PATCH 03/43] android: Add translation for Traditional Chinese
Courtesy of Chris Chiang.
---
.../app/src/main/res/values-zh-rTW/arrays.xml | 24 +++
.../src/main/res/values-zh-rTW/strings.xml | 152 ++++++++++++++++++
2 files changed, 176 insertions(+)
create mode 100644 src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
create mode 100644 src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
new file mode 100644
index 000000000..c7a1963bb
--- /dev/null
+++ b/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ - IKEv2 EAP (用戶名稱/密碼)
+ - IKEv2 憑證
+ - IKEv2 憑證 + EAP (用戶名稱/密碼)
+ - IKEv2 EAP-TLS (憑證)
+ - IKEv2 EAP-TNC (用戶名稱/密碼)
+
+
\ No newline at end of file
diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 000000000..79eb30114
--- /dev/null
+++ b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,152 @@
+
+
+
+
+
+ strongSwan VPN 用戶端
+ strongSwan
+ 觀看日誌
+ 搜尋
+ 無法支援VPN
+ 您的設備無法使用VPN。\n請聯絡供應商。
+ 鎖定模式無法連線VPN
+ 載入中…
+ 沒有找到設定檔
+ strongSwan快速選單
+
+
+ 日誌
+ 發送日誌檔
+ 沒有日誌檔
+ strongSwan %1$s 日誌檔
+
+
+ 沒有設定檔
+ 新增VPN設定檔
+ 編輯
+ 刪除
+ 選擇設定檔
+ 選擇的設定檔已經刪除
+ 沒有選擇設定檔
+ 已選擇1個設定檔
+ 已選擇%1$d個設定檔
+
+
+ 儲存
+ 匯入
+ 取消
+ 設定檔名稱(選填)
+ 設定檔名稱
+ 預設為已設定的伺服器位置
+ 預設為 \"%1$s\"
+ 伺服器位置
+ IP位置或伺服器網域
+ VPN類型
+ 用戶ID
+ 密碼(選填)
+ 可留空白在需要時才請您設定
+ 用戶憑證
+ 選擇用戶憑證
+ 選擇指定的用戶憑證
+ 用戶帳號
+ 請先選擇一個憑證
+ 預設(%1$s)
+ CA憑證
+ 自動選擇
+ 選擇CA憑證
+ 選擇一個指定的CA憑證
+ 進階設定
+ 顯示進階設定
+ 伺服器ID
+ 預設為已設定的伺服器位置。自訂值會在授權期間送到伺服器
+ 預設為 \"%1$s\"。自訂值會在授權期間送到伺服器
+ VPN通道裝置的MTU值
+ 如果在某個網路下預設值不適合
+ 伺服器Port
+ 如果和預設值不同,則需要連接的UDP Port
+ 拆分隧道
+ 屏蔽不通过VPN的IPV4流量
+ 屏蔽不通过VPN的IPV6流量
+ 匯入VPN設定檔
+ 匯入VPN設定檔失敗
+ 匯入VPN設定檔失敗: %1$s
+ 沒有找到檔案
+ 不明主機
+ TLS連線失敗
+ 這個VPN設定檔已經存在,當前設定檔會被覆蓋。
+ 從VPN設定檔匯入憑證
+ \"%1$s\" 對應的憑證
+
+ 請填寫必要訊息才能初始化連線
+ 請輸入您的用戶名稱
+ 沒有選擇CA憑證
+ 請選擇一項或啟動 自動選擇
+ 請輸入一個數字範圍從%1$d到%2$d
+ EAP-TNC可能會影響您的隱私安全
+ 裝置資料已經發送給伺服器管理者
+ Trusted Network Connect (TNC) 可以讓伺服器管理者評估用戶裝置的狀況。
在這個目的下,伺服器管理者可能會要求以下資料,例如ID、已安裝的App項目、系統設定、或加密檔案驗證值。
任何資料都只有在驗證伺服器的身分ID之後才會被送出。]]>
+
+
+ CA憑證
+ 沒有憑證
+ 重新載入CA憑證
+ 系統
+ 用戶
+ 已經匯入
+ 是否刪除憑證?
+ 憑證將被永久移除!
+ 導入憑證
+ 憑證已經成功匯入
+ 憑證匯入失敗
+
+
+ 狀態:
+ 設定檔:
+ 結束連線
+ 連線中…
+ 已連線
+ 結束連線中…
+ 無運作中的VPN
+ 錯誤
+
+
+ 評估詳情:
+ 受限的
+ 失敗的
+ 觀看修復說明
+
+
+ 修復說明
+
+
+ 輸入密碼進行連線
+ 用戶名稱
+ 密碼
+ 連線
+ 無法建立VPN:
+ 伺服器位置查詢失敗。
+ 伺服器位置無法連線。
+ 驗證伺服器授權失敗。
+ 用戶授權失敗。
+ 穩定性評估失敗。
+ 連線中遇到不明錯誤。
+ VPN已連線
+ 這個VPN設定檔目前已經連線。
+ 重新連線
+ 是否連線%1$s?
+ 這將會覆蓋您當前運作的VPN連線!
+ 連線
+
+
From 6766c85231a083a239ac8c7bed6d9ef5644dded2 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 18 Apr 2017 17:07:57 +0200
Subject: [PATCH 04/43] android: Add menu item to import VPN profile via
Storage Access Framework
This is useful in case the proper MIME type was not set for a downloaded
profile.
---
.../strongswan/android/ui/MainActivity.java | 17 ++++++-
.../android/ui/VpnProfileImportActivity.java | 49 ++++++++++++++-----
.../android/app/src/main/res/menu/main.xml | 7 ++-
3 files changed, 59 insertions(+), 14 deletions(-)
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 890434255..5ba1061c2 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Tobias Brunner
+ * Copyright (C) 2012-2017 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* Hochschule fuer Technik Rapperswil
@@ -26,6 +26,7 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.net.VpnService;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.Fragment;
@@ -140,11 +141,25 @@ public class MainActivity extends AppCompatActivity implements OnVpnProfileSelec
return true;
}
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu)
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+ {
+ menu.removeItem(R.id.menu_import_profile);
+ }
+ return true;
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
+ case R.id.menu_import_profile:
+ Intent intent = new Intent(this, VpnProfileImportActivity.class);
+ startActivity(intent);
+ return true;
case R.id.menu_manage_certs:
Intent certIntent = new Intent(this, TrustedCertificatesActivity.class);
startActivity(certIntent);
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
index 1b6b6e896..8ab1a178d 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 Tobias Brunner
+ * Copyright (C) 2016-2017 Tobias Brunner
* HSR Hochschule fuer Technik Rapperswil
*
* This program is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.security.KeyChain;
import android.security.KeyChainAliasCallback;
@@ -75,7 +76,9 @@ import javax.net.ssl.SSLHandshakeException;
public class VpnProfileImportActivity extends AppCompatActivity
{
private static final String PKCS12_INSTALLED = "PKCS12_INSTALLED";
+ private static final String PROFILE_URI = "PROFILE_URI";
private static final int INSTALL_PKCS12 = 0;
+ private static final int OPEN_DOCUMENT = 1;
private static final int PROFILE_LOADER = 0;
private static final int USER_CERT_LOADER = 1;
@@ -107,7 +110,7 @@ public class VpnProfileImportActivity extends AppCompatActivity
@Override
public Loader onCreateLoader(int id, Bundle args)
{
- return new ProfileLoader(VpnProfileImportActivity.this, getIntent().getData());
+ return new ProfileLoader(VpnProfileImportActivity.this, (Uri)args.getParcelable(PROFILE_URI));
}
@Override
@@ -197,16 +200,13 @@ public class VpnProfileImportActivity extends AppCompatActivity
String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action))
{
- mProgress = ProgressDialog.show(this, null, getString(R.string.loading),
- true, true, new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog)
- {
- finish();
- }
- });
-
- getLoaderManager().initLoader(PROFILE_LOADER, null, mProfileLoaderCallbacks);
+ loadProfile(getIntent().getData());
+ }
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+ {
+ Intent openIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ openIntent.setType("*/*");
+ startActivityForResult(openIntent, OPEN_DOCUMENT);
}
if (savedInstanceState != null)
@@ -279,9 +279,34 @@ public class VpnProfileImportActivity extends AppCompatActivity
mImportUserCert.setEnabled(false);
mSelectUserCert.performClick();
}
+ break;
+ case OPEN_DOCUMENT:
+ if (resultCode == Activity.RESULT_OK && data != null)
+ {
+ loadProfile(data.getData());
+ return;
+ }
+ finish();
+ break;
}
}
+ private void loadProfile(Uri uri)
+ {
+ mProgress = ProgressDialog.show(this, null, getString(R.string.loading),
+ true, true, new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog)
+ {
+ finish();
+ }
+ });
+
+ Bundle args = new Bundle();
+ args.putParcelable(PROFILE_URI, uri);
+ getLoaderManager().initLoader(PROFILE_LOADER, args, mProfileLoaderCallbacks);
+ }
+
public void handleProfile(ProfileLoadResult data)
{
mProgress.dismiss();
diff --git a/src/frontends/android/app/src/main/res/menu/main.xml b/src/frontends/android/app/src/main/res/menu/main.xml
index 599254a5e..3c8da084b 100644
--- a/src/frontends/android/app/src/main/res/menu/main.xml
+++ b/src/frontends/android/app/src/main/res/menu/main.xml
@@ -1,6 +1,6 @@
+
+
+
+
From cea8213f1bbff0cc772e42fa1eb1f04cb290e8a5 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 10:14:43 +0200
Subject: [PATCH 35/43] android: Handle checked state in activated background
---
.../app/src/main/res/drawable/activated_background.xml | 4 ++++
src/frontends/android/app/src/main/res/values/colors.xml | 3 +++
2 files changed, 7 insertions(+)
diff --git a/src/frontends/android/app/src/main/res/drawable/activated_background.xml b/src/frontends/android/app/src/main/res/drawable/activated_background.xml
index 04cf6effb..457df4fb3 100644
--- a/src/frontends/android/app/src/main/res/drawable/activated_background.xml
+++ b/src/frontends/android/app/src/main/res/drawable/activated_background.xml
@@ -23,6 +23,10 @@
android:state_activated="true"
android:drawable="@color/accent" />
+
+
diff --git a/src/frontends/android/app/src/main/res/values/colors.xml b/src/frontends/android/app/src/main/res/values/colors.xml
index f29ae1911..eedfe2397 100644
--- a/src/frontends/android/app/src/main/res/values/colors.xml
+++ b/src/frontends/android/app/src/main/res/values/colors.xml
@@ -39,6 +39,9 @@
#5a5a5a
+ #4a4a4a
+
#5a5a5a
From 5561633fcd54636b1a3f917eb5c86980d0c527b9 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 13:50:18 +0200
Subject: [PATCH 36/43] android: Add class that wraps ApplicationInfo for
selected apps
---
.../ui/adapter/SelectedApplicationEntry.java | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationEntry.java
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationEntry.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationEntry.java
new file mode 100644
index 000000000..0bfbbcfa5
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationEntry.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
+
+import java.text.Collator;
+
+public class SelectedApplicationEntry implements Comparable
+{
+ private final ApplicationInfo mInfo;
+ private final Drawable mIcon;
+ private final String mName;
+ private boolean mSelected;
+
+ public SelectedApplicationEntry(PackageManager packageManager, ApplicationInfo info)
+ {
+ mInfo = info;
+ CharSequence name = info.loadLabel(packageManager);
+ mName = name == null ? info.packageName : name.toString();
+ mIcon = info.loadIcon(packageManager);
+ }
+
+ public void setSelected(boolean selected)
+ {
+ mSelected = selected;
+ }
+
+ public boolean isSelected()
+ {
+ return mSelected;
+ }
+
+ public ApplicationInfo getInfo()
+ {
+ return mInfo;
+ }
+
+ public Drawable getIcon()
+ {
+ return mIcon;
+ }
+
+ @Override
+ public String toString()
+ {
+ return mName;
+ }
+
+ @Override
+ public int compareTo(@NonNull SelectedApplicationEntry another)
+ {
+ return Collator.getInstance().compare(toString(), another.toString());
+ }
+}
From 238c3061b7b8d674b7ec6053c5468a739b555635 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 13:54:32 +0200
Subject: [PATCH 37/43] android: Add list item layout for installed/selected
apps
---
.../res/layout/selected_application_item.xml | 52 +++++++++++++++++++
1 file changed, 52 insertions(+)
create mode 100644 src/frontends/android/app/src/main/res/layout/selected_application_item.xml
diff --git a/src/frontends/android/app/src/main/res/layout/selected_application_item.xml b/src/frontends/android/app/src/main/res/layout/selected_application_item.xml
new file mode 100644
index 000000000..faece8396
--- /dev/null
+++ b/src/frontends/android/app/src/main/res/layout/selected_application_item.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
From 208e15c0bab77b609c844c68408550eace537e2d Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 14:11:16 +0200
Subject: [PATCH 38/43] android: Add filterable adapter for list of
installed/selected apps
---
.../adapter/SelectedApplicationsAdapter.java | 162 ++++++++++++++++++
1 file changed, 162 insertions(+)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationsAdapter.java
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationsAdapter.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationsAdapter.java
new file mode 100644
index 000000000..2a2fe9533
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/adapter/SelectedApplicationsAdapter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Filter;
+import android.widget.Filterable;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import org.strongswan.android.R;
+import org.strongswan.android.ui.widget.CheckableLinearLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SelectedApplicationsAdapter extends BaseAdapter implements Filterable
+{
+ private Context mContext;
+ private final Object mLock = new Object();
+ private List mData;
+ private List mDataFiltered;
+ private SelectedApplicationsFilter mFilter;
+
+ public SelectedApplicationsAdapter(Context context)
+ {
+ mContext = context;
+ mData = mDataFiltered = new ArrayList<>();
+ }
+
+ /**
+ * Set new data for this adapter.
+ *
+ * @param data the new data (null to clear)
+ */
+ public void setData(List data)
+ {
+ synchronized (mLock)
+ {
+ mData.clear();
+ mDataFiltered = mData;
+ if (data != null)
+ {
+ mData.addAll(data);
+ }
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount()
+ {
+ return mDataFiltered.size();
+ }
+
+ @Override
+ public SelectedApplicationEntry getItem(int position)
+ {
+ return mDataFiltered.get(position);
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return mDataFiltered.get(position).toString().hashCode();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent)
+ {
+ View view;
+ if (convertView != null)
+ {
+ view = convertView;
+ }
+ else
+ {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ view = inflater.inflate(R.layout.selected_application_item, parent, false);
+ }
+ SelectedApplicationEntry item = getItem(position);
+ CheckableLinearLayout checkable = (CheckableLinearLayout)view;
+ checkable.setChecked(item.isSelected());
+ ImageView icon = (ImageView)view.findViewById(R.id.app_icon);
+ icon.setImageDrawable(item.getIcon());
+ TextView text = (TextView)view.findViewById(R.id.app_name);
+ text.setText(item.toString());
+ return view;
+ }
+
+ @Override
+ public Filter getFilter()
+ {
+ if (mFilter == null)
+ {
+ mFilter = new SelectedApplicationsFilter();
+ }
+ return mFilter;
+ }
+
+ private class SelectedApplicationsFilter extends Filter
+ {
+
+ @Override
+ protected FilterResults performFiltering(CharSequence constraint)
+ {
+ FilterResults results = new FilterResults();
+ ArrayList data, filtered;
+
+ synchronized (mLock)
+ {
+ data = new ArrayList<>(mData);
+ }
+
+ if (TextUtils.isEmpty(constraint))
+ {
+ filtered = data;
+ }
+ else
+ {
+ String filter = constraint.toString().toLowerCase();
+ filtered = new ArrayList<>();
+ for (SelectedApplicationEntry entry : data)
+ {
+ if (entry.toString().toLowerCase().contains(filter))
+ {
+ filtered.add(entry);
+ }
+ }
+ }
+ results.values = filtered;
+ results.count = filtered.size();
+ return results;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void publishResults(CharSequence constraint, FilterResults results)
+ {
+ mDataFiltered = (List)results.values;
+ notifyDataSetChanged();
+ }
+ }
+}
From eb59c6a38a7b0038ea3abc9df39f5a5ed6971206 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 15:10:41 +0200
Subject: [PATCH 39/43] android: Add list fragment for the selection of apps
---
.../ui/SelectedApplicationsListFragment.java | 267 ++++++++++++++++++
1 file changed, 267 insertions(+)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsListFragment.java
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsListFragment.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsListFragment.java
new file mode 100644
index 000000000..38c7494d5
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsListFragment.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.ListFragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.AsyncTaskLoader;
+import android.support.v4.content.Loader;
+import android.support.v7.widget.SearchView;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Filter;
+import android.widget.ListView;
+
+import org.strongswan.android.R;
+import org.strongswan.android.data.VpnProfileDataSource;
+import org.strongswan.android.ui.adapter.SelectedApplicationEntry;
+import org.strongswan.android.ui.adapter.SelectedApplicationsAdapter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class SelectedApplicationsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks, List>>, SearchView.OnQueryTextListener
+{
+ private SelectedApplicationsAdapter mAdapter;
+ private SortedSet mSelection;
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState)
+ {
+ super.onActivityCreated(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+
+ mAdapter = new SelectedApplicationsAdapter(getActivity());
+ setListAdapter(mAdapter);
+ setListShown(false);
+
+ ArrayList selection;
+ if (savedInstanceState == null)
+ {
+ selection = getActivity().getIntent().getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+ }
+ else
+ {
+ selection = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+ }
+ mSelection = new TreeSet<>(selection);
+
+ getLoaderManager().initLoader(0, null, this);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState)
+ {
+ super.onSaveInstanceState(outState);
+ outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelection));
+ }
+
+ /**
+ * Returns the package names of all selected apps
+ */
+ public ArrayList getSelectedApplications()
+ {
+ return new ArrayList<>(mSelection);
+ }
+
+ /**
+ * Track check state as ListView is unable to do that when using filters
+ */
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id)
+ {
+ super.onListItemClick(l, v, position, id);
+ SelectedApplicationEntry item = (SelectedApplicationEntry)getListView().getItemAtPosition(position);
+ item.setSelected(!item.isSelected());
+ if (item.isSelected())
+ {
+ mSelection.add(item.getInfo().packageName);
+ }
+ else
+ {
+ mSelection.remove(item.getInfo().packageName);
+ }
+ }
+
+ /**
+ * Manually set the check state as ListView is unable to track that when using filters
+ */
+ private void setCheckState()
+ {
+ for (int i = 0; i < getListView().getCount(); i++)
+ {
+ SelectedApplicationEntry item = mAdapter.getItem(i);
+ getListView().setItemChecked(i, item.isSelected());
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
+ {
+ MenuItem item = menu.add(R.string.search);
+ item.setIcon(android.R.drawable.ic_menu_search);
+ item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+
+ SearchView sv = new SearchView(getActivity());
+ sv.setOnQueryTextListener(this);
+ item.setActionView(sv);
+
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public Loader, List>> onCreateLoader(int id, Bundle args)
+ {
+ return new InstalledPackagesLoader(getActivity(), mSelection);
+ }
+
+ @Override
+ public void onLoadFinished(Loader, List>> loader, Pair, List> data)
+ {
+ mAdapter.setData(data.first);
+ mSelection.removeAll(data.second);
+ setCheckState();
+
+ if (isResumed())
+ {
+ setListShown(true);
+ }
+ else
+ {
+ setListShownNoAnimation(true);
+ }
+ }
+
+ @Override
+ public void onLoaderReset(Loader, List>> loader)
+ {
+ mAdapter.setData(null);
+ }
+
+ @Override
+ public boolean onQueryTextSubmit(String query)
+ {
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText)
+ {
+ String search = TextUtils.isEmpty(newText) ? null : newText;
+ mAdapter.getFilter().filter(search, new Filter.FilterListener()
+ {
+ @Override
+ public void onFilterComplete(int count)
+ {
+ setCheckState();
+ }
+ });
+ return true;
+ }
+
+ public static class InstalledPackagesLoader extends AsyncTaskLoader, List>>
+ {
+ private final PackageManager mPackageManager;
+ private final SortedSet mSelection;
+ private Pair, List> mData;
+
+ public InstalledPackagesLoader(Context context, SortedSet selection)
+ {
+ super(context);
+ mPackageManager = context.getPackageManager();
+ mSelection = selection;
+ }
+
+ @Override
+ public Pair, List> loadInBackground()
+ {
+ List apps = new ArrayList<>();
+ SortedSet seen = new TreeSet<>();
+ for (ApplicationInfo info : mPackageManager.getInstalledApplications(PackageManager.GET_META_DATA))
+ {
+ /* skip apps that can't access the network anyway */
+ if (mPackageManager.checkPermission(Manifest.permission.INTERNET, info.packageName) == PackageManager.PERMISSION_GRANTED)
+ {
+ SelectedApplicationEntry entry = new SelectedApplicationEntry(mPackageManager, info);
+ entry.setSelected(mSelection.contains(info.packageName));
+ apps.add(entry);
+ seen.add(info.packageName);
+ }
+ }
+ Collections.sort(apps);
+ /* check for selected packages that don't exist anymore */
+ List missing = new ArrayList<>();
+ for (String pkg : mSelection)
+ {
+ if (!seen.contains(pkg))
+ {
+ missing.add(pkg);
+ }
+ }
+ return new Pair<>(apps, missing);
+ }
+
+ @Override
+ protected void onStartLoading()
+ {
+ if (mData != null)
+ { /* if we have data ready, deliver it directly */
+ deliverResult(mData);
+ }
+ if (takeContentChanged() || mData == null)
+ {
+ forceLoad();
+ }
+ }
+
+ @Override
+ public void deliverResult(Pair, List> data)
+ {
+ if (isReset())
+ {
+ return;
+ }
+ mData = data;
+ if (isStarted())
+ { /* if it is started we deliver the data directly,
+ * otherwise this is handled in onStartLoading */
+ super.deliverResult(data);
+ }
+ }
+
+ @Override
+ protected void onReset()
+ {
+ mData = null;
+ super.onReset();
+ }
+ }
+}
From 3cc6a03fa018e602d1b94c05151a31e4e7fa3124 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 15:13:30 +0200
Subject: [PATCH 40/43] android: Add simple activity for the selection of apps
---
.../android/app/src/main/AndroidManifest.xml | 4 +
.../ui/SelectedApplicationsActivity.java | 76 +++++++++++++++++++
.../app/src/main/res/values-de/strings.xml | 1 +
.../app/src/main/res/values-pl/strings.xml | 1 +
.../app/src/main/res/values-ru/strings.xml | 1 +
.../app/src/main/res/values-ua/strings.xml | 1 +
.../src/main/res/values-zh-rCN/strings.xml | 1 +
.../src/main/res/values-zh-rTW/strings.xml | 1 +
.../app/src/main/res/values/strings.xml | 1 +
9 files changed, 87 insertions(+)
create mode 100644 src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsActivity.java
diff --git a/src/frontends/android/app/src/main/AndroidManifest.xml b/src/frontends/android/app/src/main/AndroidManifest.xml
index bc2de9af7..c7f745b70 100644
--- a/src/frontends/android/app/src/main/AndroidManifest.xml
+++ b/src/frontends/android/app/src/main/AndroidManifest.xml
@@ -48,6 +48,10 @@
android:name=".ui.TrustedCertificatesActivity"
android:label="@string/trusted_certs_title" >
+
+
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsActivity.java
new file mode 100644
index 000000000..db2e56655
--- /dev/null
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/SelectedApplicationsActivity.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.MenuItem;
+
+import org.strongswan.android.data.VpnProfileDataSource;
+
+public class SelectedApplicationsActivity extends AppCompatActivity
+{
+ private static final String LIST_TAG = "ApplicationList";
+ private SelectedApplicationsListFragment mApps;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ ActionBar actionBar = getSupportActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(true);
+
+ FragmentManager fm = getSupportFragmentManager();
+ mApps = (SelectedApplicationsListFragment)fm.findFragmentByTag(LIST_TAG);
+ if (mApps == null)
+ {
+ mApps = new SelectedApplicationsListFragment();
+ fm.beginTransaction().add(android.R.id.content, mApps, LIST_TAG).commit();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case android.R.id.home:
+ prepareResult();
+ finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed()
+ {
+ prepareResult();
+ super.onBackPressed();
+ }
+
+ private void prepareResult()
+ {
+ Intent data = new Intent();
+ data.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, mApps.getSelectedApplications());
+ setResult(RESULT_OK, data);
+ }
+}
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 b981375ff..ca22f78b5 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
@@ -87,6 +87,7 @@
Nur Verkehr in die spezifizierten Subnetze wird via VPN geleitet, der Rest wird behandelt, als ob kein VPN vorhanden wäre (mit Leerzeichen getrennt, z.B. \"192.168.1.0/24 2001:db8::/64\")
Ausgeschlossene Subnetze
Verkehr in diese Subnetze wird vom VPN ausgeschlossen und behandelt, als ob kein VPN vorhanden wäre (mit Leerzeichen getrennt, z.B. \"192.168.1.0/24 2001:db8::/64\")
+ Apps auswählen
VPN Profile importieren
VPN Profil-Import fehlgeschlagen
VPN Profil-Import fehlgeschlagen: %1$s
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 6924aecab..1708f6edf 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
@@ -87,6 +87,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
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 0d117b70b..0183e0306 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
@@ -84,6 +84,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
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 22e8da227..156300d3e 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
@@ -85,6 +85,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
index 8df905b82..119d462cc 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -84,6 +84,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
导入VPN配置
导入VPN配置失败
导入VPN配置失败: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
index 1aa060587..2b0ce6b2b 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -84,6 +84,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
匯入VPN設定檔
匯入VPN設定檔失敗
匯入VPN設定檔失敗: %1$s
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 3b35306da..e9a62f03c 100644
--- a/src/frontends/android/app/src/main/res/values/strings.xml
+++ b/src/frontends/android/app/src/main/res/values/strings.xml
@@ -87,6 +87,7 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Select applications
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
From 291ef58c69d62ad5aed1817ec9ee7ee9ac3b842f Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Tue, 27 Jun 2017 15:17:43 +0200
Subject: [PATCH 41/43] android: Make app handling and selection of apps
configurable in profile editor
---
.../android/ui/VpnProfileDetailActivity.java | 98 ++++++++++++++++++-
.../main/res/layout/profile_detail_view.xml | 22 +++++
.../app/src/main/res/values-de/arrays.xml | 11 ++-
.../app/src/main/res/values-de/strings.xml | 4 +
.../app/src/main/res/values-pl/arrays.xml | 7 ++
.../app/src/main/res/values-pl/strings.xml | 4 +
.../app/src/main/res/values-ru/arrays.xml | 7 ++
.../app/src/main/res/values-ru/strings.xml | 4 +
.../app/src/main/res/values-ua/arrays.xml | 7 ++
.../app/src/main/res/values-ua/strings.xml | 4 +
.../app/src/main/res/values-zh-rCN/arrays.xml | 7 ++
.../src/main/res/values-zh-rCN/strings.xml | 4 +
.../app/src/main/res/values-zh-rTW/arrays.xml | 7 ++
.../src/main/res/values-zh-rTW/strings.xml | 4 +
.../app/src/main/res/values/arrays.xml | 13 ++-
.../app/src/main/res/values/strings.xml | 4 +
16 files changed, 200 insertions(+), 7 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 d495b742a..0bae6142d 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
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2016 Tobias Brunner
+ * Copyright (C) 2012-2017 Tobias Brunner
* Copyright (C) 2012 Giuliano Grassi
* Copyright (C) 2012 Ralf Sager
* HSR Hochschule fuer Technik Rapperswil
@@ -57,6 +57,7 @@ import android.widget.TextView;
import org.strongswan.android.R;
import org.strongswan.android.data.VpnProfile;
+import org.strongswan.android.data.VpnProfile.SelectedAppsHandling;
import org.strongswan.android.data.VpnProfileDataSource;
import org.strongswan.android.data.VpnType;
import org.strongswan.android.data.VpnType.VpnTypeFeature;
@@ -68,11 +69,15 @@ import org.strongswan.android.utils.Constants;
import org.strongswan.android.utils.IPRangeSet;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.UUID;
public class VpnProfileDetailActivity extends AppCompatActivity
{
private static final int SELECT_TRUSTED_CERTIFICATE = 0;
+ private static final int SELECT_APPLICATIONS = 1;
private VpnProfileDataSource mDataSource;
private Long mId;
@@ -82,6 +87,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private String mSelectedUserId;
private TrustedCertificateEntry mUserCertEntry;
private VpnType mVpnType = VpnType.IKEV2_EAP;
+ private SelectedAppsHandling mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE;
+ private SortedSet mSelectedApps = new TreeSet<>();
private VpnProfile mProfile;
private MultiAutoCompleteTextView mName;
private TextInputLayoutHelper mNameWrap;
@@ -112,6 +119,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
private TextInputLayoutHelper mExcludedSubnetsWrap;
private CheckBox mBlockIPv4;
private CheckBox mBlockIPv6;
+ private Spinner mSelectSelectedAppsHandling;
+ private RelativeLayout mSelectApps;
@Override
public void onCreate(Bundle savedInstanceState)
@@ -161,6 +170,9 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mBlockIPv4 = (CheckBox)findViewById(R.id.split_tunneling_v4);
mBlockIPv6 = (CheckBox)findViewById(R.id.split_tunneling_v6);
+ mSelectSelectedAppsHandling = (Spinner)findViewById(R.id.apps_handling);
+ mSelectApps = (RelativeLayout)findViewById(R.id.select_applications);
+
final SpaceTokenizer spaceTokenizer = new SpaceTokenizer();
mName.setTokenizer(spaceTokenizer);
mRemoteId.setTokenizer(spaceTokenizer);
@@ -265,6 +277,32 @@ public class VpnProfileDetailActivity extends AppCompatActivity
}
});
+ mSelectSelectedAppsHandling.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id)
+ {
+ mSelectedAppsHandling = SelectedAppsHandling.values()[position];
+ updateAppsSelector();
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent)
+ { /* should not happen */
+ mSelectedAppsHandling = SelectedAppsHandling.SELECTED_APPS_DISABLE;
+ updateAppsSelector();
+ }
+ });
+
+ mSelectApps.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v)
+ {
+ Intent intent = new Intent(VpnProfileDetailActivity.this, SelectedApplicationsActivity.class);
+ intent.putExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
+ startActivityForResult(intent, SELECT_APPLICATIONS);
+ }
+ });
+
mId = savedInstanceState == null ? null : savedInstanceState.getLong(VpnProfileDataSource.KEY_ID);
if (mId == null)
{
@@ -277,6 +315,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
updateCredentialView();
updateCertificateSelector();
updateAdvancedSettings();
+ updateAppsSelector();
}
@Override
@@ -306,6 +345,7 @@ public class VpnProfileDetailActivity extends AppCompatActivity
{
outState.putString(VpnProfileDataSource.KEY_CERTIFICATE, mCertEntry.getAlias());
}
+ outState.putStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST, new ArrayList<>(mSelectedApps));
}
@Override
@@ -347,6 +387,14 @@ public class VpnProfileDetailActivity extends AppCompatActivity
updateCertificateSelector();
}
break;
+ case SELECT_APPLICATIONS:
+ if (resultCode == RESULT_OK)
+ {
+ ArrayList selection = data.getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+ mSelectedApps = new TreeSet<>(selection);
+ updateAppsSelector();
+ }
+ break;
default:
super.onActivityResult(requestCode, resultCode, data);
}
@@ -435,6 +483,40 @@ public class VpnProfileDetailActivity extends AppCompatActivity
}
}
+ /**
+ * Update the application selection UI
+ */
+ private void updateAppsSelector()
+ {
+ if (mSelectedAppsHandling == SelectedAppsHandling.SELECTED_APPS_DISABLE)
+ {
+ mSelectApps.setEnabled(false);
+ mSelectApps.setVisibility(View.GONE);
+
+ }
+ else
+ {
+ mSelectApps.setEnabled(true);
+ mSelectApps.setVisibility(View.VISIBLE);
+
+ ((TextView)mSelectApps.findViewById(android.R.id.text1)).setText(R.string.profile_select_apps);
+ String selected;
+ switch (mSelectedApps.size())
+ {
+ case 0:
+ selected = getString(R.string.profile_select_no_apps);
+ break;
+ case 1:
+ selected = getString(R.string.profile_select_one_app);
+ break;
+ default:
+ selected = getString(R.string.profile_select_x_apps, mSelectedApps.size());
+ break;
+ }
+ ((TextView)mSelectApps.findViewById(android.R.id.text2)).setText(selected);
+ }
+ }
+
/**
* Update the advanced settings UI depending on whether any advanced
* settings have already been made.
@@ -447,7 +529,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
Integer st = mProfile.getSplitTunneling();
show = mProfile.getRemoteId() != null || mProfile.getMTU() != null ||
mProfile.getPort() != null || (st != null && st != 0) ||
- mProfile.getIncludedSubnets() != null || mProfile.getExcludedSubnets() != null;
+ mProfile.getIncludedSubnets() != null || mProfile.getExcludedSubnets() != null ||
+ mProfile.getSelectedAppsHandling() != SelectedAppsHandling.SELECTED_APPS_DISABLE;
}
mShowAdvanced.setVisibility(!show ? View.VISIBLE : View.GONE);
mAdvancedSettings.setVisibility(show ? View.VISIBLE : View.GONE);
@@ -575,6 +658,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
st |= mBlockIPv4.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4 : 0;
st |= mBlockIPv6.isChecked() ? VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6 : 0;
mProfile.setSplitTunneling(st == 0 ? null : st);
+ mProfile.setSelectedAppsHandling(mSelectedAppsHandling);
+ mProfile.setSelectedApps(mSelectedApps);
}
/**
@@ -604,6 +689,8 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mExcludedSubnets.setText(mProfile.getExcludedSubnets());
mBlockIPv4.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV4) != 0);
mBlockIPv6.setChecked(mProfile.getSplitTunneling() != null && (mProfile.getSplitTunneling() & VpnProfile.SPLIT_TUNNELING_BLOCK_IPV6) != 0);
+ mSelectedAppsHandling = mProfile.getSelectedAppsHandling();
+ mSelectedApps = mProfile.getSelectedAppsSet();
useralias = mProfile.getUserCertificateAlias();
local_id = mProfile.getLocalId();
alias = mProfile.getCertificateAlias();
@@ -646,6 +733,13 @@ public class VpnProfileDetailActivity extends AppCompatActivity
mCertEntry = null;
}
}
+
+ mSelectSelectedAppsHandling.setSelection(mSelectedAppsHandling.ordinal());
+ if (savedInstanceState != null)
+ {
+ ArrayList selectedApps = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+ mSelectedApps = new TreeSet<>(selectedApps);
+ }
}
/**
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 f9a512d96..8b153b89a 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
@@ -250,6 +250,7 @@
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textSize="20sp"
android:text="@string/profile_split_tunneling_label" />
@@ -306,6 +307,27 @@
android:layout_height="wrap_content"
android:text="@string/profile_split_tunnelingv6_title" />
+
+
+
+
+
+
diff --git a/src/frontends/android/app/src/main/res/values-de/arrays.xml b/src/frontends/android/app/src/main/res/values-de/arrays.xml
index d05140165..1eda4f1ae 100644
--- a/src/frontends/android/app/src/main/res/values-de/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-de/arrays.xml
@@ -1,7 +1,7 @@
+
+ - Alle Apps verwenden das VPN
+ - Ausgewählte Apps vom VPN ausschliessen
+ - Nur ausgewählte Apps verwenden das VPN
+
\ 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 ca22f78b5..5c5a22a64 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
@@ -87,7 +87,11 @@
Nur Verkehr in die spezifizierten Subnetze wird via VPN geleitet, der Rest wird behandelt, als ob kein VPN vorhanden wäre (mit Leerzeichen getrennt, z.B. \"192.168.1.0/24 2001:db8::/64\")
Ausgeschlossene Subnetze
Verkehr in diese Subnetze wird vom VPN ausgeschlossen und behandelt, als ob kein VPN vorhanden wäre (mit Leerzeichen getrennt, z.B. \"192.168.1.0/24 2001:db8::/64\")
+ Apps
Apps auswählen
+ Keine Apps ausgewählt
+ Eine App ausgewählt
+ %1$d Apps ausgewählt
VPN Profile importieren
VPN Profil-Import fehlgeschlagen
VPN Profil-Import fehlgeschlagen: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-pl/arrays.xml b/src/frontends/android/app/src/main/res/values-pl/arrays.xml
index 30e43f1fb..5df0e72c4 100644
--- a/src/frontends/android/app/src/main/res/values-pl/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-pl/arrays.xml
@@ -22,4 +22,11 @@
- IKEv2 EAP-TLS (certyfikat)
- IKEv2 EAP-TNC (użytkownik/hasło)
+
+
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
\ No newline at end of file
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 1708f6edf..d8965a1e4 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
@@ -87,7 +87,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-ru/arrays.xml b/src/frontends/android/app/src/main/res/values-ru/arrays.xml
index 5fbd43168..64c234eb4 100644
--- a/src/frontends/android/app/src/main/res/values-ru/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-ru/arrays.xml
@@ -21,4 +21,11 @@
- IKEv2 EAP-TLS (Сертификат)
- IKEv2 EAP-TNC (Логин/Пароль)
+
+
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
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 0183e0306..7296554d8 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
@@ -84,7 +84,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-ua/arrays.xml b/src/frontends/android/app/src/main/res/values-ua/arrays.xml
index 1acc0d769..8401ef91f 100644
--- a/src/frontends/android/app/src/main/res/values-ua/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-ua/arrays.xml
@@ -21,4 +21,11 @@
- IKEv2 EAP-TLS (Сертифікати)
- IKEv2 EAP-TNC (Логін/Пароль)
+
+
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
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 156300d3e..6f30fcf16 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
@@ -85,7 +85,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
index 546000ab2..64bd21bbe 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
@@ -21,4 +21,11 @@
- IKEv2 EAP-TLS (证书)
- IKEv2 EAP-TNC (用户名/密码)
+
+
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
\ No newline at end of file
diff --git a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
index 119d462cc..415212eab 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
@@ -84,7 +84,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
导入VPN配置
导入VPN配置失败
导入VPN配置失败: %1$s
diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
index c7a1963bb..e4ceccc95 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
@@ -21,4 +21,11 @@
- IKEv2 EAP-TLS (憑證)
- IKEv2 EAP-TNC (用戶名稱/密碼)
+
+
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
\ No newline at end of file
diff --git a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
index 2b0ce6b2b..e1cdf32e5 100644
--- a/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
+++ b/src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
@@ -84,7 +84,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
匯入VPN設定檔
匯入VPN設定檔失敗
匯入VPN設定檔失敗: %1$s
diff --git a/src/frontends/android/app/src/main/res/values/arrays.xml b/src/frontends/android/app/src/main/res/values/arrays.xml
index b324b594f..be12aab1c 100644
--- a/src/frontends/android/app/src/main/res/values/arrays.xml
+++ b/src/frontends/android/app/src/main/res/values/arrays.xml
@@ -1,7 +1,7 @@
+
+ - All applications use the VPN
+ - Exclude selected applications from the VPN
+ - Only selected applications use the VPN
+
+
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 e9a62f03c..fa38753c2 100644
--- a/src/frontends/android/app/src/main/res/values/strings.xml
+++ b/src/frontends/android/app/src/main/res/values/strings.xml
@@ -87,7 +87,11 @@
Only route traffic to specific subnets via VPN, everything else is routed as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
Excluded subnets
Traffic to these subnets will not be routed via VPN, but as if there was no VPN (separated by spaces, e.g. \"192.168.1.0/24 2001:db8::/64\")
+ Applications
Select applications
+ No applications selected
+ One application selected
+ %1$d applications selected
Import VPN profile
Failed to import VPN profile
Failed to import VPN profile: %1$s
From 646260f4641db08daa1574374d0c72490913c091 Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Wed, 28 Jun 2017 12:33:17 +0200
Subject: [PATCH 42/43] android: Change format of address ranges and print sets
---
.../org/strongswan/android/utils/IPRange.java | 2 +-
.../org/strongswan/android/utils/IPRangeSet.java | 15 +++++++++++++++
.../strongswan/android/test/IPRangeSetTest.java | 7 +++++++
.../org/strongswan/android/test/IPRangeTest.java | 2 +-
4 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRange.java b/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRange.java
index 621dfd691..6c0aa659b 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRange.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRange.java
@@ -241,7 +241,7 @@ public class IPRange implements Comparable
{
return InetAddress.getByAddress(mFrom).getHostAddress() + "/" + mPrefix;
}
- return InetAddress.getByAddress(mFrom).getHostAddress() + ".." +
+ return InetAddress.getByAddress(mFrom).getHostAddress() + "-" +
InetAddress.getByAddress(mTo).getHostAddress();
}
catch (UnknownHostException ignored)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRangeSet.java b/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRangeSet.java
index d5f4b619f..4716055bb 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRangeSet.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/utils/IPRangeSet.java
@@ -205,4 +205,19 @@ public class IPRangeSet implements Iterable
{
return mRanges.size();
}
+
+ @Override
+ public String toString()
+ { /* we could use TextUtils, but that causes the unit tests to fail */
+ StringBuilder sb = new StringBuilder();
+ for (IPRange range : mRanges)
+ {
+ if (sb.length() > 0)
+ {
+ sb.append(" ");
+ }
+ sb.append(range.toString());
+ }
+ return sb.toString();
+ }
}
diff --git a/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeSetTest.java b/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeSetTest.java
index 3b054ceed..af60c15ba 100644
--- a/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeSetTest.java
+++ b/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeSetTest.java
@@ -302,4 +302,11 @@ public class IPRangeSetTest
assertEquals("next", new IPRange("192.168.1.0/24"), iterator.next());
iterator.remove();
}
+
+ @Test
+ public void testToString() throws UnknownHostException
+ {
+ IPRangeSet set = IPRangeSet.fromString("192.168.3.10/24 192.168.1.0/24 192.168.1.1-192.168.1.10");
+ assertEquals("string", "192.168.1.0/24 192.168.3.0/24", set.toString());
+ }
}
diff --git a/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeTest.java b/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeTest.java
index 15d710e5a..51e976e34 100644
--- a/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeTest.java
+++ b/src/frontends/android/app/src/test/java/org/strongswan/android/test/IPRangeTest.java
@@ -200,7 +200,7 @@ public class IPRangeTest
{
testToString("192.168.1.1", "192.168.1.1", "192.168.1.1/32");
testToString("192.168.1.0", "192.168.1.255", "192.168.1.0/24");
- testToString("192.168.1.1", "192.168.1.255", "192.168.1.1..192.168.1.255");
+ testToString("192.168.1.1", "192.168.1.255", "192.168.1.1-192.168.1.255");
testToString("0.0.0.0", "255.255.255.255", "0.0.0.0/0");
testToString("fec1::1", "fec1::1", "fec1:0:0:0:0:0:0:1/128");
testToString("fec1::", "fec1::ffff", "fec1:0:0:0:0:0:0:0/112");
From 0b075420df66cba62287b1609b394253ac67f78e Mon Sep 17 00:00:00 2001
From: Tobias Brunner
Date: Wed, 28 Jun 2017 14:28:38 +0200
Subject: [PATCH 43/43] android: Use arrays as primary config option for
subnets in profile files
---
.../android/ui/VpnProfileImportActivity.java | 26 ++++++++++++++++---
1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
index d4c9d32e8..5833258d2 100644
--- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
+++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileImportActivity.java
@@ -527,15 +527,33 @@ public class VpnProfileImportActivity extends AppCompatActivity
private String getSubnets(JSONObject split, String key) throws JSONException
{
- String subnets = split.optString(key, null);
- if (subnets != null && !subnets.isEmpty())
+ ArrayList subnets = new ArrayList<>();
+ JSONArray arr = split.optJSONArray(key);
+ if (arr != null)
{
- if (IPRangeSet.fromString(subnets) == null)
+ for (int i = 0; i < arr.length(); i++)
+ { /* replace all spaces, e.g. in "192.168.1.1 - 192.168.1.10" */
+ subnets.add(arr.getString(i).replace(" ", ""));
+ }
+ }
+ else
+ {
+ String value = split.optString(key, null);
+ if (!TextUtils.isEmpty(value))
+ {
+ subnets.add(value);
+ }
+ }
+ if (subnets.size() > 0)
+ {
+ String joined = TextUtils.join(" ", subnets);
+ IPRangeSet ranges = IPRangeSet.fromString(joined);
+ if (ranges == null)
{
throw new JSONException(getString(R.string.profile_import_failed_value,
"split-tunneling." + key));
}
- return subnets;
+ return ranges.toString();
}
return null;
}