From c3e7e4fbab09a55a7e433f76f290be75d77bcb28 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Thu, 3 May 2012 22:18:56 +0200 Subject: add delete Profile support add Config Parser begin openvpn config => Vpn Profile Converter --- src/de/blinkt/openvpn/LaunchVPN.java | 252 +++++++++++---------- src/de/blinkt/openvpn/OpenVPN.java | 3 +- src/de/blinkt/openvpn/OpenVpnManagementThread.java | 21 +- src/de/blinkt/openvpn/OpenVpnService.java | 19 +- src/de/blinkt/openvpn/Settings_Obscure.java | 6 +- src/de/blinkt/openvpn/VPNProfileList.java | 191 +++++++++++----- src/de/blinkt/openvpn/VpnProfile.java | 9 +- 7 files changed, 308 insertions(+), 193 deletions(-) (limited to 'src/de') diff --git a/src/de/blinkt/openvpn/LaunchVPN.java b/src/de/blinkt/openvpn/LaunchVPN.java index caeedc09..7a034e4c 100644 --- a/src/de/blinkt/openvpn/LaunchVPN.java +++ b/src/de/blinkt/openvpn/LaunchVPN.java @@ -22,6 +22,7 @@ import java.util.Vector; import android.app.Activity; import android.app.AlertDialog; import android.app.ListActivity; +import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.net.VpnService; @@ -70,27 +71,27 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { private ProfileManager mPM; private VpnProfile mSelectedProfile; - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Resolve the intent + + final Intent intent = getIntent(); + final String action = intent.getAction(); - // Resolve the intent + mPM =ProfileManager.getInstance(); + if(mPM.getNumberOfProfiles() == 0) { + mPM.loadVPNList(this); + } - final Intent intent = getIntent(); - final String action = intent.getAction(); - - mPM =ProfileManager.getInstance(); - if(mPM.getNumberOfProfiles() == 0) { - mPM.loadVPNList(this); - } - - // If the intent is a request to create a shortcut, we'll do that and exit + // If the intent is a request to create a shortcut, we'll do that and exit if(Intent.ACTION_MAIN.equals(action)) { // we got called to be the starting point, most likely a shortcut String shortcutUUID = intent.getStringExtra( EXTRA_KEY); - + VpnProfile profileToConnect = ProfileManager.get(shortcutUUID); if(profileToConnect ==null) { Toast notfound = Toast.makeText(this, R.string.shortcut_profile_notfound, Toast.LENGTH_SHORT); @@ -98,111 +99,111 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { finish(); return; } - + mSelectedProfile = profileToConnect; launchVPN(); - - - + + + } else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { createListView(); } - - - - - - - - } - - private void createListView() { - ListView lv = getListView(); - //lv.setTextFilterEnabled(true); - - Collection vpnlist = mPM.getProfiles(); - - Vector vpnnames=new Vector(); - for (VpnProfile vpnProfile : vpnlist) { - vpnnames.add(vpnProfile.mName); + + + + + + + + } + + private void createListView() { + ListView lv = getListView(); + //lv.setTextFilterEnabled(true); + + Collection vpnlist = mPM.getProfiles(); + + Vector vpnnames=new Vector(); + for (VpnProfile vpnProfile : vpnlist) { + vpnnames.add(vpnProfile.mName); } - - - - ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,vpnnames); - lv.setAdapter(adapter); - - lv.setOnItemClickListener(this); + + + + ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,vpnnames); + lv.setAdapter(adapter); + + lv.setOnItemClickListener(this); } /** - * This function creates a shortcut and returns it to the caller. There are actually two - * intents that you will send back. - * - * The first intent serves as a container for the shortcut and is returned to the launcher by - * setResult(). This intent must contain three fields: - * - *
    - *
  • {@link android.content.Intent#EXTRA_SHORTCUT_INTENT} The shortcut intent.
  • - *
  • {@link android.content.Intent#EXTRA_SHORTCUT_NAME} The text that will be displayed with - * the shortcut.
  • - *
  • {@link android.content.Intent#EXTRA_SHORTCUT_ICON} The shortcut's icon, if provided as a - * bitmap, or {@link android.content.Intent#EXTRA_SHORTCUT_ICON_RESOURCE} if provided as - * a drawable resource.
  • - *
- * - * If you use a simple drawable resource, note that you must wrapper it using - * {@link android.content.Intent.ShortcutIconResource}, as shown below. This is required so - * that the launcher can access resources that are stored in your application's .apk file. If - * you return a bitmap, such as a thumbnail, you can simply put the bitmap into the extras - * bundle using {@link android.content.Intent#EXTRA_SHORTCUT_ICON}. - * - * The shortcut intent can be any intent that you wish the launcher to send, when the user - * clicks on the shortcut. Typically this will be {@link android.content.Intent#ACTION_VIEW} - * with an appropriate Uri for your content, but any Intent will work here as long as it - * triggers the desired action within your Activity. - * @param profile - */ - private void setupShortcut(VpnProfile profile) { - // First, set up the shortcut intent. For this example, we simply create an intent that - // will bring us directly back to this activity. A more typical implementation would use a - // data Uri in order to display a more specific result, or a custom action in order to - // launch a specific operation. - - Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); - shortcutIntent.setClass(this, LaunchVPN.class); - shortcutIntent.putExtra(EXTRA_KEY,profile.getUUID().toString()); - - // Then, set up the container intent (the response to the caller) - - Intent intent = new Intent(); - intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, profile.getName()); - Parcelable iconResource = Intent.ShortcutIconResource.fromContext( - this, R.drawable.icon); - intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); - - // Now, return the result to the launcher - - setResult(RESULT_OK, intent); - } - + * This function creates a shortcut and returns it to the caller. There are actually two + * intents that you will send back. + * + * The first intent serves as a container for the shortcut and is returned to the launcher by + * setResult(). This intent must contain three fields: + * + *
    + *
  • {@link android.content.Intent#EXTRA_SHORTCUT_INTENT} The shortcut intent.
  • + *
  • {@link android.content.Intent#EXTRA_SHORTCUT_NAME} The text that will be displayed with + * the shortcut.
  • + *
  • {@link android.content.Intent#EXTRA_SHORTCUT_ICON} The shortcut's icon, if provided as a + * bitmap, or {@link android.content.Intent#EXTRA_SHORTCUT_ICON_RESOURCE} if provided as + * a drawable resource.
  • + *
+ * + * If you use a simple drawable resource, note that you must wrapper it using + * {@link android.content.Intent.ShortcutIconResource}, as shown below. This is required so + * that the launcher can access resources that are stored in your application's .apk file. If + * you return a bitmap, such as a thumbnail, you can simply put the bitmap into the extras + * bundle using {@link android.content.Intent#EXTRA_SHORTCUT_ICON}. + * + * The shortcut intent can be any intent that you wish the launcher to send, when the user + * clicks on the shortcut. Typically this will be {@link android.content.Intent#ACTION_VIEW} + * with an appropriate Uri for your content, but any Intent will work here as long as it + * triggers the desired action within your Activity. + * @param profile + */ + private void setupShortcut(VpnProfile profile) { + // First, set up the shortcut intent. For this example, we simply create an intent that + // will bring us directly back to this activity. A more typical implementation would use a + // data Uri in order to display a more specific result, or a custom action in order to + // launch a specific operation. + + Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); + shortcutIntent.setClass(this, LaunchVPN.class); + shortcutIntent.putExtra(EXTRA_KEY,profile.getUUID().toString()); + + // Then, set up the container intent (the response to the caller) + + Intent intent = new Intent(); + intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); + intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, profile.getName()); + Parcelable iconResource = Intent.ShortcutIconResource.fromContext( + this, R.drawable.icon); + intent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, iconResource); + + // Now, return the result to the launcher + + setResult(RESULT_OK, intent); + } + @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - String profilename = ((TextView) view).getText().toString(); - - VpnProfile profile = mPM.getProfileByName(profilename); - - // if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { - setupShortcut(profile); - finish(); - return; - // } - + String profilename = ((TextView) view).getText().toString(); + + VpnProfile profile = mPM.getProfileByName(profilename); + + // if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { + setupShortcut(profile); + finish(); + return; + // } + } - + private void askForPW(final String type) { final EditText entry = new EditText(this); @@ -243,16 +244,21 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - - if(requestCode==START_VPN_PROFILE && resultCode == Activity.RESULT_OK) { - - if(mSelectedProfile.needUserPWInput()!=null) { - askForPW(mSelectedProfile.needUserPWInput()); - } else { - new startOpenVpnThread().start(); - } - } + if(requestCode==START_VPN_PROFILE) { + if(resultCode == Activity.RESULT_OK) { + + if(mSelectedProfile.needUserPWInput()!=null) { + askForPW(mSelectedProfile.needUserPWInput()); + } else { + new startOpenVpnThread().start(); + } + + } else if (resultCode == Activity.RESULT_CANCELED) { + // User does want us to start, so we just vanish + finish(); + } + } } void showConfigErrorDialog(int vpnok) { @@ -262,10 +268,10 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { d.setPositiveButton(android.R.string.ok, null); d.show(); } - + void launchVPN () { - - + + int vpnok = mSelectedProfile.checkProfile(); if(vpnok!= R.string.no_error_found) { showConfigErrorDialog(vpnok); @@ -276,13 +282,17 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { if (intent != null) { // Start the query - startActivityForResult(intent, START_VPN_PROFILE); + try { + startActivityForResult(intent, START_VPN_PROFILE); + } catch (ActivityNotFoundException ane){ + Toast.makeText(this, "Your image does not support the VPNService API,sorry :(", Toast.LENGTH_LONG); + } } else { onActivityResult(START_VPN_PROFILE, Activity.RESULT_OK, null); } - + } - + private class startOpenVpnThread extends Thread { @Override @@ -305,5 +315,5 @@ public class LaunchVPN extends ListActivity implements OnItemClickListener { } } - + } diff --git a/src/de/blinkt/openvpn/OpenVPN.java b/src/de/blinkt/openvpn/OpenVPN.java index ea6c4926..86bd3665 100644 --- a/src/de/blinkt/openvpn/OpenVPN.java +++ b/src/de/blinkt/openvpn/OpenVPN.java @@ -100,8 +100,7 @@ public class OpenVPN { public static int openTunDevice() { Log.d(TAG,"Opening tun device"); - ParcelFileDescriptor pfd = mOpenVpnService.openTun(); - return pfd.detachFd(); + return mOpenVpnService.openTun(); } //! Dummy method being called to force loading of JNI Libraries public static void foo() { } diff --git a/src/de/blinkt/openvpn/OpenVpnManagementThread.java b/src/de/blinkt/openvpn/OpenVpnManagementThread.java index 02e5dc46..19f8d7e5 100644 --- a/src/de/blinkt/openvpn/OpenVpnManagementThread.java +++ b/src/de/blinkt/openvpn/OpenVpnManagementThread.java @@ -1,10 +1,13 @@ package de.blinkt.openvpn; +import java.io.FileDescriptor; import java.io.IOException; import java.io.InputStream; +import java.net.DatagramSocket; import java.util.Vector; import android.net.LocalSocket; +import android.os.ParcelFileDescriptor; import android.util.Log; public class OpenVpnManagementThread implements Runnable { @@ -12,11 +15,14 @@ public class OpenVpnManagementThread implements Runnable { private static final String TAG = "openvpn"; private LocalSocket mSocket; private VpnProfile mProfile; + private OpenVpnService mOpenVPNService; + private static Vector active=new Vector(); - public OpenVpnManagementThread(VpnProfile profile, LocalSocket mgmtsocket) { + public OpenVpnManagementThread(VpnProfile profile, LocalSocket mgmtsocket, OpenVpnService openVpnService) { mProfile = profile; mSocket = mgmtsocket; + mOpenVPNService = openVpnService; } @@ -102,6 +108,8 @@ private static Vector active=new Vector active=new Vector mDnslist=new Vector(); private VpnProfile mProfile; @@ -159,12 +157,12 @@ public class OpenVpnService extends VpnService implements Handler.Callback { if(mgmtsocket!=null) { // start a Thread that handles incoming messages of the managment socket - mSocketManager = new OpenVpnManagementThread(mProfile,mgmtsocket); + mSocketManager = new OpenVpnManagementThread(mProfile,mgmtsocket,this); mSocketManagerThread = new Thread(mSocketManager,"OpenVPNMgmtThread"); mSocketManagerThread.start(); } - return START_STICKY; + return START_NOT_STICKY; } @@ -191,7 +189,7 @@ public class OpenVpnService extends VpnService implements Handler.Callback { - public ParcelFileDescriptor openTun() { + public int openTun() { Builder builder = new Builder(); builder.addAddress(mLocalIP.mIp, mLocalIP.len); @@ -223,8 +221,15 @@ public class OpenVpnService extends VpnService implements Handler.Callback { Intent intent = new Intent(getBaseContext(),LogWindow.class); PendingIntent startLW = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0); builder.setConfigureIntent(startLW); - mInterface = builder.establish(); - return mInterface; + try { + ParcelFileDescriptor pfd = builder.establish(); + return pfd.detachFd(); + } catch (Exception e) { + OpenVPN.logMessage(0, "", getString(R.string.tun_open_error)); + OpenVPN.logMessage(0, "", getString(R.string.error) + e.getLocalizedMessage()); + OpenVPN.logMessage(0, "", getString(R.string.tun_error_helpful)); + return -1; + } } diff --git a/src/de/blinkt/openvpn/Settings_Obscure.java b/src/de/blinkt/openvpn/Settings_Obscure.java index d7bce9ad..65d1c400 100644 --- a/src/de/blinkt/openvpn/Settings_Obscure.java +++ b/src/de/blinkt/openvpn/Settings_Obscure.java @@ -63,12 +63,14 @@ public class Settings_Obscure extends PreferenceFragment implements OnPreference mProfile.mVerb = mLogverbosity.getValue(); } + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if(preference==mLogverbosity) { mLogverbosity.setDefaultValue(newValue); - // Does not refresh otherwise - mLogverbosity.setSummary("%s"); + //This is idiotic. + int i =Integer.parseInt((String) newValue); + mLogverbosity.setSummary(mLogverbosity.getEntries()[i]); } return true; diff --git a/src/de/blinkt/openvpn/VPNProfileList.java b/src/de/blinkt/openvpn/VPNProfileList.java index 42c881a8..8565f460 100644 --- a/src/de/blinkt/openvpn/VPNProfileList.java +++ b/src/de/blinkt/openvpn/VPNProfileList.java @@ -2,38 +2,90 @@ package de.blinkt.openvpn; import android.app.Activity; import android.app.AlertDialog; +import android.app.ListFragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.preference.PreferenceScreen; +import android.view.ActionMode; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.AdapterView.OnItemLongClickListener; +import android.widget.ArrayAdapter; import android.widget.EditText; +import android.widget.ListView; import android.widget.Toast; -import de.blinkt.openvpn.VPNConfigPreference.VpnPreferencesClickListener; -public class VPNProfileList extends PreferenceFragment implements VpnPreferencesClickListener { +public class VPNProfileList extends ListFragment { private static final int MENU_ADD_PROFILE = Menu.FIRST; private static final int START_VPN_CONFIG = 92; - private VpnProfile mSelectedVPN; + private ArrayAdapter mArrayadapter; + protected Object mActionMode; + + protected VpnProfile mEditProfile=null; + + + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.vpn_profile_list); setHasOptionsMenu(true); - refreshList(); // Debug load JNI OpenVPN.foo(); } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + ListView lv = getListView(); + lv.setOnItemLongClickListener(new OnItemLongClickListener() { + + // Called when the user long-clicks on someView + + @Override + public boolean onItemLongClick(AdapterView parent, View view, + int position, long id) { + if (mActionMode != null) { + return false; + } + + // Start the CAB using the ActionMode.Callback defined above + mActionMode = getActivity().startActionMode(mActionModeCallback); + mEditProfile =(VpnProfile) getListAdapter().getItem(position); + + //getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); + //getListView().setSelection(position); + return true; + } + }); + + lv.setOnItemClickListener(new OnItemClickListener() { + + @Override + public void onItemClick(AdapterView parent, View view, + int position, long id) { + VpnProfile profile =(VpnProfile) getListAdapter().getItem(position); + startVPN(profile); + } + }); + + + if(getPM().getNumberOfProfiles() == 0) { + getPM().loadVPNList(getActivity()); + } + + mArrayadapter = new ArrayAdapter(getActivity(),android.R.layout.simple_list_item_activated_1); + mArrayadapter.addAll(getPM().getProfiles()); + setListAdapter(mArrayadapter); + } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { @@ -56,7 +108,27 @@ public class VPNProfileList extends PreferenceFragment implements VpnPreference } } + private void askProfileRemoval() { + AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity()); + dialog.setTitle("Confirm deletion"); + dialog.setMessage(getString(R.string.remove_vpn_query, mEditProfile.mName)); + + dialog.setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + removeProfile(mEditProfile); + } + + }); + dialog.setNegativeButton(android.R.string.no,null); + dialog.create().show(); + } + protected void removeProfile(VpnProfile profile) { + mArrayadapter.remove(profile); + + } private void onAddProfileClicked() { Context context = getActivity(); @@ -78,7 +150,6 @@ public class VPNProfileList extends PreferenceFragment implements VpnPreference if (getPM().getProfileByName(name)==null) { VpnProfile profile = new VpnProfile(name); addProfile(profile); - refreshList(); } else { Toast.makeText(getActivity(), R.string.duplicate_profile_name, Toast.LENGTH_LONG).show(); } @@ -102,58 +173,17 @@ public class VPNProfileList extends PreferenceFragment implements VpnPreference getPM().addProfile(profile); getPM().saveProfileList(getActivity()); getPM().saveProfile(getActivity(),profile); - + mArrayadapter.add(profile); } - public void refreshList() { - PreferenceScreen plist = getPreferenceScreen(); - if (plist != null) { - plist.removeAll(); - getPM().loadVPNList(getActivity()); - - for (VpnProfile vpnprofile: getPM().getProfiles()) { - - String profileuuid = vpnprofile.getUUID().toString(); - - - VPNConfigPreference vpref = new VPNConfigPreference(this); - vpref.setKey(profileuuid); - vpref.setTitle(vpnprofile.getName()); - vpref.setPersistent(false); - vpref.setOnQuickSettingsClickListener(this); - plist.addPreference(vpref); - } - - - } - } - - - private ProfileManager getPM() { return ProfileManager.getInstance(); } - @Override - public boolean onQuickSettingsClick(Preference preference) { - String key = preference.getKey(); - - VpnProfile vprofile = ProfileManager.get(key); - - Intent vprefintent = new Intent(getActivity(),VPNPreferences.class) - .putExtra(getActivity().getPackageName() + ".profileUUID", vprofile.getUUID().toString()); - - startActivityForResult(vprefintent,START_VPN_CONFIG); - return true; - } - - - - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { @@ -164,26 +194,69 @@ public class VPNProfileList extends PreferenceFragment implements VpnPreference VpnProfile profile = ProfileManager.get(configuredVPN); getPM().saveProfile(getActivity(), profile); // Name could be modified - refreshList(); + } } + private void editVPN(VpnProfile profile) { - @Override - public void onStartVPNClick(VPNConfigPreference preference) { - getPM(); - // Query the System for permission - mSelectedVPN = ProfileManager.get(preference.getKey()); + Intent vprefintent = new Intent(getActivity(),VPNPreferences.class) + .putExtra(getActivity().getPackageName() + ".profileUUID", profile.getUUID().toString()); - getPM().saveProfile(getActivity(), mSelectedVPN); + startActivityForResult(vprefintent,START_VPN_CONFIG); + } + + private void startVPN(VpnProfile profile) { + + getPM().saveProfile(getActivity(), profile); Intent intent = new Intent(getActivity(),LaunchVPN.class); - intent.putExtra(LaunchVPN.EXTRA_KEY, mSelectedVPN.getUUID().toString()); + intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); intent.setAction(Intent.ACTION_MAIN); startActivity(intent); getActivity().finish(); } + + private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.vpn_context, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return false;// Return false if nothing is done + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + switch (item.getItemId()) { + case R.id.remove_vpn: + askProfileRemoval(); + mode.finish(); // Action picked, so close the CAB + return true; + case R.id.connect_vpn: + startVPN(mEditProfile); + mode.finish(); + return true; + case R.id.edit_vpn: + editVPN(mEditProfile); + mode.finish(); + return true; + default: + return false; + } } + + @Override + public void onDestroyActionMode(ActionMode mode) { + mActionMode = null; + getListView().setChoiceMode(ListView.CHOICE_MODE_NONE); + } + }; + } diff --git a/src/de/blinkt/openvpn/VpnProfile.java b/src/de/blinkt/openvpn/VpnProfile.java index 04b5927f..ba3d4535 100644 --- a/src/de/blinkt/openvpn/VpnProfile.java +++ b/src/de/blinkt/openvpn/VpnProfile.java @@ -422,7 +422,7 @@ public class VpnProfile implements Serializable{ return intent; } - public String getTemporaryPKCS12Password() { + private String getTemporaryPKCS12Password() { if(mTempPKCS12Password!=null) return mTempPKCS12Password; @@ -550,6 +550,13 @@ public class VpnProfile implements Serializable{ return mPassword; } } + + + // Used by the Array Adapter + @Override + public String toString() { + return mName; + } -- cgit v1.2.3