android: Make app handling and selection of apps configurable in profile editor
authorTobias Brunner <tobias@strongswan.org>
Tue, 27 Jun 2017 13:17:43 +0000 (15:17 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 3 Jul 2017 08:27:54 +0000 (10:27 +0200)
16 files changed:
src/frontends/android/app/src/main/java/org/strongswan/android/ui/VpnProfileDetailActivity.java
src/frontends/android/app/src/main/res/layout/profile_detail_view.xml
src/frontends/android/app/src/main/res/values-de/arrays.xml
src/frontends/android/app/src/main/res/values-de/strings.xml
src/frontends/android/app/src/main/res/values-pl/arrays.xml
src/frontends/android/app/src/main/res/values-pl/strings.xml
src/frontends/android/app/src/main/res/values-ru/arrays.xml
src/frontends/android/app/src/main/res/values-ru/strings.xml
src/frontends/android/app/src/main/res/values-ua/arrays.xml
src/frontends/android/app/src/main/res/values-ua/strings.xml
src/frontends/android/app/src/main/res/values-zh-rCN/arrays.xml
src/frontends/android/app/src/main/res/values-zh-rCN/strings.xml
src/frontends/android/app/src/main/res/values-zh-rTW/arrays.xml
src/frontends/android/app/src/main/res/values-zh-rTW/strings.xml
src/frontends/android/app/src/main/res/values/arrays.xml
src/frontends/android/app/src/main/res/values/strings.xml

index d495b74..0bae614 100644 (file)
@@ -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<String> 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<String> selection = data.getStringArrayListExtra(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+                                       mSelectedApps = new TreeSet<>(selection);
+                                       updateAppsSelector();
+                               }
+                               break;
                        default:
                                super.onActivityResult(requestCode, resultCode, data);
                }
@@ -436,6 +484,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<String> selectedApps = savedInstanceState.getStringArrayList(VpnProfileDataSource.KEY_SELECTED_APPS_LIST);
+                       mSelectedApps = new TreeSet<>(selectedApps);
+               }
        }
 
        /**
index f9a512d..8b153b8 100644 (file)
                 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" />
 
                 android:layout_height="wrap_content"
                 android:text="@string/profile_split_tunnelingv6_title" />
 
+            <TextView
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:layout_marginBottom="10dp"
+                android:layout_marginLeft="4dp"
+                android:layout_marginStart="4dp"
+                android:textSize="20sp"
+                android:text="@string/profile_select_apps_label" />
+
+            <Spinner
+                android:id="@+id/apps_handling"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:spinnerMode="dropdown"
+                android:entries="@array/apps_handling" />
+
+            <include
+                android:id="@+id/select_applications"
+                layout="@layout/two_line_button" />
+
         </LinearLayout>
 
     </LinearLayout>
index d051401..1eda4f1 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2012-2013 Tobias Brunner
-    Hochschule fuer Technik Rapperswil
+    Copyright (C) 2012-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
         <item>IKEv2 EAP-TLS (Zertifikat)</item>
         <item>IKEv2 EAP-TNC (Benutzername/Passwort)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>Alle Apps verwenden das VPN</item>
+        <item>Ausgewählte Apps vom VPN ausschliessen</item>
+        <item>Nur ausgewählte Apps verwenden das VPN</item>
+    </string-array>
 </resources>
\ No newline at end of file
index ca22f78..5c5a22a 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Ausgeschlossene Subnetze</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Apps</string>
     <string name="profile_select_apps">Apps auswählen</string>
+    <string name="profile_select_no_apps">Keine Apps ausgewählt</string>
+    <string name="profile_select_one_app">Eine App ausgewählt</string>
+    <string name="profile_select_x_apps">%1$d Apps ausgewählt</string>
     <string name="profile_import">VPN Profile importieren</string>
     <string name="profile_import_failed">VPN Profil-Import fehlgeschlagen</string>
     <string name="profile_import_failed_detail">VPN Profil-Import fehlgeschlagen: %1$s</string>
index 30e43f1..5df0e72 100644 (file)
         <item>IKEv2 EAP-TLS (certyfikat)</item>
         <item>IKEv2 EAP-TNC (użytkownik/hasło)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
 </resources>
\ No newline at end of file
index 1708f6e..d8965a1 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">Import VPN profile</string>
     <string name="profile_import_failed">Failed to import VPN profile</string>
     <string name="profile_import_failed_detail">Failed to import VPN profile: %1$s</string>
index 5fbd431..64c234e 100644 (file)
         <item>IKEv2 EAP-TLS (Сертификат)</item>
         <item>IKEv2 EAP-TNC (Логин/Пароль)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
 </resources>
index 0183e03..7296554 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">Import VPN profile</string>
     <string name="profile_import_failed">Failed to import VPN profile</string>
     <string name="profile_import_failed_detail">Failed to import VPN profile: %1$s</string>
index 1acc0d7..8401ef9 100644 (file)
         <item>IKEv2 EAP-TLS (Сертифікати)</item>
         <item>IKEv2 EAP-TNC (Логін/Пароль)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
 </resources>
index 156300d..6f30fcf 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">Import VPN profile</string>
     <string name="profile_import_failed">Failed to import VPN profile</string>
     <string name="profile_import_failed_detail">Failed to import VPN profile: %1$s</string>
index 546000a..64bd21b 100644 (file)
         <item>IKEv2 EAP-TLS (证书)</item>
         <item>IKEv2 EAP-TNC (用户名/密码)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
 </resources>
\ No newline at end of file
index 119d462..415212e 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">导入VPN配置</string>
     <string name="profile_import_failed">导入VPN配置失败</string>
     <string name="profile_import_failed_detail">导入VPN配置失败: %1$s</string>
index c7a1963..e4ceccc 100644 (file)
         <item>IKEv2 EAP-TLS (憑證)</item>
         <item>IKEv2 EAP-TNC (用戶名稱/密碼)</item>
     </string-array>
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
 </resources>
\ No newline at end of file
index 2b0ce6b..e1cdf32 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">匯入VPN設定檔</string>
     <string name="profile_import_failed">匯入VPN設定檔失敗</string>
     <string name="profile_import_failed_detail">匯入VPN設定檔失敗: %1$s</string>
index b324b59..be12aab 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2012-2014 Tobias Brunner
-    Hochschule fuer Technik Rapperswil
+    Copyright (C) 2012-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
         <item>IKEv2 EAP-TLS (Certificate)</item>
         <item>IKEv2 EAP-TNC (Username/Password)</item>
     </string-array>
-</resources>
\ No newline at end of file
+
+    <!-- the order here must match the enum entries in VpnProfile.java -->
+    <string-array name="apps_handling">
+        <item>All applications use the VPN</item>
+        <item>Exclude selected applications from the VPN</item>
+        <item>Only selected applications use the VPN</item>
+    </string-array>
+</resources>
index e9a62f0..fa38753 100644 (file)
     <string name="profile_included_subnets_hint">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\")</string>
     <string name="profile_excluded_subnets_label">Excluded subnets</string>
     <string name="profile_excluded_subnets_hint">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\")</string>
+    <string name="profile_select_apps_label">Applications</string>
     <string name="profile_select_apps">Select applications</string>
+    <string name="profile_select_no_apps">No applications selected</string>
+    <string name="profile_select_one_app">One application selected</string>
+    <string name="profile_select_x_apps">%1$d applications selected</string>
     <string name="profile_import">Import VPN profile</string>
     <string name="profile_import_failed">Failed to import VPN profile</string>
     <string name="profile_import_failed_detail">Failed to import VPN profile: %1$s</string>