diff options
14 files changed, 730 insertions, 723 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/activities/ConfigConverter.java b/main/src/main/java/de/blinkt/openvpn/activities/ConfigConverter.java index 262c6e9b..cace20ff 100644 --- a/main/src/main/java/de/blinkt/openvpn/activities/ConfigConverter.java +++ b/main/src/main/java/de/blinkt/openvpn/activities/ConfigConverter.java @@ -25,8 +25,10 @@ import android.view.MenuItem;  import android.view.View;  import android.view.ViewGroup;  import android.widget.CheckBox; +import android.widget.ImageButton;  import android.widget.LinearLayout;  import android.widget.TextView; +import android.widget.Toast;  import junit.framework.Assert; @@ -54,7 +56,7 @@ import de.blinkt.openvpn.views.FileSelectLayout;  import static de.blinkt.openvpn.views.FileSelectLayout.FileSelectCallback; -public class ConfigConverter extends Activity implements FileSelectCallback { +public class ConfigConverter extends Activity implements FileSelectCallback, View.OnClickListener {      public static final String IMPORT_PROFILE = "de.blinkt.openvpn.IMPORT_PROFILE";      private static final int RESULT_INSTALLPKCS12 = 7; @@ -74,28 +76,39 @@ public class ConfigConverter extends Activity implements FileSelectCallback {      private String mCrlFileName;      @Override +    public void onClick(View v) { +        if (v.getId() == R.id.fab_save) +            userActionSaveProfile(); +    } + +    @Override      public boolean onOptionsItemSelected(MenuItem item) {          if (item.getItemId() == R.id.cancel) {              setResult(Activity.RESULT_CANCELED);              finish();          } else if (item.getItemId() == R.id.ok) { -            if (mResult == null) { -                log("Importing the config had error, cannot save it"); -                return true; -            } +            return userActionSaveProfile(); +        } -            Intent in = installPKCS12(); +        return super.onOptionsItemSelected(item); -            if (in != null) -                startActivityForResult(in, RESULT_INSTALLPKCS12); -            else -                saveProfile(); +    } +    private boolean userActionSaveProfile() { +        if (mResult == null) { +            log(R.string.import_config_error); +            Toast.makeText(this, R.string.import_config_error, Toast.LENGTH_LONG).show();              return true;          } -        return super.onOptionsItemSelected(item); +        Intent in = installPKCS12(); + +        if (in != null) +            startActivityForResult(in, RESULT_INSTALLPKCS12); +        else +            saveProfile(); +        return true;      }      @Override @@ -511,6 +524,10 @@ public class ConfigConverter extends Activity implements FileSelectCallback {          setContentView(R.layout.config_converter);          super.onCreate(savedInstanceState); +        ImageButton fab_button = (ImageButton) findViewById(R.id.fab_save); +        if(fab_button!=null) +            fab_button.setOnClickListener(this); +          if (savedInstanceState != null && savedInstanceState.containsKey(VPNPROFILE)) {              mResult = (VpnProfile) savedInstanceState.getSerializable(VPNPROFILE);              mAliasName = savedInstanceState.getString("mAliasName"); @@ -662,5 +679,4 @@ public class ConfigConverter extends Activity implements FileSelectCallback {          log(getString(ressourceId, formatArgs));      } -  } diff --git a/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java b/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java index 1554a557..4aa7e120 100644 --- a/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java +++ b/main/src/main/java/de/blinkt/openvpn/activities/MainActivity.java @@ -9,6 +9,7 @@ import android.annotation.TargetApi;  import android.app.ActionBar;  import android.app.Activity;  import android.content.Intent; +import android.content.pm.PackageManager;  import android.os.Build;  import android.support.v4n.view.ViewPager;  import android.view.Menu; @@ -18,6 +19,7 @@ import de.blinkt.openvpn.R;  import de.blinkt.openvpn.fragments.AboutFragment;  import de.blinkt.openvpn.fragments.FaqFragment;  import de.blinkt.openvpn.fragments.GeneralSettings; +import de.blinkt.openvpn.fragments.LogFragment;  import de.blinkt.openvpn.fragments.SendDumpFragment;  import de.blinkt.openvpn.fragments.VPNProfileList;  import de.blinkt.openvpn.views.ScreenSlidePagerAdapter; @@ -57,7 +59,8 @@ public class MainActivity extends Activity {              mPagerAdapter.addTab(R.string.crashdump, SendDumpFragment.class);          } -        //mPagerAdapter.addTab(R.string.openvpn_log, LogFragment.class); +        if (isDirectToTV()) +            mPagerAdapter.addTab(R.string.openvpn_log, LogFragment.class);          mPagerAdapter.addTab(R.string.about, AboutFragment.class);          mPager.setAdapter(mPagerAdapter); @@ -66,6 +69,11 @@ public class MainActivity extends Activity {          tabs.setViewPager(mPager);  	} +    private boolean isDirectToTV() { +        return(getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) +                || getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)); +    } +      @TargetApi(Build.VERSION_CODES.LOLLIPOP)      private void disableToolbarElevation() {          ActionBar toolbar = getActionBar(); diff --git a/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java b/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java index 89a98241..2420194d 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java +++ b/main/src/main/java/de/blinkt/openvpn/core/ICSOpenVPNApplication.java @@ -38,5 +38,4 @@ public class ICSOpenVPNApplication extends Application {              //ACRA.init(this);          }      } -  } diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java index 113142b7..9716f020 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -16,7 +16,6 @@ import android.content.IntentFilter;  import android.content.SharedPreferences;  import android.content.pm.PackageManager;  import android.net.ConnectivityManager; -import android.net.NetworkRequest;  import android.net.VpnService;  import android.os.Binder;  import android.os.Build; @@ -62,7 +61,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      private static final String RESUME_VPN = "de.blinkt.openvpn.RESUME_VPN";      private static final int OPENVPN_STATUS = 1;      private static boolean mNotificationAlwaysVisible = false; -    private final Vector<String> mDnslist = new Vector<String>(); +    private final Vector<String> mDnslist = new Vector<>();      private final NetworkSpace mRoutes = new NetworkSpace();      private final NetworkSpace mRoutesv6 = new NetworkSpace();      private final IBinder mBinder = new LocalBinder(); @@ -81,7 +80,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac      private String mLastTunCfg;      private String mRemoteGW;      private final Object mProcessLock = new Object(); -    private LollipopDeviceStateListener mLollipopDeviceStateListener;      // From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java      public static String humanReadableByteCount(long bytes, boolean mbit) { @@ -235,13 +233,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              //ignore exception -        } catch (NoSuchMethodException nsm) { -            VpnStatus.logException(nsm); -        } catch (IllegalArgumentException e) { -            VpnStatus.logException(e); -        } catch (IllegalAccessException e) { -            VpnStatus.logException(e); -        } catch (InvocationTargetException e) { +        } catch (NoSuchMethodException | IllegalArgumentException | +                InvocationTargetException | IllegalAccessException e) {              VpnStatus.logException(e);          } @@ -325,6 +318,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          /* The intent is null when the service has been restarted */          if (intent == null) {              mProfile = ProfileManager.getLastConnectedProfile(this, false); +            VpnStatus.logInfo(R.string.service_restarted);              /* Got no profile, just stop */              if (mProfile == null) { @@ -412,7 +406,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          } else { -            HashMap<String, String> env = new HashMap<String, String>(); +            HashMap<String, String> env = new HashMap<>();              processThread = new OpenVPNThread(this, argv, env, nativeLibraryDirectory);          } @@ -428,7 +422,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          ProfileManager.setConnectedVpnProfile(this, mProfile);          /* TODO: At the moment we have no way to handle asynchronous PW input -         * Fixing will also allow to handle challenge/responsee authentication */ +         * Fixing will also allow to handle challenge/response authentication */          if (mProfile.needUserPWInput(true) != 0)              return START_NOT_STICKY; @@ -439,17 +433,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          try {              Class cl = Class.forName("de.blinkt.openvpn.core.OpenVPNThreadv3");              return (OpenVPNManagement) cl.getConstructor(OpenVPNService.class, VpnProfile.class).newInstance(this, mProfile); -        } catch (IllegalArgumentException e) { -            e.printStackTrace(); -        } catch (InstantiationException e) { -            e.printStackTrace(); -        } catch (IllegalAccessException e) { -            e.printStackTrace(); -        } catch (InvocationTargetException e) { -            e.printStackTrace(); -        } catch (NoSuchMethodException e) { -            e.printStackTrace(); -        } catch (ClassNotFoundException e) { +        } catch (IllegalArgumentException | InstantiationException | InvocationTargetException | +                NoSuchMethodException | ClassNotFoundException | IllegalAccessException e ) {              e.printStackTrace();          }          return null; @@ -572,7 +557,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              }          } -        if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist != null && mDnslist.size() >= 1) { +        if ("samsung".equals(Build.BRAND) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mDnslist.size() >= 1) {              // Check if the first DNS Server is in the VPN range              try {                  ipAddress dnsServer = new ipAddress(new CIDRIP(mDnslist.get(0), 32), true); @@ -652,22 +637,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac          builder.allowFamily(OsConstants.AF_INET6);      } -    @TargetApi(Build.VERSION_CODES.LOLLIPOP) -    void removeLollipopCMListener() { -        ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE); -        cm.unregisterNetworkCallback(mLollipopDeviceStateListener); -        mLollipopDeviceStateListener = null; -    } - -    @TargetApi(Build.VERSION_CODES.LOLLIPOP) -    void addLollipopCMListener() { -        ConnectivityManager cm = (ConnectivityManager) getBaseContext().getSystemService(CONNECTIVITY_SERVICE); -        NetworkRequest.Builder nrb = new NetworkRequest.Builder(); - -        mLollipopDeviceStateListener = new LollipopDeviceStateListener(); -        cm.registerNetworkCallback(nrb.build(), mLollipopDeviceStateListener); -    } -      private void addLocalNetworksToRoutes() {          // Add local network interfaces @@ -879,8 +848,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac              // CONNECTED              // Does not work :(              String msg = getString(resid); -            String ticker = msg; -            showNotification(msg + " " + logmessage, ticker, lowpriority, 0, level); +            showNotification(msg + " " + logmessage, msg, lowpriority, 0, level);          }      } diff --git a/main/src/main/java/de/blinkt/openvpn/core/ProfileManager.java b/main/src/main/java/de/blinkt/openvpn/core/ProfileManager.java index e4687c3c..086cdb44 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/ProfileManager.java +++ b/main/src/main/java/de/blinkt/openvpn/core/ProfileManager.java @@ -5,11 +5,15 @@  package de.blinkt.openvpn.core; -import java.io.FileNotFoundException; +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.preference.PreferenceManager; +  import java.io.IOException;  import java.io.ObjectInputStream;  import java.io.ObjectOutputStream; -import java.io.StreamCorruptedException;  import java.util.Collection;  import java.util.HashMap;  import java.util.HashSet; @@ -17,207 +21,173 @@ import java.util.Set;  import de.blinkt.openvpn.VpnProfile; -import android.app.Activity; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; -import android.preference.PreferenceManager; -  public class ProfileManager { -	private static final String PREFS_NAME =  "VPNList"; +    private static final String PREFS_NAME = "VPNList"; +    private static final String LAST_CONNECTED_PROFILE = "lastConnectedProfile"; +    private static ProfileManager instance; +    private static VpnProfile mLastConnectedVpn = null; +    private HashMap<String, VpnProfile> profiles = new HashMap<>(); +    private static VpnProfile tmpprofile = null; -	private static final String LAST_CONNECTED_PROFILE = "lastConnectedProfile"; +    private static VpnProfile get(String key) { +        if (tmpprofile != null && tmpprofile.getUUIDString().equals(key)) +            return tmpprofile; +        if (instance == null) +            return null; +        return instance.profiles.get(key); + +    } -	private static ProfileManager instance; +    private ProfileManager() { +    } +    private static void checkInstance(Context context) { +        if (instance == null) { +            instance = new ProfileManager(); +            instance.loadVPNList(context); +        } +    } -	private static VpnProfile mLastConnectedVpn=null; -	private HashMap<String,VpnProfile> profiles=new HashMap<String, VpnProfile>(); -	private static VpnProfile tmpprofile=null; +    synchronized public static ProfileManager getInstance(Context context) { +        checkInstance(context); +        return instance; +    } +    public static void setConntectedVpnProfileDisconnected(Context c) { +        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); +        Editor prefsedit = prefs.edit(); +        prefsedit.putString(LAST_CONNECTED_PROFILE, null); +        prefsedit.apply(); -	private static VpnProfile get(String key) { -		if (tmpprofile!=null && tmpprofile.getUUIDString().equals(key)) -			return tmpprofile; -			 -		if(instance==null) -			return null; -		return instance.profiles.get(key); -		 -	} +    } +    public static void setConnectedVpnProfile(Context c, VpnProfile connectedrofile) { +        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); +        Editor prefsedit = prefs.edit(); -	 -	private ProfileManager() { } -	 -	private static void checkInstance(Context context) { -		if(instance == null) { -			instance = new ProfileManager(); -			instance.loadVPNList(context); -		} -	} +        prefsedit.putString(LAST_CONNECTED_PROFILE, connectedrofile.getUUIDString()); +        prefsedit.apply(); +        mLastConnectedVpn = connectedrofile; -	synchronized public static ProfileManager getInstance(Context context) { -		checkInstance(context); -		return instance; -	} -	 -	public static void setConntectedVpnProfileDisconnected(Context c) { -		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); -		Editor prefsedit = prefs.edit(); -		prefsedit.putString(LAST_CONNECTED_PROFILE, null); -		prefsedit.apply(); -		 -	} +    } -	public static void setConnectedVpnProfile(Context c, VpnProfile connectedrofile) { -		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); -		Editor prefsedit = prefs.edit(); -		 -		prefsedit.putString(LAST_CONNECTED_PROFILE, connectedrofile.getUUIDString()); -		prefsedit.apply(); -		mLastConnectedVpn=connectedrofile; -		 -	} -	 -	public static VpnProfile getLastConnectedProfile(Context c, boolean onBoot) { -		SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); +    public static VpnProfile getLastConnectedProfile(Context c, boolean onBoot) { +        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(c); -		boolean useStartOnBoot = prefs.getBoolean("restartvpnonboot", false); +        boolean useStartOnBoot = prefs.getBoolean("restartvpnonboot", false);          if (onBoot && !useStartOnBoot)              return null; -		 -		String lastConnectedProfile = prefs.getString(LAST_CONNECTED_PROFILE, null); -		if(lastConnectedProfile!=null) -			return get(c, lastConnectedProfile); -		else  -			return null; -	} -	 -	 -	 -	 -	public Collection<VpnProfile> getProfiles() { -		return profiles.values(); -	} -	 -	public VpnProfile getProfileByName(String name) { -		for (VpnProfile vpnp : profiles.values()) { -			if(vpnp.getName().equals(name)) { -				return vpnp; -			} -		} -		return null;			 -	} - -	public void saveProfileList(Context context) { -		SharedPreferences sharedprefs = context.getSharedPreferences(PREFS_NAME,Activity.MODE_PRIVATE); -		Editor editor = sharedprefs.edit(); -		editor.putStringSet("vpnlist", profiles.keySet()); -		 -		// For reasing I do not understand at all  -		// Android saves my prefs file only one time  -		// if I remove the debug code below :( -		int counter = sharedprefs.getInt("counter", 0); -		editor.putInt("counter", counter+1); -		editor.apply(); - -	} - -	public void addProfile(VpnProfile profile) { -		profiles.put(profile.getUUID().toString(),profile); -		 -	} -	 -	public static void setTemporaryProfile(VpnProfile tmp) { -		ProfileManager.tmpprofile = tmp; -	} -	 -	 -	public void saveProfile(Context context, VpnProfile profile) { -		ObjectOutputStream vpnfile; -		try { -			vpnfile = new ObjectOutputStream(context.openFileOutput((profile.getUUID().toString() + ".vp"),Activity.MODE_PRIVATE)); - -			vpnfile.writeObject(profile); -			vpnfile.flush(); -			vpnfile.close(); -		} catch (IOException e) { -            VpnStatus.logException("saving VPN profile", e); -			throw new RuntimeException(e); -		} -	} -	 -	 -	private void loadVPNList(Context context) { -		profiles = new HashMap<String, VpnProfile>(); -		SharedPreferences listpref = context.getSharedPreferences(PREFS_NAME,Activity.MODE_PRIVATE); -		Set<String> vlist = listpref.getStringSet("vpnlist", null); -		Exception exp =null; -		if(vlist==null){ -			vlist = new HashSet<String>(); -		} - -		for (String vpnentry : vlist) { -			try { -				ObjectInputStream vpnfile = new ObjectInputStream(context.openFileInput(vpnentry + ".vp")); -				VpnProfile vp = ((VpnProfile) vpnfile.readObject()); - -				// Sanity check  -				if(vp==null || vp.mName==null || vp.getUUID()==null) -					continue; -                vp.upgradeProfile(); -				profiles.put(vp.getUUID().toString(), vp); +        String lastConnectedProfile = prefs.getString(LAST_CONNECTED_PROFILE, null); +        if (lastConnectedProfile != null) +            return get(c, lastConnectedProfile); +        else +            return null; +    } + + +    public Collection<VpnProfile> getProfiles() { +        return profiles.values(); +    } + +    public VpnProfile getProfileByName(String name) { +        for (VpnProfile vpnp : profiles.values()) { +            if (vpnp.getName().equals(name)) { +                return vpnp; +            } +        } +        return null; +    } + +    public void saveProfileList(Context context) { +        SharedPreferences sharedprefs = context.getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE); +        Editor editor = sharedprefs.edit(); +        editor.putStringSet("vpnlist", profiles.keySet()); + +        // For reasing I do not understand at all +        // Android saves my prefs file only one time +        // if I remove the debug code below :( +        int counter = sharedprefs.getInt("counter", 0); +        editor.putInt("counter", counter + 1); +        editor.apply(); + +    } + +    public void addProfile(VpnProfile profile) { +        profiles.put(profile.getUUID().toString(), profile); -			} catch (StreamCorruptedException e) { -				exp=e; -			} catch (FileNotFoundException e) { -				exp=e; -			} catch (IOException e) { -				exp=e; -			} catch (ClassNotFoundException e) {  -				exp=e; -			} -			if(exp!=null) { -			    VpnStatus.logException("Loading VPN List",exp); -			} -		} -	} +    } -	public int getNumberOfProfiles() { -		return profiles.size(); -	} +    public static void setTemporaryProfile(VpnProfile tmp) { +        ProfileManager.tmpprofile = tmp; +    } +    public void saveProfile(Context context, VpnProfile profile) { +        ObjectOutputStream vpnfile; +        try { +            vpnfile = new ObjectOutputStream(context.openFileOutput((profile.getUUID().toString() + ".vp"), Activity.MODE_PRIVATE)); -	public void removeProfile(Context context,VpnProfile profile) { -		String vpnentry = profile.getUUID().toString(); -		profiles.remove(vpnentry); -		saveProfileList(context); -		context.deleteFile(vpnentry + ".vp"); -		if(mLastConnectedVpn==profile) -			mLastConnectedVpn=null; -		 -	} +            vpnfile.writeObject(profile); +            vpnfile.flush(); +            vpnfile.close(); +        } catch (IOException e) { +            VpnStatus.logException("saving VPN profile", e); +            throw new RuntimeException(e); +        } +    } + + +    private void loadVPNList(Context context) { +        profiles = new HashMap<>(); +        SharedPreferences listpref = context.getSharedPreferences(PREFS_NAME, Activity.MODE_PRIVATE); +        Set<String> vlist = listpref.getStringSet("vpnlist", null); +        if (vlist == null) { +            vlist = new HashSet<>(); +        } + +        for (String vpnentry : vlist) { +            try { +                ObjectInputStream vpnfile = new ObjectInputStream(context.openFileInput(vpnentry + ".vp")); +                VpnProfile vp = ((VpnProfile) vpnfile.readObject()); + +                // Sanity check +                if (vp == null || vp.mName == null || vp.getUUID() == null) +                    continue; + +                vp.upgradeProfile(); +                profiles.put(vp.getUUID().toString(), vp); +            } catch (IOException | ClassNotFoundException e) { +                VpnStatus.logException("Loading VPN List", e); +            } +        } +    } -	public static VpnProfile get(Context context, String profileUUID) { -		checkInstance(context); -		return get(profileUUID); -	} +    public void removeProfile(Context context, VpnProfile profile) { +        String vpnentry = profile.getUUID().toString(); +        profiles.remove(vpnentry); +        saveProfileList(context); +        context.deleteFile(vpnentry + ".vp"); +        if (mLastConnectedVpn == profile) +            mLastConnectedVpn = null; +    } +    public static VpnProfile get(Context context, String profileUUID) { +        checkInstance(context); +        return get(profileUUID); +    } -	public static VpnProfile getLastConnectedVpn() { -		return mLastConnectedVpn; -	} +    public static VpnProfile getLastConnectedVpn() { +        return mLastConnectedVpn; +    }  } diff --git a/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java b/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java index ea114d7e..d485a687 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java @@ -8,7 +8,6 @@ package de.blinkt.openvpn.core;  import android.annotation.TargetApi;  import android.content.Context;  import android.content.Intent; -import android.content.pm.ApplicationInfo;  import android.os.Build;  import java.io.File; @@ -67,14 +66,14 @@ public class VPNLaunchHelper {      public static String[] buildOpenvpnArgv(Context c) { -        Vector<String> args = new Vector<String>(); +        Vector<String> args = new Vector<>();          // Add fixed paramenters          //args.add("/data/data/de.blinkt.openvpn/lib/openvpn");          args.add(writeMiniVPN(c));          args.add("--config"); -        args.add(c.getCacheDir().getAbsolutePath() + "/" + OVPNCONFIGFILE); +        args.add(getConfigFilePath(c));          return args.toArray(new String[args.size()]);      } diff --git a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java index 8ab65424..9d19a0e8 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -6,6 +6,7 @@  package de.blinkt.openvpn.core;  import android.annotation.SuppressLint; +import android.app.Activity;  import android.content.Context;  import android.content.pm.PackageInfo;  import android.content.pm.PackageManager; @@ -16,12 +17,8 @@ import android.os.Parcel;  import android.os.Parcelable;  import android.text.TextUtils; -import org.w3c.dom.Text; - -import de.blinkt.openvpn.R; -  import java.io.ByteArrayInputStream; -import java.io.FileNotFoundException; +import java.io.File;  import java.io.PrintWriter;  import java.io.StringWriter;  import java.security.MessageDigest; @@ -36,28 +33,30 @@ import java.util.Locale;  import java.util.UnknownFormatConversionException;  import java.util.Vector; +import de.blinkt.openvpn.R; +  public class VpnStatus { -	public static LinkedList<LogItem> logbuffer; +    public static LinkedList<LogItem> logbuffer; -	private static Vector<LogListener> logListener; -	private static Vector<StateListener> stateListener; -	private static Vector<ByteCountListener> byteCountListener; +    private static Vector<LogListener> logListener; +    private static Vector<StateListener> stateListener; +    private static Vector<ByteCountListener> byteCountListener; -	private static String mLaststatemsg=""; +    private static String mLaststatemsg = ""; -	private static String mLaststate = "NOPROCESS"; +    private static String mLaststate = "NOPROCESS"; -	private static int mLastStateresid=R.string.state_noprocess; +    private static int mLastStateresid = R.string.state_noprocess; -	private static long mlastByteCount[]={0,0,0,0}; +    private static long mlastByteCount[] = {0, 0, 0, 0}; -    public static void logException(LogLevel ll, String context,  Exception e) { +    public static void logException(LogLevel ll, String context, Exception e) {          StringWriter sw = new StringWriter();          e.printStackTrace(new PrintWriter(sw));          LogItem li; -        if (context !=null) { +        if (context != null) {              li = new LogItem(ll, R.string.unhandled_exception_context, e.getMessage(), sw.toString(), context);          } else {              li = new LogItem(ll, R.string.unhandled_exception, e.getMessage(), sw.toString()); @@ -75,18 +74,16 @@ public class VpnStatus {      private static final int MAXLOGENTRIES = 1000; -    public static final String MANAGMENT_PREFIX = "M:"; -      public enum ConnectionStatus {          LEVEL_CONNECTED,          LEVEL_VPNPAUSED,          LEVEL_CONNECTING_SERVER_REPLIED,          LEVEL_CONNECTING_NO_SERVER_REPLY_YET,          LEVEL_NONETWORK, -		LEVEL_NOTCONNECTED, -		LEVEL_AUTH_FAILED, -		LEVEL_WAITING_FOR_USER_INPUT, -		UNKNOWN_LEVEL +        LEVEL_NOTCONNECTED, +        LEVEL_AUTH_FAILED, +        LEVEL_WAITING_FOR_USER_INPUT, +        UNKNOWN_LEVEL      }      public enum LogLevel { @@ -97,6 +94,7 @@ public class VpnStatus {          DEBUG(4);          protected int mValue; +          LogLevel(int value) {              mValue = value;          } @@ -107,59 +105,64 @@ public class VpnStatus {          public static LogLevel getEnumByValue(int value) {              switch (value) { -                case 1:   return INFO; -                case 2:   return ERROR; -                case 3: return WARNING; -                case 4: return DEBUG; -                default:  return null; +                case 1: +                    return INFO; +                case 2: +                    return ERROR; +                case 3: +                    return WARNING; +                case 4: +                    return DEBUG; +                default: +                    return null;              }          }      }      // keytool -printcert -jarfile de.blinkt.openvpn_85.apk -	public static final byte[] officalkey = {-58, -42, -44, -106, 90, -88, -87, -88, -52, -124, 84, 117, 66, 79, -112, -111, -46, 86, -37, 109}; -	public static final byte[] officaldebugkey = {-99, -69, 45, 71, 114, -116, 82, 66, -99, -122, 50, -70, -56, -111, 98, -35, -65, 105, 82, 43}; -	public static final byte[] amazonkey = {-116, -115, -118, -89, -116, -112, 120, 55, 79, -8, -119, -23, 106, -114, -85, -56, -4, 105, 26, -57}; +    public static final byte[] officalkey = {-58, -42, -44, -106, 90, -88, -87, -88, -52, -124, 84, 117, 66, 79, -112, -111, -46, 86, -37, 109}; +    public static final byte[] officaldebugkey = {-99, -69, 45, 71, 114, -116, 82, 66, -99, -122, 50, -70, -56, -111, 98, -35, -65, 105, 82, 43}; +    public static final byte[] amazonkey = {-116, -115, -118, -89, -116, -112, 120, 55, 79, -8, -119, -23, 106, -114, -85, -56, -4, 105, 26, -57};      public static final byte[] fdroidkey = {-92, 111, -42, -46, 123, -96, -60, 79, -27, -31, 49, 103, 11, -54, -68, -27, 17, 2, 121, 104}; -	private static ConnectionStatus mLastLevel=ConnectionStatus.LEVEL_NOTCONNECTED; +    private static ConnectionStatus mLastLevel = ConnectionStatus.LEVEL_NOTCONNECTED; -	static { -		logbuffer  = new LinkedList<LogItem>(); -		logListener = new Vector<VpnStatus.LogListener>(); -		stateListener = new Vector<VpnStatus.StateListener>(); -		byteCountListener = new Vector<VpnStatus.ByteCountListener>(); -		logInformation(); -	} +    static { +        logbuffer = new LinkedList<>(); +        logListener = new Vector<>(); +        stateListener = new Vector<>(); +        byteCountListener = new Vector<>(); +        logInformation(); +    } -	public static class LogItem implements Parcelable { +    public static class LogItem implements Parcelable { -		private Object [] mArgs = null; -		private String mMessage = null; -		private int mRessourceId; -		// Default log priority -		LogLevel mLevel = LogLevel.INFO; -		private long logtime = System.currentTimeMillis(); +        private Object[] mArgs = null; +        private String mMessage = null; +        private int mRessourceId; +        // Default log priority +        LogLevel mLevel = LogLevel.INFO; +        private long logtime = System.currentTimeMillis();          private int mVerbosityLevel = -1; -		private LogItem(int ressourceId, Object[] args) { -			mRessourceId = ressourceId; -			mArgs = args; -		} +        private LogItem(int ressourceId, Object[] args) { +            mRessourceId = ressourceId; +            mArgs = args; +        }          public LogItem(LogLevel level, int verblevel, String message) { -            mMessage=message; +            mMessage = message;              mLevel = level;              mVerbosityLevel = verblevel;          }          @Override -		public int describeContents() { -			return 0; -		} +        public int describeContents() { +            return 0; +        }          @Override @@ -173,65 +176,65 @@ public class VpnStatus {              dest.writeLong(logtime);          } -		public LogItem(Parcel in) { -			mArgs = in.readArray(Object.class.getClassLoader()); -			mMessage = in.readString(); -			mRessourceId = in.readInt(); -			mLevel = LogLevel.getEnumByValue(in.readInt()); +        public LogItem(Parcel in) { +            mArgs = in.readArray(Object.class.getClassLoader()); +            mMessage = in.readString(); +            mRessourceId = in.readInt(); +            mLevel = LogLevel.getEnumByValue(in.readInt());              mVerbosityLevel = in.readInt(); -			logtime = in.readLong(); -		} +            logtime = in.readLong(); +        } -		public static final Parcelable.Creator<LogItem> CREATOR -		= new Parcelable.Creator<LogItem>() { -			public LogItem createFromParcel(Parcel in) { -				return new LogItem(in); -			} +        public static final Parcelable.Creator<LogItem> CREATOR +                = new Parcelable.Creator<LogItem>() { +            public LogItem createFromParcel(Parcel in) { +                return new LogItem(in); +            } -			public LogItem[] newArray(int size) { -				return new LogItem[size]; -			} -		}; +            public LogItem[] newArray(int size) { +                return new LogItem[size]; +            } +        }; -        public LogItem(LogLevel loglevel,int ressourceId, Object... args) { +        public LogItem(LogLevel loglevel, int ressourceId, Object... args) {              mRessourceId = ressourceId; -            mArgs =args; +            mArgs = args;              mLevel = loglevel;          } -		public LogItem(LogLevel loglevel, String msg) { -			mLevel = loglevel; -			mMessage = msg; -		} +        public LogItem(LogLevel loglevel, String msg) { +            mLevel = loglevel; +            mMessage = msg; +        } -		public LogItem(LogLevel loglevel, int ressourceId) { -			mRessourceId =ressourceId; -			mLevel = loglevel; -		} +        public LogItem(LogLevel loglevel, int ressourceId) { +            mRessourceId = ressourceId; +            mLevel = loglevel; +        } -		public String getString(Context c) { +        public String getString(Context c) {              try { -			if(mMessage !=null) { -				return mMessage; -			} else { -				if(c!=null) { -					if(mRessourceId==R.string.mobile_info) -						return getMobileInfoString(c); -					if(mArgs == null) -						return c.getString(mRessourceId); -					else -						return c.getString(mRessourceId, mArgs); -				} else { -					String str = String.format(Locale.ENGLISH,"Log (no context) resid %d", mRessourceId); -					if(mArgs !=null) -						str += TextUtils.join("|", mArgs); - - -					return str; -				} -			} +                if (mMessage != null) { +                    return mMessage; +                } else { +                    if (c != null) { +                        if (mRessourceId == R.string.mobile_info) +                            return getMobileInfoString(c); +                        if (mArgs == null) +                            return c.getString(mRessourceId); +                        else +                            return c.getString(mRessourceId, mArgs); +                    } else { +                        String str = String.format(Locale.ENGLISH, "Log (no context) resid %d", mRessourceId); +                        if (mArgs != null) +                            str += TextUtils.join("|", mArgs); + + +                        return str; +                    } +                }              } catch (UnknownFormatConversionException e) {                  if (c != null)                      throw new UnknownFormatConversionException(e.getLocalizedMessage() + getString(null)); @@ -239,68 +242,66 @@ public class VpnStatus {                      throw e;              } catch (java.util.FormatFlagsConversionMismatchException e) {                  if (c != null) -                    throw new FormatFlagsConversionMismatchException(e.getLocalizedMessage() + getString(null),e.getConversion()); +                    throw new FormatFlagsConversionMismatchException(e.getLocalizedMessage() + getString(null), e.getConversion());                  else                      throw e;              } -		} +        } -        public LogLevel getLogLevel() -        { +        public LogLevel getLogLevel() {              return mLevel;          }          // The lint is wrong here -		@SuppressLint("StringFormatMatches") -		private String getMobileInfoString(Context c) { -			c.getPackageManager(); -			String apksign="error getting package signature"; - -			String version="error getting version"; -			try { -				Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0]; -				CertificateFactory cf = CertificateFactory.getInstance("X.509"); -				X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray())); -				MessageDigest md = MessageDigest.getInstance("SHA-1"); -				byte[] der = cert.getEncoded(); -				md.update(der); -				byte[] digest = md.digest(); - -				if (Arrays.equals(digest, officalkey)) -					apksign = c.getString(R.string.official_build); -				else if (Arrays.equals(digest, officaldebugkey)) -					apksign = c.getString(R.string.debug_build); -				else if (Arrays.equals(digest, amazonkey)) -					apksign = "amazon version"; -				else if (Arrays.equals(digest, fdroidkey)) +        @SuppressLint("StringFormatMatches") +        private String getMobileInfoString(Context c) { +            c.getPackageManager(); +            String apksign = "error getting package signature"; + +            String version = "error getting version"; +            try { +                Signature raw = c.getPackageManager().getPackageInfo(c.getPackageName(), PackageManager.GET_SIGNATURES).signatures[0]; +                CertificateFactory cf = CertificateFactory.getInstance("X.509"); +                X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray())); +                MessageDigest md = MessageDigest.getInstance("SHA-1"); +                byte[] der = cert.getEncoded(); +                md.update(der); +                byte[] digest = md.digest(); + +                if (Arrays.equals(digest, officalkey)) +                    apksign = c.getString(R.string.official_build); +                else if (Arrays.equals(digest, officaldebugkey)) +                    apksign = c.getString(R.string.debug_build); +                else if (Arrays.equals(digest, amazonkey)) +                    apksign = "amazon version"; +                else if (Arrays.equals(digest, fdroidkey))                      apksign = "F-Droid built and signed version";                  else -					apksign = c.getString(R.string.built_by,cert.getSubjectX500Principal().getName()); +                    apksign = c.getString(R.string.built_by, cert.getSubjectX500Principal().getName()); -				PackageInfo packageinfo = c.getPackageManager().getPackageInfo(c.getPackageName(), 0); -				version = packageinfo.versionName; +                PackageInfo packageinfo = c.getPackageManager().getPackageInfo(c.getPackageName(), 0); +                version = packageinfo.versionName; -			} catch (NameNotFoundException e) { -			} catch (CertificateException e) { -			} catch (NoSuchAlgorithmException e) { -			} +            } catch (NameNotFoundException | CertificateException | +                    NoSuchAlgorithmException ignored) { +            } -			Object[] argsext = Arrays.copyOf(mArgs, mArgs.length+2); -			argsext[argsext.length-1]=apksign; -			argsext[argsext.length-2]=version; +            Object[] argsext = Arrays.copyOf(mArgs, mArgs.length + 2); +            argsext[argsext.length - 1] = apksign; +            argsext[argsext.length - 2] = version; -			return c.getString(R.string.mobile_info_extended, argsext); +            return c.getString(R.string.mobile_info_extended, argsext); -		} +        } -		public long getLogtime() { -			return logtime; -		} +        public long getLogtime() { +            return logtime; +        }          public int getVerbosityLevel() { -            if (mVerbosityLevel==-1) { +            if (mVerbosityLevel == -1) {                  // Hack:                  // For message not from OpenVPN, report the status level as log level                  return mLevel.getInt(); @@ -309,90 +310,94 @@ public class VpnStatus {          }      } +    public void saveLogToDisk(Context c) { + +        new File(c.getCacheDir(), "log.xml"); -	public interface LogListener { -		void newLog(LogItem logItem); -	} - -	public interface StateListener { -		void updateState(String state, String logmessage, int localizedResId, ConnectionStatus level); -	} - -	public interface ByteCountListener { -		void updateByteCount(long in, long out, long diffIn, long diffOut); -	} - -	public synchronized static void logMessage(LogLevel level,String prefix, String message) -	{ -		newLogItem(new LogItem(level, prefix + message)); - -	} - -	public synchronized static void clearLog() { -		logbuffer.clear(); -		logInformation(); -	} - -	private static void logInformation() { -		logInfo(R.string.mobile_info,Build.MODEL, Build.BOARD,Build.BRAND,Build.VERSION.SDK_INT); -	} - -	public synchronized static void addLogListener(LogListener ll){ -		logListener.add(ll); -	} - -	public synchronized static void removeLogListener(LogListener ll) { -		logListener.remove(ll); -	} - -	public synchronized static void addByteCountListener(ByteCountListener bcl) { -		bcl.updateByteCount(mlastByteCount[0],	mlastByteCount[1], mlastByteCount[2], mlastByteCount[3]); -		byteCountListener.add(bcl); -	}	 - -	public synchronized static void removeByteCountListener(ByteCountListener bcl) { -		byteCountListener.remove(bcl); -	} - - -	public synchronized static void addStateListener(StateListener sl){ -		if(!stateListener.contains(sl)){ -			stateListener.add(sl); -			if(mLaststate!=null) -				sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel); -		} -	}	 - -	private static int getLocalizedState(String state){ -		if (state.equals("CONNECTING"))  -			return R.string.state_connecting; -		else if (state.equals("WAIT")) -			return R.string.state_wait; -		else if (state.equals("AUTH")) -			return R.string.state_auth; -		else if (state.equals("GET_CONFIG")) -			return R.string.state_get_config; -		else if (state.equals("ASSIGN_IP")) -			return R.string.state_assign_ip; -		else if (state.equals("ADD_ROUTES")) -			return R.string.state_add_routes; -		else if (state.equals("CONNECTED")) -			return R.string.state_connected; -		else if (state.equals("DISCONNECTED")) -			return R.string.state_disconnected; -		else if (state.equals("RECONNECTING")) -			return R.string.state_reconnecting; -		else if (state.equals("EXITING")) -			return R.string.state_exiting; -		else if (state.equals("RESOLVE")) -			return R.string.state_resolve; -		else if (state.equals("TCP_CONNECT")) -			return R.string.state_tcp_connect; -		else -			return R.string.unknown_state; - -	} +    } + +    public interface LogListener { +        void newLog(LogItem logItem); +    } + +    public interface StateListener { +        void updateState(String state, String logmessage, int localizedResId, ConnectionStatus level); +    } + +    public interface ByteCountListener { +        void updateByteCount(long in, long out, long diffIn, long diffOut); +    } + +    public synchronized static void logMessage(LogLevel level, String prefix, String message) { +        newLogItem(new LogItem(level, prefix + message)); + +    } + +    public synchronized static void clearLog() { +        logbuffer.clear(); +        logInformation(); +    } + +    private static void logInformation() { +        logInfo(R.string.mobile_info, Build.MODEL, Build.BOARD, Build.BRAND, Build.VERSION.SDK_INT); +    } + +    public synchronized static void addLogListener(LogListener ll) { +        logListener.add(ll); +    } + +    public synchronized static void removeLogListener(LogListener ll) { +        logListener.remove(ll); +    } + +    public synchronized static void addByteCountListener(ByteCountListener bcl) { +        bcl.updateByteCount(mlastByteCount[0], mlastByteCount[1], mlastByteCount[2], mlastByteCount[3]); +        byteCountListener.add(bcl); +    } + +    public synchronized static void removeByteCountListener(ByteCountListener bcl) { +        byteCountListener.remove(bcl); +    } + + +    public synchronized static void addStateListener(StateListener sl) { +        if (!stateListener.contains(sl)) { +            stateListener.add(sl); +            if (mLaststate != null) +                sl.updateState(mLaststate, mLaststatemsg, mLastStateresid, mLastLevel); +        } +    } + +    private static int getLocalizedState(String state) { +        if (state.equals("CONNECTING")) +            return R.string.state_connecting; +        else if (state.equals("WAIT")) +            return R.string.state_wait; +        else if (state.equals("AUTH")) +            return R.string.state_auth; +        else if (state.equals("GET_CONFIG")) +            return R.string.state_get_config; +        else if (state.equals("ASSIGN_IP")) +            return R.string.state_assign_ip; +        else if (state.equals("ADD_ROUTES")) +            return R.string.state_add_routes; +        else if (state.equals("CONNECTED")) +            return R.string.state_connected; +        else if (state.equals("DISCONNECTED")) +            return R.string.state_disconnected; +        else if (state.equals("RECONNECTING")) +            return R.string.state_reconnecting; +        else if (state.equals("EXITING")) +            return R.string.state_exiting; +        else if (state.equals("RESOLVE")) +            return R.string.state_resolve; +        else if (state.equals("TCP_CONNECT")) +            return R.string.state_tcp_connect; +        else +            return R.string.unknown_state; + +    }      public static void updateStatePause(OpenVPNManagement.pauseReason pauseReason) {          switch (pauseReason) { @@ -409,88 +414,84 @@ public class VpnStatus {      } -    private static ConnectionStatus getLevel(String state){ -		String[] noreplyet = {"CONNECTING","WAIT", "RECONNECTING", "RESOLVE", "TCP_CONNECT"};  -		String[] reply = {"AUTH","GET_CONFIG","ASSIGN_IP","ADD_ROUTES"}; -		String[] connected = {"CONNECTED"}; -		String[] notconnected = {"DISCONNECTED", "EXITING"}; - -		for(String x:noreplyet) -			if(state.equals(x)) -				return ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET; - -		for(String x:reply) -			if(state.equals(x)) -				return ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED; +    private static ConnectionStatus getLevel(String state) { +        String[] noreplyet = {"CONNECTING", "WAIT", "RECONNECTING", "RESOLVE", "TCP_CONNECT"}; +        String[] reply = {"AUTH", "GET_CONFIG", "ASSIGN_IP", "ADD_ROUTES"}; +        String[] connected = {"CONNECTED"}; +        String[] notconnected = {"DISCONNECTED", "EXITING"}; -		for(String x:connected) -			if(state.equals(x)) -				return ConnectionStatus.LEVEL_CONNECTED; +        for (String x : noreplyet) +            if (state.equals(x)) +                return ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET; -		for(String x:notconnected) -			if(state.equals(x)) -				return ConnectionStatus.LEVEL_NOTCONNECTED; +        for (String x : reply) +            if (state.equals(x)) +                return ConnectionStatus.LEVEL_CONNECTING_SERVER_REPLIED; -		return ConnectionStatus.UNKNOWN_LEVEL; +        for (String x : connected) +            if (state.equals(x)) +                return ConnectionStatus.LEVEL_CONNECTED; -	} +        for (String x : notconnected) +            if (state.equals(x)) +                return ConnectionStatus.LEVEL_NOTCONNECTED; +        return ConnectionStatus.UNKNOWN_LEVEL; +    } -	public synchronized static void removeStateListener(StateListener sl) { -		stateListener.remove(sl); -	} +    public synchronized static void removeStateListener(StateListener sl) { +        stateListener.remove(sl); +    } -	synchronized public static LogItem[] getlogbuffer() { +    synchronized public static LogItem[] getlogbuffer() { -		// The stoned way of java to return an array from a vector -		// brought to you by eclipse auto complete -		return logbuffer.toArray(new LogItem[logbuffer.size()]); +        // The stoned way of java to return an array from a vector +        // brought to you by eclipse auto complete +        return logbuffer.toArray(new LogItem[logbuffer.size()]); -	} +    } -	public static void updateStateString (String state, String msg) { -		int rid = getLocalizedState(state); -		ConnectionStatus level = getLevel(state); -		updateStateString(state, msg, rid, level); -	} +    public static void updateStateString(String state, String msg) { +        int rid = getLocalizedState(state); +        ConnectionStatus level = getLevel(state); +        updateStateString(state, msg, rid, level); +    } -	public synchronized static void updateStateString(String state, String msg, int resid, ConnectionStatus level) { +    public synchronized static void updateStateString(String state, String msg, int resid, ConnectionStatus level) {          // Workound for OpenVPN doing AUTH and wait and being connected          // Simply ignore these state          if (mLastLevel == ConnectionStatus.LEVEL_CONNECTED && -                (state.equals("WAIT") || state.equals("AUTH"))) -        { -            newLogItem(new LogItem((LogLevel.DEBUG), String.format("Ignoring OpenVPN Status in CONNECTED state (%s->%s): %s",state,level.toString(),msg))); +                (state.equals("WAIT") || state.equals("AUTH"))) { +            newLogItem(new LogItem((LogLevel.DEBUG), String.format("Ignoring OpenVPN Status in CONNECTED state (%s->%s): %s", state, level.toString(), msg)));              return;          } -        mLaststate= state; -		mLaststatemsg = msg; -		mLastStateresid = resid; -		mLastLevel = level; - +        mLaststate = state; +        mLaststatemsg = msg; +        mLastStateresid = resid; +        mLastLevel = level;          for (StateListener sl : stateListener) { -			sl.updateState(state,msg,resid,level); -		} +            sl.updateState(state, msg, resid, level); +        }          //newLogItem(new LogItem((LogLevel.DEBUG), String.format("New OpenVPN Status (%s->%s): %s",state,level.toString(),msg))); -	} +    } -	public static void logInfo(String message) { -		newLogItem(new LogItem(LogLevel.INFO, message)); -	} +    public static void logInfo(String message) { +        newLogItem(new LogItem(LogLevel.INFO, message)); +    }      public static void logDebug(String message) {          newLogItem(new LogItem(LogLevel.DEBUG, message));      }      public static void logInfo(int resourceId, Object... args) { -		newLogItem(new LogItem(LogLevel.INFO, resourceId, args)); -	} +        newLogItem(new LogItem(LogLevel.INFO, resourceId, args)); +    }      public static void logDebug(int resourceId, Object... args) {          newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args)); @@ -498,19 +499,19 @@ public class VpnStatus {      private synchronized static void newLogItem(LogItem logItem) { -		logbuffer.addLast(logItem); -		if(logbuffer.size()>MAXLOGENTRIES) -			logbuffer.removeFirst(); +        logbuffer.addLast(logItem); +        if (logbuffer.size() > MAXLOGENTRIES) +            logbuffer.removeFirst(); -		for (LogListener ll : logListener) { -			ll.newLog(logItem); -		} -	} +        for (LogListener ll : logListener) { +            ll.newLog(logItem); +        } +    } -	public static void logError(String msg) { -		newLogItem(new LogItem(LogLevel.ERROR, msg)); +    public static void logError(String msg) { +        newLogItem(new LogItem(LogLevel.ERROR, msg)); -	} +    }      public static void logWarning(int resourceId, Object... args) {          newLogItem(new LogItem(LogLevel.WARNING, resourceId, args)); @@ -522,11 +523,12 @@ public class VpnStatus {      public static void logError(int resourceId) { -		newLogItem(new LogItem(LogLevel.ERROR, resourceId)); -	} -	public static void logError(int resourceId, Object... args) { -		newLogItem(new LogItem(LogLevel.ERROR, resourceId, args)); -	} +        newLogItem(new LogItem(LogLevel.ERROR, resourceId)); +    } + +    public static void logError(int resourceId, Object... args) { +        newLogItem(new LogItem(LogLevel.ERROR, resourceId, args)); +    }      public static void logMessageOpenVPN(LogLevel level, int ovpnlevel, String message) {          newLogItem(new LogItem(level, ovpnlevel, message)); @@ -535,19 +537,17 @@ public class VpnStatus {      public static synchronized void updateByteCount(long in, long out) { -		long lastIn = mlastByteCount[0]; -		long lastOut = mlastByteCount[1]; -		long diffIn = mlastByteCount[2] = in - lastIn; -		long diffOut = mlastByteCount[3] = out - lastOut; -		 -		 - -		mlastByteCount = new long[] {in,out,diffIn,diffOut}; -		for(ByteCountListener bcl:byteCountListener){ -			bcl.updateByteCount(in, out, diffIn,diffOut); -		} -	} +        long lastIn = mlastByteCount[0]; +        long lastOut = mlastByteCount[1]; +        long diffIn = mlastByteCount[2] = in - lastIn; +        long diffOut = mlastByteCount[3] = out - lastOut; + +        mlastByteCount = new long[]{in, out, diffIn, diffOut}; +        for (ByteCountListener bcl : byteCountListener) { +            bcl.updateByteCount(in, out, diffIn, diffOut); +        } +    }  } diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java index e15fe509..cf712e7c 100644 --- a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -22,8 +22,10 @@ import android.view.*;  import android.view.View.OnClickListener;  import android.widget.ArrayAdapter;  import android.widget.EditText; +import android.widget.ImageButton;  import android.widget.TextView;  import android.widget.Toast; +  import de.blinkt.openvpn.*;  import de.blinkt.openvpn.activities.ConfigConverter;  import de.blinkt.openvpn.activities.FileSelect; @@ -34,120 +36,120 @@ import java.util.Collection;  import java.util.Comparator;  import java.util.TreeSet; -public class VPNProfileList extends ListFragment { +public class VPNProfileList extends ListFragment implements OnClickListener { -	public final static int RESULT_VPN_DELETED = Activity.RESULT_FIRST_USER; -    public final static int RESULT_VPN_DUPLICATE = Activity.RESULT_FIRST_USER +1 ; +    public final static int RESULT_VPN_DELETED = Activity.RESULT_FIRST_USER; +    public final static int RESULT_VPN_DUPLICATE = Activity.RESULT_FIRST_USER + 1; -	private static final int MENU_ADD_PROFILE = Menu.FIRST; +    private static final int MENU_ADD_PROFILE = Menu.FIRST; -	private static final int START_VPN_CONFIG = 92; -	private static final int SELECT_PROFILE = 43; -	private static final int IMPORT_PROFILE = 231; +    private static final int START_VPN_CONFIG = 92; +    private static final int SELECT_PROFILE = 43; +    private static final int IMPORT_PROFILE = 231;      private static final int FILE_PICKER_RESULT_KITKAT = 392; -	private static final int MENU_IMPORT_PROFILE = Menu.FIRST +1; +    private static final int MENU_IMPORT_PROFILE = Menu.FIRST + 1;      class VPNArrayAdapter extends ArrayAdapter<VpnProfile> { -		public VPNArrayAdapter(Context context, int resource, -				int textViewResourceId) { -			super(context, resource, textViewResourceId); -		} - -		@Override -		public View getView(final int position, View convertView, ViewGroup parent) { -			View v = super.getView(position, convertView, parent); - -			View titleview = v.findViewById(R.id.vpn_list_item_left); -			titleview.setOnClickListener(new OnClickListener() { -				@Override -				public void onClick(View v) { -					VpnProfile profile =(VpnProfile) getListAdapter().getItem(position); -					startVPN(profile); -				} -			}); - -			View settingsview = v.findViewById(R.id.quickedit_settings); -			settingsview.setOnClickListener(new OnClickListener() { - -				@Override -				public void onClick(View v) { -					VpnProfile editProfile = (VpnProfile) getListAdapter().getItem(position); -					editVPN(editProfile); - -				} -			}); - -			return v; -		} -	} - - - +        public VPNArrayAdapter(Context context, int resource, +                               int textViewResourceId) { +            super(context, resource, textViewResourceId); +        } +        @Override +        public View getView(final int position, View convertView, ViewGroup parent) { +            View v = super.getView(position, convertView, parent); + +            View titleview = v.findViewById(R.id.vpn_list_item_left); +            titleview.setOnClickListener(new OnClickListener() { +                @Override +                public void onClick(View v) { +                    VpnProfile profile = (VpnProfile) getListAdapter().getItem(position); +                    startVPN(profile); +                } +            }); + +            View settingsview = v.findViewById(R.id.quickedit_settings); +            settingsview.setOnClickListener(new OnClickListener() { + +                @Override +                public void onClick(View v) { +                    VpnProfile editProfile = (VpnProfile) getListAdapter().getItem(position); +                    editVPN(editProfile); + +                } +            }); + +            return v; +        } +    } +    private ArrayAdapter<VpnProfile> mArrayadapter; +    protected VpnProfile mEditProfile = null; -	private ArrayAdapter<VpnProfile> mArrayadapter; -	protected VpnProfile mEditProfile=null; +    @Override +    public void onCreate(Bundle savedInstanceState) { +        super.onCreate(savedInstanceState); +        setHasOptionsMenu(true); +    } -	@Override -	public void onCreate(Bundle savedInstanceState) { -		super.onCreate(savedInstanceState); -		setHasOptionsMenu(true); +    class MiniImageGetter implements ImageGetter { -	} +        @Override +        public Drawable getDrawable(String source) { +            Drawable d = null; +            if ("ic_menu_add".equals(source)) +                d = getActivity().getResources().getDrawable(R.drawable.ic_menu_add_grey); +            else if ("ic_menu_archive".equals(source)) +                d = getActivity().getResources().getDrawable(R.drawable.ic_menu_import_grey); + + +            if (d != null) { +                d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); +                return d; +            } else { +                return null; +            } +        } +    } -	class MiniImageGetter implements ImageGetter { +    @Override +    public View onCreateView(LayoutInflater inflater, ViewGroup container, +                             Bundle savedInstanceState) { +        View v = inflater.inflate(R.layout.vpn_profile_list, container, false); -		@Override -		public Drawable getDrawable(String source) { -			Drawable d=null; -			if ("ic_menu_add".equals(source)) -				d = getActivity().getResources().getDrawable(R.drawable.ic_menu_add_grey); -			else if("ic_menu_archive".equals(source)) -				d = getActivity().getResources().getDrawable(R.drawable.ic_menu_import_grey); -			 -			 -			 -			if(d!=null) { -				d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); -				return d; -			}else{ -				return null; -			} -		} -	} +        TextView newvpntext = (TextView) v.findViewById(R.id.add_new_vpn_hint); +        TextView importvpntext = (TextView) v.findViewById(R.id.import_vpn_hint); +        newvpntext.setText(Html.fromHtml(getString(R.string.add_new_vpn_hint), new MiniImageGetter(), null)); +        importvpntext.setText(Html.fromHtml(getString(R.string.vpn_import_hint), new MiniImageGetter(), null)); -	@Override -    public View onCreateView(LayoutInflater inflater, ViewGroup container, -                             Bundle savedInstanceState) { -		View v = inflater.inflate(R.layout.vpn_profile_list, container,false); +        ImageButton fab_add = (ImageButton) v.findViewById(R.id.fab_add); +        ImageButton fab_import = (ImageButton) v.findViewById(R.id.fab_import); +        if (fab_add != null) +            fab_add.setOnClickListener(this); -		TextView newvpntext = (TextView) v.findViewById(R.id.add_new_vpn_hint); -		TextView importvpntext = (TextView) v.findViewById(R.id.import_vpn_hint); -		 -		newvpntext.setText(Html.fromHtml(getString(R.string.add_new_vpn_hint),new MiniImageGetter(),null)); -		importvpntext.setText(Html.fromHtml(getString(R.string.vpn_import_hint),new MiniImageGetter(),null)); +        if (fab_import != null) +            fab_import.setOnClickListener(this); -		return v; +        return v; -	} +    } -	@Override -	public void onActivityCreated(Bundle savedInstanceState) { -		super.onActivityCreated(savedInstanceState); -		setListAdapter(); -	} +    @Override +    public void onActivityCreated(Bundle savedInstanceState) { +        super.onActivityCreated(savedInstanceState); +        setListAdapter(); +    }      static class VpnProfileNameComparator implements Comparator<VpnProfile> { @@ -172,52 +174,63 @@ public class VPNProfileList extends ListFragment {      } -	private void setListAdapter() { -		mArrayadapter = new VPNArrayAdapter(getActivity(),R.layout.vpn_list_item,R.id.vpn_item_title); -		Collection<VpnProfile> allvpn = getPM().getProfiles(); +    private void setListAdapter() { +        mArrayadapter = new VPNArrayAdapter(getActivity(), R.layout.vpn_list_item, R.id.vpn_item_title); +        Collection<VpnProfile> allvpn = getPM().getProfiles();          TreeSet<VpnProfile> sortedset = new TreeSet<VpnProfile>(new VpnProfileNameComparator());          sortedset.addAll(allvpn);          mArrayadapter.addAll(sortedset);          setListAdapter(mArrayadapter); -	} - +    } -	@Override -	public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { -		menu.add(0, MENU_ADD_PROFILE, 0 , R.string.menu_add_profile) -		.setIcon(R.drawable.ic_menu_add) -		.setAlphabeticShortcut('a') -		.setTitleCondensed(getActivity().getString(R.string.add)) -		.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS |  MenuItem.SHOW_AS_ACTION_WITH_TEXT); +    @Override +    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { +        menu.add(0, MENU_ADD_PROFILE, 0, R.string.menu_add_profile) +                .setIcon(R.drawable.ic_menu_add) +                .setAlphabeticShortcut('a') +                .setTitleCondensed(getActivity().getString(R.string.add)) +                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); -		menu.add(0, MENU_IMPORT_PROFILE, 0,  R.string.menu_import) -		.setIcon(R.drawable.ic_menu_import) -		.setAlphabeticShortcut('i') -		.setTitleCondensed(getActivity().getString(R.string.menu_import_short)) -		.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT ); -	} +        menu.add(0, MENU_IMPORT_PROFILE, 0, R.string.menu_import) +                .setIcon(R.drawable.ic_menu_import) +                .setAlphabeticShortcut('i') +                .setTitleCondensed(getActivity().getString(R.string.menu_import_short)) +                .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); +    } -	@Override -	public boolean onOptionsItemSelected(MenuItem item) { -		final int itemId = item.getItemId(); -		if (itemId == MENU_ADD_PROFILE) { -			onAddOrDuplicateProfile(null); -			return true; -		} else if (itemId == MENU_IMPORT_PROFILE) { +    @Override +    public boolean onOptionsItemSelected(MenuItem item) { +        final int itemId = item.getItemId(); +        if (itemId == MENU_ADD_PROFILE) { +            onAddOrDuplicateProfile(null); +            return true; +        } else if (itemId == MENU_IMPORT_PROFILE) {              return startImportConfigFilePicker(); -		} else { -			return super.onOptionsItemSelected(item); -		} -	} +        } else { +            return super.onOptionsItemSelected(item); +        } +    } + +    @Override +    public void onClick(View v) { +        switch (v.getId()) { +            case R.id.fab_import: +                startImportConfigFilePicker(); +                break; +            case R.id.fab_add: +                onAddOrDuplicateProfile(null); +                break; +        } +    }      private boolean startImportConfigFilePicker() { -        boolean startOldFileDialog=true; -        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) -                startOldFileDialog = ! startFilePicker(); +        boolean startOldFileDialog = true; +        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) +            startOldFileDialog = !startFilePicker();          if (startOldFileDialog)              startImportConfig(); @@ -228,8 +241,8 @@ public class VPNProfileList extends ListFragment {      @TargetApi(Build.VERSION_CODES.KITKAT)      private boolean startFilePicker() { -       Intent i = Utils.getFilePickerIntent(getActivity(), Utils.FileType.OVPN_CONFIG); -        if (i!=null) { +        Intent i = Utils.getFilePickerIntent(getActivity(), Utils.FileType.OVPN_CONFIG); +        if (i != null) {              startActivityForResult(i, FILE_PICKER_RESULT_KITKAT);              return true;          } else @@ -237,32 +250,29 @@ public class VPNProfileList extends ListFragment {      }      private void startImportConfig() { -		Intent intent = new Intent(getActivity(),FileSelect.class); -		intent.putExtra(FileSelect.NO_INLINE_SELECTION, true); -		intent.putExtra(FileSelect.WINDOW_TITLE, R.string.import_configuration_file); -		startActivityForResult(intent, SELECT_PROFILE); -	} - - - +        Intent intent = new Intent(getActivity(), FileSelect.class); +        intent.putExtra(FileSelect.NO_INLINE_SELECTION, true); +        intent.putExtra(FileSelect.WINDOW_TITLE, R.string.import_configuration_file); +        startActivityForResult(intent, SELECT_PROFILE); +    } -	private void onAddOrDuplicateProfile(final VpnProfile mCopyProfile) { -		Context context = getActivity(); -		if (context != null) { -			final EditText entry = new EditText(context); -			entry.setSingleLine(); +    private void onAddOrDuplicateProfile(final VpnProfile mCopyProfile) { +        Context context = getActivity(); +        if (context != null) { +            final EditText entry = new EditText(context); +            entry.setSingleLine(); -			AlertDialog.Builder dialog = new AlertDialog.Builder(context); +            AlertDialog.Builder dialog = new AlertDialog.Builder(context);              if (mCopyProfile == null) -			    dialog.setTitle(R.string.menu_add_profile); +                dialog.setTitle(R.string.menu_add_profile);              else {                  dialog.setTitle(context.getString(R.string.duplicate_profile_title, mCopyProfile.mName));                  entry.setText(getString(R.string.copy_of_profile, mCopyProfile.mName));              }              dialog.setMessage(R.string.add_profile_name_prompt); -			dialog.setView(entry); +            dialog.setView(entry);              dialog.setNeutralButton(R.string.menu_import_short,                      new DialogInterface.OnClickListener() { @@ -271,7 +281,7 @@ public class VPNProfileList extends ListFragment {                              startImportConfigFilePicker();                          }                      }); -			dialog.setPositiveButton(android.R.string.ok, +            dialog.setPositiveButton(android.R.string.ok,                      new DialogInterface.OnClickListener() {                          @Override                          public void onClick(DialogInterface dialog, int which) { @@ -292,32 +302,32 @@ public class VPNProfileList extends ListFragment {                      }); -			dialog.setNegativeButton(android.R.string.cancel, null); -			dialog.create().show(); -		} +            dialog.setNegativeButton(android.R.string.cancel, null); +            dialog.create().show(); +        } -	} +    } -	private void addProfile(VpnProfile profile) { -		getPM().addProfile(profile); -		getPM().saveProfileList(getActivity()); -		getPM().saveProfile(getActivity(),profile); -		mArrayadapter.add(profile); -	} +    private void addProfile(VpnProfile profile) { +        getPM().addProfile(profile); +        getPM().saveProfileList(getActivity()); +        getPM().saveProfile(getActivity(), profile); +        mArrayadapter.add(profile); +    } -	private ProfileManager getPM() { -		return ProfileManager.getInstance(getActivity()); -	} +    private ProfileManager getPM() { +        return ProfileManager.getInstance(getActivity()); +    } -	@Override -	public void onActivityResult(int requestCode, int resultCode, Intent data) { -		super.onActivityResult(requestCode, resultCode, data); +    @Override +    public void onActivityResult(int requestCode, int resultCode, Intent data) { +        super.onActivityResult(requestCode, resultCode, data); -		if(resultCode == RESULT_VPN_DELETED){ -			if(mArrayadapter != null && mEditProfile !=null) -				mArrayadapter.remove(mEditProfile); -		} else if (resultCode == RESULT_VPN_DUPLICATE && data != null) { +        if (resultCode == RESULT_VPN_DELETED) { +            if (mArrayadapter != null && mEditProfile != null) +                mArrayadapter.remove(mEditProfile); +        } else if (resultCode == RESULT_VPN_DUPLICATE && data != null) {              String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID);              VpnProfile profile = ProfileManager.get(getActivity(), profileUUID);              if (profile != null) @@ -325,37 +335,37 @@ public class VPNProfileList extends ListFragment {          } -        if(resultCode != Activity.RESULT_OK) -			return; +        if (resultCode != Activity.RESULT_OK) +            return; -		if (requestCode == START_VPN_CONFIG) { -			String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); +        if (requestCode == START_VPN_CONFIG) { +            String configuredVPN = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); -			VpnProfile profile = ProfileManager.get(getActivity(),configuredVPN); -			getPM().saveProfile(getActivity(), profile); -			// Name could be modified, reset List adapter -			setListAdapter(); +            VpnProfile profile = ProfileManager.get(getActivity(), configuredVPN); +            getPM().saveProfile(getActivity(), profile); +            // Name could be modified, reset List adapter +            setListAdapter(); -		} else if(requestCode== SELECT_PROFILE) { +        } else if (requestCode == SELECT_PROFILE) {              String fileData = data.getStringExtra(FileSelect.RESULT_DATA);              Uri uri = new Uri.Builder().path(fileData).scheme("file").build();              startConfigImport(uri); -		} else if(requestCode == IMPORT_PROFILE) { -			String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); -			mArrayadapter.add(ProfileManager.get(getActivity(), profileUUID)); -		} else if(requestCode == FILE_PICKER_RESULT_KITKAT) { +        } else if (requestCode == IMPORT_PROFILE) { +            String profileUUID = data.getStringExtra(VpnProfile.EXTRA_PROFILEUUID); +            mArrayadapter.add(ProfileManager.get(getActivity(), profileUUID)); +        } else if (requestCode == FILE_PICKER_RESULT_KITKAT) {              if (data != null) {                  Uri uri = data.getData();                  startConfigImport(uri);              }          } -	} +    }      private void startConfigImport(Uri uri) { -        Intent startImport = new Intent(getActivity(),ConfigConverter.class); +        Intent startImport = new Intent(getActivity(), ConfigConverter.class);          startImport.setAction(ConfigConverter.IMPORT_PROFILE);          startImport.setData(uri);          startActivityForResult(startImport, IMPORT_PROFILE); @@ -363,20 +373,20 @@ public class VPNProfileList extends ListFragment {      private void editVPN(VpnProfile profile) { -		mEditProfile =profile; -		Intent vprefintent = new Intent(getActivity(),VPNPreferences.class) -		.putExtra(getActivity().getPackageName() + ".profileUUID", profile.getUUID().toString()); +        mEditProfile = profile; +        Intent vprefintent = new Intent(getActivity(), VPNPreferences.class) +                .putExtra(getActivity().getPackageName() + ".profileUUID", profile.getUUID().toString()); -		startActivityForResult(vprefintent,START_VPN_CONFIG); -	} +        startActivityForResult(vprefintent, START_VPN_CONFIG); +    } -	private void startVPN(VpnProfile profile) { +    private void startVPN(VpnProfile profile) { -		getPM().saveProfile(getActivity(), profile); +        getPM().saveProfile(getActivity(), profile); -		Intent intent = new Intent(getActivity(),LaunchVPN.class); -		intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); -		intent.setAction(Intent.ACTION_MAIN); -		startActivity(intent); -	} +        Intent intent = new Intent(getActivity(), LaunchVPN.class); +        intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); +        intent.setAction(Intent.ACTION_MAIN); +        startActivity(intent); +    }  } diff --git a/main/src/main/res/layout-v21/profile_list_fabs.xml b/main/src/main/res/layout-v21/profile_list_fabs.xml index 5ea6512d..28d393f0 100644 --- a/main/src/main/res/layout-v21/profile_list_fabs.xml +++ b/main/src/main/res/layout-v21/profile_list_fabs.xml @@ -9,7 +9,7 @@      tools:ignore="InconsistentLayout">      <ImageButton -        android:id="@+id/add_button" +        android:id="@+id/fab_import"          android:layout_width="@dimen/round_button_diameter"          android:layout_height="@dimen/round_button_diameter"          android:layout_gravity="end|bottom" @@ -23,7 +23,7 @@      <ImageButton          android:contentDescription="@string/add_profile" -        android:id="@+id/import_button" +        android:id="@+id/fab_add"          android:layout_width="@dimen/round_button_diameter"          android:layout_height="@dimen/round_button_diameter"          android:layout_gravity="end|bottom" diff --git a/main/src/main/res/layout-v21/save_fab.xml b/main/src/main/res/layout-v21/save_fab.xml new file mode 100644 index 00000000..55fdecc6 --- /dev/null +++ b/main/src/main/res/layout-v21/save_fab.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +    <!-- +      ~ Copyright (c) 2012-2015 Arne Schwabe +      ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +      --> + +<ImageButton xmlns:android="http://schemas.android.com/apk/res/android" +    tools:ignore="InconsistentLayout" +    xmlns:tools="http://schemas.android.com/tools" +    android:id="@+id/fab_save" +    android:layout_width="@dimen/round_button_diameter" +    android:layout_height="@dimen/round_button_diameter" +    android:layout_alignParentBottom="true" +    android:layout_alignParentEnd="true" +    android:layout_marginBottom="16dp" +    android:layout_marginEnd="16dp" +    android:background="@drawable/oval_ripple" +    android:elevation="1dp" +    android:src="@android:drawable/ic_menu_save" +    android:stateListAnimator="@anim/fab_anim" +    android:tint="@android:color/white" +    tools:showIn="@layout/config_converter" />
\ No newline at end of file diff --git a/main/src/main/res/layout/config_converter.xml b/main/src/main/res/layout/config_converter.xml index 96381b60..fe4ca31f 100644 --- a/main/src/main/res/layout/config_converter.xml +++ b/main/src/main/res/layout/config_converter.xml @@ -3,10 +3,12 @@    ~ Copyright (c) 2012-2014 Arne Schwabe    ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt    --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +    xmlns:tools="http://schemas.android.com/tools" +    android:layout_width="match_parent" +    android:layout_height="match_parent"> -<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" -            xmlns:tools="http://schemas.android.com/tools" -            android:layout_width="match_parent" +<ScrollView android:layout_width="match_parent"              android:layout_height="match_parent"              android:layout_margin="@dimen/stdpadding"              android:orientation="vertical"> @@ -43,6 +45,6 @@                  android:id="@+id/textView"/>      </LinearLayout> - - -</ScrollView>
\ No newline at end of file +    </ScrollView> +    <include layout="@layout/save_fab" /> +</FrameLayout> diff --git a/main/src/main/res/layout/save_fab.xml b/main/src/main/res/layout/save_fab.xml new file mode 100644 index 00000000..e3cae9ab --- /dev/null +++ b/main/src/main/res/layout/save_fab.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +  ~ Copyright (c) 2012-2014 Arne Schwabe +  ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt +  --> + +<merge xmlns:android="http://schemas.android.com/apk/res/android" +    android:orientation="vertical" android:layout_width="match_parent" +    android:layout_height="match_parent"> + +</merge>
\ No newline at end of file diff --git a/main/src/main/res/layout/vpn_profile_list.xml b/main/src/main/res/layout/vpn_profile_list.xml index 082ffd77..3f295f2d 100644 --- a/main/src/main/res/layout/vpn_profile_list.xml +++ b/main/src/main/res/layout/vpn_profile_list.xml @@ -74,6 +74,6 @@      </LinearLayout> -    <!-- <include layout="@layout/profile_list_fabs" /> --> +     <include layout="@layout/profile_list_fabs" />  </FrameLayout> diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml index 88e61928..6f64d2ba 100755 --- a/main/src/main/res/values/strings.xml +++ b/main/src/main/res/values/strings.xml @@ -380,5 +380,7 @@      <string name="tls_cipher_alert">Newer OpenVPN for Android versions (0.6.29/March 2015) use a more secure default for the allowed cipher suites (tls-cipher \"DEFAULT:!EXP:!PSK:!SRP:!kRSA\"). Unfortunately, omitting the less secure cipher suites and export cipher suites, especially the omission of cipher suites that do not support Perfect Forward Secrecy (Diffie-Hellman) causes some problems. This usually caused by an well-intentioned but poorly executed attempts to strengthen TLS security by setting tls-cipher on the server or some embedded OSes with stripped down SSL (e.g. MikroTik).\nTo solve this problem the problem, set the tls-cipher settings on the server to reasonable default like tls-cipher \"DEFAULT:!EXP:!PSK:!SRP:!kRSA\". To work around the problem on the client add the custom option tls-cipher DEFAULT on the Android client.</string>      <string name="message_no_user_edit">This profile has been added from an external app (%s) and has been marked as not user editable.</string>      <string name="crl_file">Certificate Revocation List</string> +    <string name="service_restarted">Restarting OpenVPN Service (App crashed probably crashed or killed for memory pressure)</string> +    <string name="import_config_error">Importing the config yielded an error, cannot save it</string>  </resources>  | 
