diff options
70 files changed, 1051 insertions, 719 deletions
diff --git a/gradle.properties b/gradle.properties index 14448e8f..55513d17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,6 @@ # android.useAndroidX=true -android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false org.gradle.jvmargs=-Xmx4096m diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 37fd145d..dff44225 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,34 +1,39 @@ [versions] -android-gradle-plugin = "8.1.2" -androidx-annotation = "1.6.0" -androidx-appcompat = "1.6.1" -android-view-material = "1.7.0" -androidx-core-ktx = "1.10.1" -androidx-fragment-ktx = "1.6.0" -androidx-preference-ktx = "1.2.0" -androidx-webkit = "1.7.0" -androidx-lifecycle-viewmodel-ktx = "2.6.1" -androidx-lifecycle-runtime-ktx = "2.6.1" +android-gradle-plugin = "8.5.2" +androidx-activity = "1.9.1" +androidx-annotation = "1.8.2" +androidx-appcompat = "1.7.0" +android-view-material = "1.12.0" +androidx-core-ktx = "1.13.1" +androidx-fragment-ktx = "1.8.2" +androidx-preference-ktx = "1.2.1" +androidx-webkit = "1.11.0" +androidx-lifecycle-viewmodel-ktx = "2.8.4" +androidx-lifecycle-runtime-ktx = "2.8.4" androidx-security-crypto = "1.1.0-alpha06" androidx-constraintlayout = "2.1.4" androidx-cardview = "1.0.0" -androidx-recyclerview = "1.3.0" -bouncycastle = "1.49" +androidx-recyclerview = "1.3.2" +androidx-viewpager2 = "1.1.0" +bouncycastle = "1.69" mpandroidchart = "v3.1.0" -kotlin = "1.9.0" +kotlin = "1.9.22" square-okhttp = "4.10.0" # Test -androidx-test-core = "1.4.0" +androidx-test-core = "1.6.1" junit = "4.13.2" -mockito-core = "3.9.0" +mockito-core = "4.8.1" robolectric = "4.10.2" [libraries] android-view-material = { group = "com.google.android.material", name = "material", version.ref = "android-view-material" } androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidx-annotation" } +androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidx-activity"} +androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "androidx-activity"} androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } androidx-cardview = { group = "androidx.cardview", name = "cardview", version.ref = "androidx-cardview" } +androidx-viewpager2 = { group = "androidx.viewpager2", name = "viewpager2", version.ref= "androidx-viewpager2" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } androidx-core-ktx = { group = "androidx.core", name = "core", version.ref = "androidx-core-ktx" } androidx-fragment-ktx = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment-ktx" } @@ -55,4 +60,4 @@ robolectric = { group = "org.robolectric", name = "robolectric", version.ref = " [plugins] android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" } -kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
\ No newline at end of file +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdfaf7d3..618ee5cc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,6 @@ -#Wed Jun 03 01:41:27 CEST 2020 +#Tue Jul 23 16:10:00 CEST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip -distributionSha256Sum=7c3ad722e9b0ce8205b91560fd6ce8296ac3eadf065672242fd73c06b8eeb6ee diff --git a/main/build.gradle.kts b/main/build.gradle.kts index 7a7f2c6b..bc8939f3 100644 --- a/main/build.gradle.kts +++ b/main/build.gradle.kts @@ -12,25 +12,26 @@ plugins { } android { - buildToolsVersion = "33.0.1" buildFeatures { aidl = true + buildConfig = true } namespace = "de.blinkt.openvpn" - compileSdk = 34 + compileSdk = 35 //compileSdkPreview = "UpsideDownCake" // Also update runcoverity.sh - ndkVersion = "25.2.9519653" + ndkVersion = "27.0.12077973" defaultConfig { minSdk = 21 - targetSdk = 34 + targetSdk = 35 //targetSdkPreview = "UpsideDownCake" - versionCode = 204 - versionName = "0.7.49" + versionCode = 208 + versionName = "0.7.53" externalNativeBuild { cmake { + //arguments+= "-DCMAKE_VERBOSE_MAKEFILE=1" } } } @@ -235,8 +236,11 @@ dependencies { implementation(libs.androidx.core.ktx) uiImplementation(libs.android.view.material) + uiImplementation(libs.androidx.activity) + uiImplementation(libs.androidx.activity.ktx) uiImplementation(libs.androidx.appcompat) uiImplementation(libs.androidx.cardview) + uiImplementation(libs.androidx.viewpager2) uiImplementation(libs.androidx.constraintlayout) uiImplementation(libs.androidx.core.ktx) uiImplementation(libs.androidx.fragment.ktx) diff --git a/main/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl b/main/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl index 3285432c..39b53106 100644 --- a/main/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl +++ b/main/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl @@ -67,4 +67,10 @@ interface IOpenVPNAPIService { /** Same as startVPN(String), but also takes a Bundle with extra parameters,
* which will be applied to the created VPNProfile (e.g. allow vpn bypass). */
void startVPNwithExtras(in String inlineconfig, in Bundle extras);
+
+ /** Same as addNewVPNProfile(String, boolean, String) but giving possibility to pass a Bundle like
+ * in startVPNwithExtras(String, Bundle) to apply e.g. "allow vpn bypass" to profile.
+ * up to now the only extra that can be put is a boolean "de.blinkt.openvpn.api.ALLOW_VPN_BYPASS"
+ */
+ APIVpnProfile addNewVPNProfileWithExtras (String name, boolean userEditable, String config, in Bundle extras);
}
\ No newline at end of file diff --git a/main/src/main/cpp/asio b/main/src/main/cpp/asio -Subproject 1f8d154829b902dbc45a651587c6c6df948358e +Subproject 12e0ce9e0500bf0f247dbd1ae89427265645607 diff --git a/main/src/main/cpp/openssl b/main/src/main/cpp/openssl -Subproject bf2a6facba08b980a00f2f2c4e46487471b17f6 +Subproject 9d6d3672b1306a3e8991b060d3daf7f2b325576 diff --git a/main/src/main/cpp/openvpn b/main/src/main/cpp/openvpn -Subproject a80f21b133ebf10bc3c36577fedef024c6ed303 +Subproject dad402368d8ac2939196c6a313b7ff0d2267be4 diff --git a/main/src/main/cpp/openvpn3 b/main/src/main/cpp/openvpn3 -Subproject f159710100e1442c7354b4f24cbfe742abc8c89 +Subproject e4368fd83e1ad466492259a3bd6837bd8d8b6cf diff --git a/main/src/main/cpp/ovpnutil/osslutil.cpp b/main/src/main/cpp/ovpnutil/osslutil.cpp index 6a33338e..1921223c 100644 --- a/main/src/main/cpp/ovpnutil/osslutil.cpp +++ b/main/src/main/cpp/ovpnutil/osslutil.cpp @@ -113,9 +113,12 @@ extern "C" jbyteArray Java_de_blinkt_openvpn_core_NativeUtils_rsapss(JNIEnv *env if (!EVP_DigestFinal_ex(ctx, H, nullptr)) goto err; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" /* Generate dbMask in place then perform XOR on it */ if (PKCS1_MGF1(EM, maskedDBLen, H, hLen, mgf1Hash)) goto err; +#pragma clang diagnostic pop p = EM; diff --git a/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java b/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java index 90ea053a..a4a4cc5f 100644 --- a/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java +++ b/main/src/main/java/de/blinkt/openvpn/LaunchVPN.java @@ -5,6 +5,8 @@ package de.blinkt.openvpn; +import static de.blinkt.openvpn.core.OpenVPNService.EXTRA_START_REASON; + import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; @@ -73,7 +75,7 @@ public class LaunchVPN extends Activity { public static final String EXTRA_KEY = "de.blinkt.openvpn.shortcutProfileUUID"; public static final String EXTRA_NAME = "de.blinkt.openvpn.shortcutProfileName"; public static final String EXTRA_HIDELOG = "de.blinkt.openvpn.showNoLogWindow"; - public static final String EXTRA_START_REASON = "de.blinkt.openvpn.start_reason"; + public static final String CLEARLOG = "clearlogconnect"; @@ -255,7 +257,7 @@ public class LaunchVPN extends Activity { if (!mhideLog && showLogWindow) showLogWindow(); ProfileManager.updateLRU(this, mSelectedProfile); - VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext(), mSelectedProfileReason); + VPNLaunchHelper.startOpenVpn(mSelectedProfile, getBaseContext(), mSelectedProfileReason, true); finish(); } } else if (resultCode == Activity.RESULT_CANCELED) { diff --git a/main/src/main/java/de/blinkt/openvpn/OnBootReceiver.java b/main/src/main/java/de/blinkt/openvpn/OnBootReceiver.java index 58c954c9..7033664b 100644 --- a/main/src/main/java/de/blinkt/openvpn/OnBootReceiver.java +++ b/main/src/main/java/de/blinkt/openvpn/OnBootReceiver.java @@ -36,6 +36,6 @@ public class OnBootReceiver extends BroadcastReceiver { } void launchVPN(VpnProfile profile, Context context) { - VPNLaunchHelper.startOpenVpn(profile, context.getApplicationContext(), "on Boot receiver"); + VPNLaunchHelper.startOpenVpn(profile, context.getApplicationContext(), "on Boot receiver", false); } } diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index 0da09eb0..f105dd56 100644 --- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -5,6 +5,8 @@ package de.blinkt.openvpn; +import static de.blinkt.openvpn.core.OpenVPNService.EXTRA_DO_NOT_REPLACE_RUNNING_VPN; + import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; @@ -31,20 +33,16 @@ import org.spongycastle.util.io.pem.PemWriter; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; -import java.io.FileWriter; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Serializable; import java.io.StringWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.security.*; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; -import java.security.interfaces.RSAPublicKey; import java.security.spec.MGF1ParameterSpec; import java.security.spec.PSSParameterSpec; import java.util.Collection; @@ -67,6 +65,8 @@ public class VpnProfile implements Serializable, Cloneable { transient public static final long MAX_EMBED_FILE_SIZE = 2048 * 1024; // 2048kB // Don't change this, not all parts of the program use this constant public static final String EXTRA_PROFILEUUID = "de.blinkt.openvpn.profileUUID"; + public static final String EXTRA_PROFILE_VERSION = "de.blinkt.openvpn.profileVersion"; + public static final String INLINE_TAG = "[[INLINE]]"; public static final String DISPLAYNAME_TAG = "[[NAME]]"; public static final int MAXLOGLEVEL = 4; @@ -816,14 +816,14 @@ public class VpnProfile implements Serializable, Cloneable { cfg.close(); } - public Intent getStartServiceIntent(Context context, String startReason) { - String prefix = context.getPackageName(); - + public Intent getStartServiceIntent(Context context, String startReason, boolean replace_running_vpn) { Intent intent = new Intent(context, OpenVPNService.class); - intent.putExtra(prefix + ".profileUUID", mUuid.toString()); - intent.putExtra(prefix + ".profileVersion", mVersion); + intent.putExtra(EXTRA_PROFILEUUID, mUuid.toString()); + intent.putExtra(EXTRA_PROFILE_VERSION, mVersion); if (startReason != null) - intent.putExtra(prefix + ".startReason", startReason); + intent.putExtra(OpenVPNService.EXTRA_START_REASON, startReason); + if (!replace_running_vpn) + intent.putExtra(EXTRA_DO_NOT_REPLACE_RUNNING_VPN, true); return intent; } diff --git a/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java b/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java index 6cc170fa..4ccf5bd3 100644 --- a/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java +++ b/main/src/main/java/de/blinkt/openvpn/activities/DisconnectVPN.java @@ -86,7 +86,7 @@ public class DisconnectVPN extends Activity implements DialogInterface.OnClickLi } else if (which == DialogInterface.BUTTON_NEUTRAL) { Intent intent = new Intent(this, LaunchVPN.class); intent.putExtra(LaunchVPN.EXTRA_KEY, VpnStatus.getLastConnectedVPNProfile()); - intent.putExtra(LaunchVPN.EXTRA_START_REASON, "Reconnect button pressed."); + intent.putExtra(OpenVPNService.EXTRA_START_REASON, "Reconnect button pressed."); intent.setAction(Intent.ACTION_MAIN); startActivity(intent); } diff --git a/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java b/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java index 53919216..9e3764e3 100644 --- a/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java +++ b/main/src/main/java/de/blinkt/openvpn/api/AppRestrictions.java @@ -14,7 +14,6 @@ import android.text.TextUtils; import de.blinkt.openvpn.VpnProfile; import de.blinkt.openvpn.core.ConfigParser; -import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.Preferences; import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.core.VpnStatus; @@ -147,6 +146,20 @@ public class AppRestrictions { editor.putBoolean("screenoff", pauseVPN); editor.apply(); } + if(restrictions.containsKey("ignorenetworkstate")) + { + boolean ignoreNetworkState = restrictions.getBoolean("ignorenetworkstate"); + SharedPreferences.Editor editor = defaultPrefs.edit(); + editor.putBoolean("ignorenetstate", ignoreNetworkState); + editor.apply(); + } + if (restrictions.containsKey("restartvpnonboot")) + { + boolean restartVPNonBoot = restrictions.getBoolean("restartvpnonboot"); + SharedPreferences.Editor editor = defaultPrefs.edit(); + editor.putBoolean("restartvpnonboot", restartVPNonBoot); + editor.apply(); + } } private void importVPNProfiles(Context c, Bundle restrictions, Parcelable[] profileList) { @@ -155,6 +168,7 @@ public class AppRestrictions { String defaultprofile = restrictions.getString("defaultprofile", null); boolean defaultprofileProvisioned = false; + ProfileManager pm = ProfileManager.getInstance(c); for (Parcelable profile : profileList) { if (!(profile instanceof Bundle)) { diff --git a/main/src/main/java/de/blinkt/openvpn/api/ExternalOpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/api/ExternalOpenVPNService.java index ab71f00b..304a1f4a 100644 --- a/main/src/main/java/de/blinkt/openvpn/api/ExternalOpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/api/ExternalOpenVPNService.java @@ -6,7 +6,6 @@ package de.blinkt.openvpn.api;
import android.annotation.TargetApi;
-import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -14,9 +13,6 @@ import android.content.Context; import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.net.VpnService;
import android.os.Binder;
import android.os.Build;
@@ -146,15 +142,22 @@ public class ExternalOpenVPNService extends Service implements StateListener { shortVPNIntent.setClass(getBaseContext(), de.blinkt.openvpn.LaunchVPN.class);
shortVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_KEY, vp.getUUIDString());
shortVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_HIDELOG, true);
- shortVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_START_REASON, startReason);
+ shortVPNIntent.putExtra(de.blinkt.openvpn.core.OpenVPNService.EXTRA_START_REASON, startReason);
shortVPNIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(shortVPNIntent);
} else {
- VPNLaunchHelper.startOpenVpn(vp, getBaseContext(), startReason);
+ VPNLaunchHelper.startOpenVpn(vp, getBaseContext(), startReason, true);
}
}
+ private void updateProfileFromExtras(Bundle extras, VpnProfile vp) {
+ if (extras != null) {
+ vp.mAllowAppVpnBypass = extras.getBoolean(EXTRA_INLINE_PROFILE_ALLOW_VPN_BYPASS, false);
+ VpnStatus.logDebug("got extra " + EXTRA_INLINE_PROFILE_ALLOW_VPN_BYPASS + ", mAllowAppVpnBypass=" + vp.mAllowAppVpnBypass);
+ }
+ }
+
@Override
public void startProfile(String profileUUID) throws RemoteException {
mExtAppDb.checkOpenVPNPermission(getPackageManager());
@@ -180,9 +183,7 @@ public class ExternalOpenVPNService extends Service implements StateListener { vp.mProfileCreator = callingApp;
- if (extras != null) {
- vp.mAllowAppVpnBypass = extras.getBoolean(EXTRA_INLINE_PROFILE_ALLOW_VPN_BYPASS, false);
- }
+ updateProfileFromExtras(extras, vp);
/*int needpw = vp.needUserPWInput(false);
if(needpw !=0)
@@ -211,6 +212,11 @@ public class ExternalOpenVPNService extends Service implements StateListener { @Override
public APIVpnProfile addNewVPNProfile(String name, boolean userEditable, String config) throws RemoteException {
+ return addNewVPNProfileWithExtras(name, userEditable, config, null);
+ }
+
+ @Override
+ public APIVpnProfile addNewVPNProfileWithExtras(String name, boolean userEditable, String config, Bundle extras) throws RemoteException {
String callingPackage = mExtAppDb.checkOpenVPNPermission(getPackageManager());
ConfigParser cp = new ConfigParser();
@@ -220,6 +226,7 @@ public class ExternalOpenVPNService extends Service implements StateListener { vp.mName = name;
vp.mProfileCreator = callingPackage;
vp.mUserEditable = userEditable;
+ updateProfileFromExtras(extras, vp);
ProfileManager pm = ProfileManager.getInstance(getBaseContext());
pm.addProfile(vp);
pm.saveProfile(ExternalOpenVPNService.this, vp);
diff --git a/main/src/main/java/de/blinkt/openvpn/api/RemoteAction.java b/main/src/main/java/de/blinkt/openvpn/api/RemoteAction.java index 22110ad0..55322e7f 100644 --- a/main/src/main/java/de/blinkt/openvpn/api/RemoteAction.java +++ b/main/src/main/java/de/blinkt/openvpn/api/RemoteAction.java @@ -93,7 +93,7 @@ public class RemoteAction extends Activity { } else { Intent startVPN = new Intent(this, LaunchVPN.class); startVPN.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); - startVPN.putExtra(LaunchVPN.EXTRA_START_REASON, ".api.ConnectVPN call"); + startVPN.putExtra(OpenVPNService.EXTRA_START_REASON, ".api.ConnectVPN call"); startVPN.setAction(Intent.ACTION_MAIN); startActivity(startVPN); } diff --git a/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java b/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java index 8c5772a8..51a84875 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java +++ b/main/src/main/java/de/blinkt/openvpn/core/DeviceStateReceiver.java @@ -14,14 +14,11 @@ import android.net.NetworkInfo; import android.net.NetworkInfo.State;
import android.os.Handler;
import android.os.Looper;
-import android.preference.PreferenceManager;
import de.blinkt.openvpn.R;
import de.blinkt.openvpn.core.VpnStatus.ByteCountListener;
import java.util.LinkedList;
-import java.util.Objects;
-import java.util.StringTokenizer;
import static de.blinkt.openvpn.core.OpenVPNManagement.pauseReason;
@@ -38,7 +35,6 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL // Time to wait after network disconnect to pause the VPN
private final int DISCONNECT_WAIT = 20;
-
connectState network = connectState.DISCONNECTED;
connectState screen = connectState.SHOULDBECONNECTED;
connectState userpause = connectState.SHOULDBECONNECTED;
@@ -179,12 +175,16 @@ public class DeviceStateReceiver extends BroadcastReceiver implements ByteCountL return (a == null) ? (b == null) : a.equals(b);
}
-
public void networkStateChange(Context context) {
- NetworkInfo networkInfo = getCurrentNetworkInfo(context);
SharedPreferences prefs = Preferences.getDefaultSharedPreferences(context);
- boolean sendusr1 = prefs.getBoolean("netchangereconnect", true);
+ boolean ignoreNetworkState = prefs.getBoolean("ignorenetstate", false);
+ if (ignoreNetworkState) {
+ network = connectState.SHOULDBECONNECTED;
+ return;
+ }
+ NetworkInfo networkInfo = getCurrentNetworkInfo(context);
+ boolean sendusr1 = prefs.getBoolean("netchangereconnect", true);
String netstatestring;
if (networkInfo == null) {
diff --git a/main/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java b/main/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java index 39151646..0da8abbb 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java +++ b/main/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java @@ -215,8 +215,9 @@ public class ExtAuthHelper { Intent intent = new Intent(ACTION_CERT_PROVIDER); intent.setPackage(packagename); - if (!context.bindService(intent, extAuthServiceConnection, Context.BIND_AUTO_CREATE)) { - throw new KeyChainException("could not bind to external authticator app: " + packagename); + if (!context.bindService(intent, extAuthServiceConnection, + Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_ACTIVITY_STARTS)) { + throw new KeyChainException("could not bind to external authenticator app: " + packagename); } return new ExternalAuthProviderConnection(context, extAuthServiceConnection, q.take()); } diff --git a/main/src/main/java/de/blinkt/openvpn/core/LogItem.java b/main/src/main/java/de/blinkt/openvpn/core/LogItem.java index 65714c43..3ec2e438 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/LogItem.java +++ b/main/src/main/java/de/blinkt/openvpn/core/LogItem.java @@ -15,6 +15,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import androidx.annotation.StringRes; + import java.io.ByteArrayInputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Array; @@ -239,7 +241,7 @@ public class LogItem implements Parcelable { } }; - public LogItem(VpnStatus.LogLevel loglevel, int ressourceId, Object... args) { + public LogItem(VpnStatus.LogLevel loglevel, @StringRes int ressourceId, Object... args) { mRessourceId = ressourceId; mArgs = args; mLevel = loglevel; 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 0e8793e4..0ca3b167 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -5,17 +5,19 @@ package de.blinkt.openvpn.core; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static de.blinkt.openvpn.VpnProfile.EXTRA_PROFILEUUID; +import static de.blinkt.openvpn.VpnProfile.EXTRA_PROFILE_VERSION; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; import static de.blinkt.openvpn.core.NetworkSpace.IpAddress; import android.Manifest.permission; +import android.annotation.TargetApi; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.UiModeManager; -import android.app.job.JobInfo; -import android.app.job.JobScheduler; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -37,8 +39,8 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; -import android.os.PersistableBundle; import android.os.RemoteException; +import android.service.notification.StatusBarNotification; import android.system.OsConstants; import android.text.TextUtils; import android.util.Base64; @@ -72,6 +74,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac public static final String START_SERVICE = "de.blinkt.openvpn.START_SERVICE"; public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY"; public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE"; + + public static final String EXTRA_DO_NOT_REPLACE_RUNNING_VPN = "de.blinkt.openvpn.DO_NOT_REPLACE_RUNNING_VPN"; + + public static final String EXTRA_START_REASON = "de.blinkt.openvpn.startReason"; + public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN"; public static final String NOTIFICATION_CHANNEL_BG_ID = "openvpn_bg"; public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "openvpn_newstat"; @@ -88,6 +95,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private static final int PRIORITY_MAX = 2; private static boolean mNotificationAlwaysVisible = false; + static class TunConfig { private final Vector<String> mDnslist = new Vector<>(); private final NetworkSpace mRoutes = new NetworkSpace(); @@ -107,7 +115,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private Thread mProcessThread = null; private VpnProfile mProfile; - private DeviceStateReceiver mDeviceStateReceiver; private boolean mDisplayBytecount = false; private boolean mStarting = false; @@ -211,7 +218,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac @Override public void challengeResponse(String response) throws RemoteException { if (mManagement != null) { - String b64response = Base64.encodeToString(response.getBytes(Charset.forName("UTF-8")), Base64.DEFAULT); + String b64response = Base64.encodeToString(response.getBytes(Charset.forName("UTF-8")), Base64.NO_WRAP); mManagement.sendCRResponse(b64response); } } @@ -302,7 +309,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac nbuilder.setOngoing(true); nbuilder.setSmallIcon(icon); - if (status == LEVEL_WAITING_FOR_USER_INPUT) { + if (status == LEVEL_WAITING_FOR_USER_INPUT && intent != null) { PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); nbuilder.setContentIntent(pIntent); } else { @@ -528,12 +535,16 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac return START_REDELIVER_INTENT; } + // Always show notification here to avoid problem with startForeground timeout VpnStatus.logInfo(R.string.building_configration); - VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START); - showNotification(VpnStatus.getLastCleanLogMessage(this), - VpnStatus.getLastCleanLogMessage(this), NOTIFICATION_CHANNEL_NEWSTATUS_ID, 0, ConnectionStatus.LEVEL_START, null); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M || (!foregroundNotificationVisible())) { + + VpnStatus.updateStateString("VPN_GENERATE_CONFIG", "", R.string.building_configration, ConnectionStatus.LEVEL_START); + showNotification(VpnStatus.getLastCleanLogMessage(this), + VpnStatus.getLastCleanLogMessage(this), NOTIFICATION_CHANNEL_NEWSTATUS_ID, 0, ConnectionStatus.LEVEL_START, null); + } /* start the OpenVPN process itself in a background thread */ mCommandHandler.post(() -> startOpenVPN(intent, startId)); @@ -541,6 +552,14 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac return START_STICKY; } + @TargetApi(Build.VERSION_CODES.M) + private boolean foregroundNotificationVisible() { + NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + StatusBarNotification[] notifications = mNotificationManager.getActiveNotifications(); + /* Assume for simplicity that all our notifications are foreground */ + return notifications.length > 0; + } + @RequiresApi(Build.VERSION_CODES.N_MR1) private void updateShortCutUsage(VpnProfile profile) { if (profile == null) @@ -555,47 +574,76 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac private VpnProfile fetchVPNProfile(Intent intent) { + VpnProfile vpnProfile = null; String startReason; - if (intent != null && intent.hasExtra(getPackageName() + ".profileUUID")) { - String profileUUID = intent.getStringExtra(getPackageName() + ".profileUUID"); - int profileVersion = intent.getIntExtra(getPackageName() + ".profileVersion", 0); - startReason = intent.getStringExtra(getPackageName() + ".startReason"); + if (intent != null && intent.hasExtra(EXTRA_PROFILEUUID)) { + String profileUUID = intent.getStringExtra(EXTRA_PROFILEUUID); + int profileVersion = intent.getIntExtra(EXTRA_PROFILE_VERSION, 0); + startReason = intent.getStringExtra(EXTRA_START_REASON); if (startReason == null) startReason = "(unknown)"; // Try for 10s to get current version of the profile - mProfile = ProfileManager.get(this, profileUUID, profileVersion, 100); + vpnProfile = ProfileManager.get(this, profileUUID, profileVersion, 100); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) { - updateShortCutUsage(mProfile); + updateShortCutUsage(vpnProfile); } } else { /* The intent is null when we are set as always-on or the service has been restarted. */ - mProfile = ProfileManager.getLastConnectedProfile(this); + vpnProfile = ProfileManager.getLastConnectedProfile(this); startReason = "Using last connected profile (started with null intent, always-on or restart after crash)"; VpnStatus.logInfo(R.string.service_restarted); /* Got no profile, just stop */ - if (mProfile == null) { + if (vpnProfile == null) { startReason = "could not get last connected profile, using default (started with null intent, always-on or restart after crash)"; Log.d("OpenVPN", "Got no last connected profile on null intent. Assuming always on."); - mProfile = ProfileManager.getAlwaysOnVPN(this); + vpnProfile = ProfileManager.getAlwaysOnVPN(this); - if (mProfile == null) { + if (vpnProfile == null) { return null; } } /* Do the asynchronous keychain certificate stuff */ - mProfile.checkForRestart(this); + vpnProfile.checkForRestart(this); } String name = "(null)"; - if (mProfile != null) - name = mProfile.getName(); + if (vpnProfile != null) + name = vpnProfile.getName(); VpnStatus.logDebug(String.format("Fetched VPN profile (%s) triggered by %s", name, startReason)); - return mProfile; + return vpnProfile; } + private boolean checkVPNPermission(VpnProfile startprofile) { + if (prepare(this) == null) + return true; + + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + + Notification.Builder nbuilder = new Notification.Builder(this); + nbuilder.setAutoCancel(true); + int icon = android.R.drawable.ic_dialog_info; + nbuilder.setSmallIcon(icon); + + Intent launchVPNIntent = new Intent(this, LaunchVPN.class); + launchVPNIntent.putExtra(LaunchVPN.EXTRA_KEY, startprofile.getUUIDString()); + launchVPNIntent.putExtra(EXTRA_START_REASON, "OpenService lacks permission"); + launchVPNIntent.putExtra(de.blinkt.openvpn.LaunchVPN.EXTRA_HIDELOG, true); + launchVPNIntent.addFlags(FLAG_ACTIVITY_NEW_TASK); + launchVPNIntent.setAction(Intent.ACTION_MAIN); + + + showNotification(getString(R.string.permission_requested), + "", NOTIFICATION_CHANNEL_USERREQ_ID, 0, LEVEL_WAITING_FOR_USER_INPUT, launchVPNIntent); + + VpnStatus.updateStateString("USER_INPUT", "waiting for user input", R.string.permission_requested, LEVEL_WAITING_FOR_USER_INPUT, launchVPNIntent); + return false; + } + + + private void startOpenVPN(Intent intent, int startId) { VpnProfile vp = fetchVPNProfile(intent); if (vp == null) { @@ -603,8 +651,26 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac return; } - ProfileManager.setConnectedVpnProfile(this, mProfile); - VpnStatus.setConnectedVPNProfile(mProfile.getUUIDString()); + if (!checkVPNPermission(vp)) + return; + + boolean noReplaceRequested = (intent != null) && intent.getBooleanExtra(EXTRA_DO_NOT_REPLACE_RUNNING_VPN, false); + + + /* we get an empty start request or explicitly get told to not replace the VPN then ignore + * a start request. This avoids OnBootreciver, Always and user quickly clicking to have + * weird race conditions + */ + if (mProfile != null && mProfile == vp && (intent == null || noReplaceRequested)) + { + /* we do not want to replace the running VPN */ + VpnStatus.logInfo(R.string.ignore_vpn_start_request, mProfile.getName()); + return; + } + + mProfile = vp; + ProfileManager.setConnectedVpnProfile(this, vp); + VpnStatus.setConnectedVPNProfile(vp.getUUIDString()); keepVPNAlive.scheduleKeepVPNAliveJobService(this, vp); String nativeLibraryDirectory = getApplicationInfo().nativeLibraryDir; @@ -1353,7 +1419,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac } public void trigger_sso(String info) { - String channel = NOTIFICATION_CHANNEL_USERREQ_ID; String method = info.split(":", 2)[0]; NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); @@ -1414,12 +1479,15 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac // updateStateString trigger the notification of the VPN to be refreshed, save this intent // to have that notification also this intent to be set PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE); + VpnStatus.updateStateString("USER_INPUT", "waiting for user input", reason, LEVEL_WAITING_FOR_USER_INPUT, intent); + nbuilder.setContentIntent(pIntent); jbNotificationExtras(PRIORITY_MAX, nbuilder); lpNotificationExtras(nbuilder, Notification.CATEGORY_STATUS); + String channel = NOTIFICATION_CHANNEL_USERREQ_ID; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //noinspection NewApi nbuilder.setChannelId(channel); diff --git a/main/src/main/java/de/blinkt/openvpn/core/StatusListener.java b/main/src/main/java/de/blinkt/openvpn/core/StatusListener.java index 293a6fd4..f7aef095 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/StatusListener.java +++ b/main/src/main/java/de/blinkt/openvpn/core/StatusListener.java @@ -26,6 +26,7 @@ import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.Locale; /** * Created by arne on 09.11.16. @@ -120,9 +121,10 @@ public class StatusListener implements VpnStatus.LogListener { }; private Context mContext; + private String pkgName = "(packageName not yet set)"; void init(Context c) { - + pkgName = c.getPackageName(); Intent intent = new Intent(c, OpenVPNStatusService.class); intent.setAction(OpenVPNService.START_SERVICE); mCacheDir = c.getCacheDir(); @@ -163,23 +165,19 @@ public class StatusListener implements VpnStatus.LogListener { @Override public void newLog(LogItem logItem) { + String tag = pkgName + "(OpenVPN)"; + long logAge = System.currentTimeMillis() - logItem.getLogtime(); + if (logAge > 5000) + { + tag += String.format(Locale.US, "[%ds ago]", logAge/1000 ); + } + switch (logItem.getLogLevel()) { - case INFO: - Log.i("OpenVPN", logItem.getString(mContext)); - break; - case DEBUG: - Log.d("OpenVPN", logItem.getString(mContext)); - break; - case ERROR: - Log.e("OpenVPN", logItem.getString(mContext)); - break; - case VERBOSE: - Log.v("OpenVPN", logItem.getString(mContext)); - break; - case WARNING: - default: - Log.w("OpenVPN", logItem.getString(mContext)); - break; + case INFO -> Log.i(tag, logItem.getString(mContext)); + case DEBUG -> Log.d(tag, logItem.getString(mContext)); + case ERROR -> Log.e(tag, logItem.getString(mContext)); + case VERBOSE -> Log.v(tag, logItem.getString(mContext)); + default -> Log.w(tag, logItem.getString(mContext)); } } 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 bc04bc5e..0784361c 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VPNLaunchHelper.java @@ -100,8 +100,8 @@ public class VPNLaunchHelper { } - public static void startOpenVpn(VpnProfile startprofile, Context context, String startReason) { - Intent startVPN = startprofile.getStartServiceIntent(context, startReason); + public static void startOpenVpn(VpnProfile startprofile, Context context, String startReason, boolean replace_running_vpn) { + Intent startVPN = startprofile.getStartServiceIntent(context, startReason, replace_running_vpn); if (startVPN != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) //noinspection NewApi 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 d1814fc2..fb55cba3 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java +++ b/main/src/main/java/de/blinkt/openvpn/core/VpnStatus.java @@ -11,6 +11,8 @@ import android.os.Build; import android.os.HandlerThread; import android.os.Message; +import androidx.annotation.StringRes; + import java.io.File; import java.io.PrintWriter; import java.io.StringWriter; @@ -408,11 +410,11 @@ public class VpnStatus { newLogItem(new LogItem(LogLevel.DEBUG, message)); } - public static void logInfo(int resourceId, Object... args) { + public static void logInfo(@StringRes int resourceId, Object... args) { newLogItem(new LogItem(LogLevel.INFO, resourceId, args)); } - public static void logDebug(int resourceId, Object... args) { + public static void logDebug(@StringRes int resourceId, Object... args) { newLogItem(new LogItem(LogLevel.DEBUG, resourceId, args)); } @@ -486,7 +488,7 @@ public class VpnStatus { } - public static void logWarning(int resourceId, Object... args) { + public static void logWarning(@StringRes int resourceId, Object... args) { newLogItem(new LogItem(LogLevel.WARNING, resourceId, args)); } @@ -495,11 +497,11 @@ public class VpnStatus { } - public static void logError(int resourceId) { + public static void logError(@StringRes int resourceId) { newLogItem(new LogItem(LogLevel.ERROR, resourceId)); } - public static void logError(int resourceId, Object... args) { + public static void logError(@StringRes int resourceId, Object... args) { newLogItem(new LogItem(LogLevel.ERROR, resourceId, args)); } diff --git a/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java b/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java index b4264aba..130dc68c 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java +++ b/main/src/main/java/de/blinkt/openvpn/core/keepVPNAlive.java @@ -49,7 +49,7 @@ public class keepVPNAlive extends JobService implements VpnStatus.StateListener unscheduleKeepVPNAliveJobService(this); return false; } - VPNLaunchHelper.startOpenVpn(vp, getApplicationContext(), "VPN keep alive Job"); + VPNLaunchHelper.startOpenVpn(vp, getApplicationContext(), "VPN keep alive Job", false); } else { VpnStatus.logDebug("Keepalive service called but VPN still connected."); } diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml index 575933ed..53893699 100755 --- a/main/src/main/res/values/strings.xml +++ b/main/src/main/res/values/strings.xml @@ -508,5 +508,8 @@ <string name="encrypt_profiles">Try to encrypt profiles on storage (if supported by Android OS)</string> <string name="missing_notification_permission">Notification permission missing. This is used to display the status of the VPN and to notify about required user interaction like multi factor authorisation.\n\nClick this message to give the app notification permissions</string> <string name="proxy_auth_username">Username</string> - + <string name="permission_requested">Permission to start a VPN connection is required</string> + <string name="missing_vpn_permission_log">VPN Service is missing permission to connect a VPN. Requesting permission via notification.</string> + <string name="ignore_vpn_start_request">VPN already running (%s). Ignoring request to start VPN.</string> + <string name="name_of_the_vpn_profile">Name of the VPN Profile</string> </resources> diff --git a/main/src/main/res/values/untranslatable.xml b/main/src/main/res/values/untranslatable.xml index 6b3c1f70..5caa853d 100644 --- a/main/src/main/res/values/untranslatable.xml +++ b/main/src/main/res/values/untranslatable.xml @@ -85,6 +85,8 @@ <string name="faq_title_ncp">Failed to negotiate cipher with server</string> <string name="import_from_URL">URL</string> <string name="restriction_pausevpn">Pause VPN when screen is off and less than 64 kB transferred data in 60s</string> + <string name="restriction_restartvpnonboot">Enable the workaround to use an on boot receiver to start the VPN if the Always On VPN functionality is not available</string> + <string name="restriction_ignorenetworkstate">Keep the VPN connected even when no network is detected, e.g. when reverse tethering over USB using adb</string> <string name="apprest_aidl_list">List of apps that are allowed to use the remote AIDL. If this list is in the restrictions, the app will not allowed any changes to the list by the user. Package names of allowed apps separated by comma, space or newlines</string> <string name="apprest_remoteaidl">Remote API access</string> diff --git a/main/src/main/res/xml/app_restrictions.xml b/main/src/main/res/xml/app_restrictions.xml index b3258b50..9843fada 100644 --- a/main/src/main/res/xml/app_restrictions.xml +++ b/main/src/main/res/xml/app_restrictions.xml @@ -73,6 +73,15 @@ android:restrictionType="bool" android:title="@string/restriction_pausevpn" /> <restriction + android:key="restartvpnonboot" + android:restrictionType="bool" + android:title="@string/restriction_restartvpnonboot" /> + <restriction + android:key="ignorenetworkstate" + android:restrictionType="bool" + android:title="@string/restriction_ignorenetworkstate" /> + + <restriction android:description="@string/apprest_aidl_list" android:key="allowed_remote_access" android:restrictionType="string" diff --git a/main/src/ui/java/de/blinkt/openvpn/OpenVPNTileService.java b/main/src/ui/java/de/blinkt/openvpn/OpenVPNTileService.java index 94c1f1db..09593047 100644 --- a/main/src/ui/java/de/blinkt/openvpn/OpenVPNTileService.java +++ b/main/src/ui/java/de/blinkt/openvpn/OpenVPNTileService.java @@ -85,7 +85,7 @@ public class OpenVPNTileService extends TileService implements VpnStatus.StateLi @SuppressLint("Override") @TargetApi(Build.VERSION_CODES.N) void launchVPN(VpnProfile profile, Context context) { - VPNLaunchHelper.startOpenVpn(profile, getBaseContext(), "QuickTile"); + VPNLaunchHelper.startOpenVpn(profile, getBaseContext(), "QuickTile", true); } @TargetApi(Build.VERSION_CODES.N) diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java deleted file mode 100644 index cca8b155..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (c) 2012-2015 Arne Schwabe - * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt - */ - -package de.blinkt.openvpn.activities; - -import android.app.UiModeManager; -import android.content.Context; -import android.content.res.Configuration; -import android.os.Bundle; -import android.view.Window; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; - -import de.blinkt.openvpn.core.LocaleHelper; - -public abstract class BaseActivity extends AppCompatActivity { - boolean isAndroidTV() { - final UiModeManager uiModeManager = (UiModeManager) getSystemService(UI_MODE_SERVICE); - if (uiModeManager == null) - return false; - return uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - if (isAndroidTV()) { - requestWindowFeature(Window.FEATURE_OPTIONS_PANEL); - } - super.onCreate(savedInstanceState); - } - - @Override - protected void attachBaseContext(Context base) { - super.attachBaseContext(LocaleHelper.updateResources(base)); - } - - @Override - public void onConfigurationChanged(@NonNull Configuration newConfig) { - super.onConfigurationChanged(newConfig); - LocaleHelper.onConfigurationChange(this); - } -} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt new file mode 100644 index 00000000..9e6dd462 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012-2015 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ +package de.blinkt.openvpn.activities + +import android.app.UiModeManager +import android.content.Context +import android.content.res.Configuration +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.view.Window +import androidx.activity.SystemBarStyle +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePadding +import de.blinkt.openvpn.R +import de.blinkt.openvpn.core.LocaleHelper + +abstract class BaseActivity : AppCompatActivity() { + val isAndroidTV: Boolean + get() { + val uiModeManager = getSystemService(UI_MODE_SERVICE) as UiModeManager + return uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION + } + + override fun onCreate(savedInstanceState: Bundle?) { + if (isAndroidTV) { + requestWindowFeature(Window.FEATURE_OPTIONS_PANEL) + } + this.enableEdgeToEdge(SystemBarStyle.dark(R.color.primary_dark)) + super.onCreate(savedInstanceState) + } + + fun setUpEdgeEdgeInsetsListener( + rootView: View, + contentViewId: Int = R.id.root_linear_layout, + setupBottom: Boolean = true + ) { + val contentView = rootView.findViewById<View>(contentViewId) + + ViewCompat.setOnApplyWindowInsetsListener(contentView) { v, windowInsets -> + val insets = + windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()) + val statusbarbg = findViewById<View>(R.id.statusbar_background); + + val statusBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()) + + statusbarbg.layoutParams.height = statusBarInsets.top + + + v.updateLayoutParams<ViewGroup.MarginLayoutParams> { + topMargin = insets.top + } + + v.updatePadding( + left = insets.left, + right = insets.right, + ) + if (setupBottom) { + v.updatePadding(bottom = insets.bottom) + WindowInsetsCompat.CONSUMED + } else { + windowInsets + } + } + } + + override fun attachBaseContext(base: Context) { + super.attachBaseContext(LocaleHelper.updateResources(base)) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + LocaleHelper.onConfigurationChange(this) + } +} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt index b85eaa26..a80afcab 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/ConfigConverter.kt @@ -618,7 +618,9 @@ class ConfigConverter : BaseActivity(), FileSelectCallback, View.OnClickListener override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.config_converter) + val v = layoutInflater.inflate(R.layout.config_converter, null) + setUpEdgeEdgeInsetsListener(v, R.id.root_layout_config_converter) + setContentView(v) val fab_button = findViewById<ImageButton?>(R.id.fab_save) if (fab_button != null) { diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/CreateShortcuts.java b/main/src/ui/java/de/blinkt/openvpn/activities/CreateShortcuts.java index 82455895..3b9312e5 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/CreateShortcuts.java +++ b/main/src/ui/java/de/blinkt/openvpn/activities/CreateShortcuts.java @@ -5,6 +5,8 @@ package de.blinkt.openvpn.activities; +import static de.blinkt.openvpn.core.OpenVPNService.EXTRA_START_REASON; + import android.app.ListActivity; import android.content.Intent; import android.os.Bundle; @@ -126,7 +128,7 @@ public class CreateShortcuts extends ListActivity implements OnItemClickListener Intent shortcutIntent = new Intent(Intent.ACTION_MAIN); shortcutIntent.setClass(this, LaunchVPN.class); shortcutIntent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); - shortcutIntent.putExtra(LaunchVPN.EXTRA_START_REASON, "shortcut"); + shortcutIntent.putExtra(EXTRA_START_REASON, "shortcut"); // Then, set up the container intent (the response to the caller) diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt b/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt index 1a16829e..fec69768 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt @@ -22,7 +22,7 @@ import de.blinkt.openvpn.R import de.blinkt.openvpn.VpnProfile import org.json.JSONObject -class InternalWebView : AppCompatActivity() { +class InternalWebView : BaseActivity() { lateinit var webView: WebView lateinit var urlTextView: TextView @@ -32,6 +32,8 @@ class InternalWebView : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.webview_internal) + setUpEdgeEdgeInsetsListener(getWindow().getDecorView().getRootView(), R.id.container) + webView = findViewById(R.id.internal_webview) urlTextView = findViewById(R.id.url_textview) @@ -94,7 +96,7 @@ class InternalWebView : AppCompatActivity() { } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java b/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java deleted file mode 100644 index 5277a25d..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2012-2016 Arne Schwabe - * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt - */ - -package de.blinkt.openvpn.activities; - -import android.app.Activity; -import android.os.Bundle; -import android.view.MenuItem; - -import de.blinkt.openvpn.R; -import de.blinkt.openvpn.fragments.LogFragment; - -/** - * Created by arne on 13.10.13. - */ -public class LogWindow extends BaseActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.log_window); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - if (savedInstanceState == null) { - getSupportFragmentManager().beginTransaction() - .add(R.id.container, new LogFragment()) - .commit(); - } - - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return super.onOptionsItemSelected(item); - } -} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.kt b/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.kt new file mode 100644 index 00000000..afc18a03 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ +package de.blinkt.openvpn.activities + +import android.os.Bundle +import android.view.MenuItem +import de.blinkt.openvpn.R +import de.blinkt.openvpn.fragments.LogFragment + +/** + * Created by arne on 13.10.13.setUpEdgeEdgeStuff + */ +class LogWindow : BaseActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.log_window) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + if (savedInstanceState == null) { + supportFragmentManager.beginTransaction() + .add(R.id.container, LogFragment()) + .commit() + } + + setUpEdgeEdgeInsetsListener(getWindow().getDecorView().getRootView(), R.id.container) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return super.onOptionsItemSelected(item) + } +} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt index 68117b52..a15de114 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt @@ -10,24 +10,27 @@ import android.os.Bundle import android.view.Menu import android.view.MenuItem import android.widget.Toast -import androidx.viewpager.widget.ViewPager +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator import de.blinkt.openvpn.R import de.blinkt.openvpn.fragments.* import de.blinkt.openvpn.fragments.ImportRemoteConfig.Companion.newInstance import de.blinkt.openvpn.views.ScreenSlidePagerAdapter class MainActivity : BaseActivity() { - private lateinit var mPager: ViewPager + private lateinit var mPager: ViewPager2 private lateinit var mPagerAdapter: ScreenSlidePagerAdapter + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContentView(R.layout.main_activity) - + val view = layoutInflater.inflate(R.layout.main_activity, null) // Instantiate a ViewPager and a PagerAdapter. - mPager = findViewById(R.id.pager) - mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, this) + mPager = view.findViewById(R.id.pager) + val tablayout: TabLayout = view.findViewById(R.id.tab_layout) + + mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, lifecycle, this) /* Toolbar and slider should have the same elevation */disableToolbarElevation() mPagerAdapter.addTab(R.string.vpn_list_title, VPNProfileList::class.java) @@ -41,8 +44,16 @@ class MainActivity : BaseActivity() { mPagerAdapter.addTab(R.string.openvpn_log, LogFragment::class.java) mPagerAdapter.addTab(R.string.about, AboutFragment::class.java) mPager.setAdapter(mPagerAdapter) + + TabLayoutMediator(tablayout, mPager) { tab, position -> + tab.text = mPagerAdapter.getPageTitle(position) + }.attach() + + setUpEdgeEdgeInsetsListener(view, R.id.root_linear_layout) + setContentView(view) } + private fun disableToolbarElevation() { supportActionBar?.elevation = 0f } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/OpenSSLSpeed.kt b/main/src/ui/java/de/blinkt/openvpn/activities/OpenSSLSpeed.kt index 324cd881..e7c6c2db 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/OpenSSLSpeed.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/OpenSSLSpeed.kt @@ -105,6 +105,7 @@ class OpenSSLSpeed : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.openssl_speed) + setUpEdgeEdgeInsetsListener(getWindow().getDecorView().getRootView(), R.id.speed_root) supportActionBar!!.setDisplayHomeAsUpEnabled(true) findViewById<View>(R.id.testSpecific).setOnClickListener { _ -> runAlgorithms(mCipher.text.toString()) } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java deleted file mode 100644 index 2c9eb761..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2012-2016 Arne Schwabe - * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt - */ - -package de.blinkt.openvpn.activities; - -import android.annotation.TargetApi; -import android.app.AlertDialog; -import android.content.Intent; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceActivity; -import android.view.Menu; -import android.view.MenuItem; -import android.widget.Toast; - -import androidx.appcompat.app.ActionBar; -import androidx.viewpager.widget.ViewPager; - -import de.blinkt.openvpn.R; -import de.blinkt.openvpn.VpnProfile; -import de.blinkt.openvpn.core.ProfileManager; -import de.blinkt.openvpn.fragments.Settings_Allowed_Apps; -import de.blinkt.openvpn.fragments.Settings_Authentication; -import de.blinkt.openvpn.fragments.Settings_Basic; -import de.blinkt.openvpn.fragments.Settings_Connections; -import de.blinkt.openvpn.fragments.Settings_IP; -import de.blinkt.openvpn.fragments.Settings_Obscure; -import de.blinkt.openvpn.fragments.Settings_Routing; -import de.blinkt.openvpn.fragments.Settings_UserEditable; -import de.blinkt.openvpn.fragments.ShowConfigFragment; -import de.blinkt.openvpn.fragments.VPNProfileList; -import de.blinkt.openvpn.views.ScreenSlidePagerAdapter; - - -public class VPNPreferences extends BaseActivity { - - static final Class[] validFragments = new Class[]{ - Settings_Authentication.class, Settings_Basic.class, Settings_IP.class, - Settings_Obscure.class, Settings_Routing.class, ShowConfigFragment.class, - Settings_Connections.class, Settings_Allowed_Apps.class, - }; - - private String mProfileUUID; - private VpnProfile mProfile; - private ViewPager mPager; - private ScreenSlidePagerAdapter mPagerAdapter; - - public VPNPreferences() { - super(); - } - - - @TargetApi(Build.VERSION_CODES.KITKAT) - protected boolean isValidFragment(String fragmentName) { - for (Class c: validFragments) - if (c.getName().equals(fragmentName)) - return true; - return false; - - } - - @Override - protected void onStop() { - super.onStop(); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - outState.putString(getIntent().getStringExtra(getPackageName() + ".profileUUID"),mProfileUUID); - super.onSaveInstanceState(outState); - } - - @Override - protected void onResume() { - super.onResume(); - getProfile(); - // When a profile is deleted from a category fragment in hadset mod we need to finish - // this activity as well when returning - if (mProfile==null || mProfile.profileDeleted) { - setResult(VPNProfileList.RESULT_VPN_DELETED); - finish(); - } - if (mProfile.mTemporaryProfile) - { - Toast.makeText(this, "Temporary profiles cannot be edited", Toast.LENGTH_LONG).show(); - finish(); - } - } - - private void getProfile() { - Intent intent = getIntent(); - - if(intent!=null) { - String profileUUID = intent.getStringExtra(getPackageName() + ".profileUUID"); - if(profileUUID==null) { - Bundle initialArguments = getIntent().getBundleExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS); - profileUUID = initialArguments.getString(getPackageName() + ".profileUUID"); - } - if(profileUUID!=null){ - - mProfileUUID = profileUUID; - mProfile = ProfileManager.get(this, mProfileUUID); - - } - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - mProfileUUID = getIntent().getStringExtra(getPackageName() + ".profileUUID"); - if(savedInstanceState!=null){ - String savedUUID = savedInstanceState.getString(getPackageName() + ".profileUUID"); - if(savedUUID!=null) - mProfileUUID=savedUUID; - } - super.onCreate(savedInstanceState); - - mProfile = ProfileManager.get(this,mProfileUUID); - if(mProfile==null) { - Toast.makeText(this, "Profile to edit cannot be found.", Toast.LENGTH_LONG).show(); - finish(); - return; - } - - setTitle(getString(R.string.edit_profile_title, mProfile.getName())); - - - setContentView(R.layout.main_activity); - - disableToolbarElevation(); - - // Instantiate a ViewPager and a PagerAdapter. - mPager = findViewById(R.id.pager); - mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager(), this); - - - Bundle fragmentArguments = new Bundle(); - fragmentArguments.putString(getPackageName() + ".profileUUID",mProfileUUID); - mPagerAdapter.setFragmentArgs(fragmentArguments); - - if (mProfile.mUserEditable) { - mPagerAdapter.addTab(R.string.basic, Settings_Basic.class); - mPagerAdapter.addTab(R.string.server_list, Settings_Connections.class); - mPagerAdapter.addTab(R.string.ipdns, Settings_IP.class); - mPagerAdapter.addTab(R.string.routing, Settings_Routing.class); - mPagerAdapter.addTab(R.string.settings_auth, Settings_Authentication.class); - - mPagerAdapter.addTab(R.string.advanced, Settings_Obscure.class); - } else { - mPagerAdapter.addTab(R.string.basic, Settings_UserEditable.class); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mPagerAdapter.addTab(R.string.vpn_allowed_apps, Settings_Allowed_Apps.class); - } - mPagerAdapter.addTab(R.string.generated_config, ShowConfigFragment.class); - - - mPager.setAdapter(mPagerAdapter); - - //TabBarView tabs = (TabBarView) findViewById(R.id.sliding_tabs); - //tabs.setViewPager(mPager); - - } - - - @Override - public void onBackPressed() { - setResult(RESULT_OK, getIntent()); - super.onBackPressed(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == R.id.remove_vpn) - askProfileRemoval(); - if (item.getItemId() == R.id.duplicate_vpn) { - Intent data = new Intent(); - data.putExtra(VpnProfile.EXTRA_PROFILEUUID, mProfileUUID); - setResult(VPNProfileList.RESULT_VPN_DUPLICATE, data); - finish(); - } - - return super.onOptionsItemSelected(item); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - getMenuInflater().inflate(R.menu.vpnpreferences_menu, menu); - - return super.onCreateOptionsMenu(menu); - } - - private void askProfileRemoval() { - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle("Confirm deletion"); - dialog.setMessage(getString(R.string.remove_vpn_query, mProfile.mName)); - - dialog.setPositiveButton(android.R.string.yes, - (dialog1, which) -> removeProfile(mProfile)); - dialog.setNegativeButton(android.R.string.no,null); - dialog.create().show(); - } - - protected void removeProfile(VpnProfile profile) { - ProfileManager.getInstance(this).removeProfile(this,profile); - setResult(VPNProfileList.RESULT_VPN_DELETED); - finish(); - - } - - private void disableToolbarElevation() { - ActionBar toolbar = getSupportActionBar(); - toolbar.setElevation(0); - } - -} diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt new file mode 100644 index 00000000..0dd61748 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ +package de.blinkt.openvpn.activities + +import android.annotation.TargetApi +import android.app.AlertDialog +import android.content.DialogInterface +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.preference.PreferenceActivity +import android.view.Menu +import android.view.MenuItem +import android.widget.Toast +import androidx.viewpager2.widget.ViewPager2 +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayoutMediator +import de.blinkt.openvpn.R +import de.blinkt.openvpn.VpnProfile +import de.blinkt.openvpn.core.ProfileManager +import de.blinkt.openvpn.fragments.Settings_Allowed_Apps +import de.blinkt.openvpn.fragments.Settings_Authentication +import de.blinkt.openvpn.fragments.Settings_Basic +import de.blinkt.openvpn.fragments.Settings_Connections +import de.blinkt.openvpn.fragments.Settings_IP +import de.blinkt.openvpn.fragments.Settings_Obscure +import de.blinkt.openvpn.fragments.Settings_Routing +import de.blinkt.openvpn.fragments.Settings_UserEditable +import de.blinkt.openvpn.fragments.ShowConfigFragment +import de.blinkt.openvpn.fragments.VPNProfileList +import de.blinkt.openvpn.views.ScreenSlidePagerAdapter + +class VPNPreferences : BaseActivity() { + private var mProfileUUID: String? = null + private var mProfile: VpnProfile? = null + private lateinit var mPager: ViewPager2 + private lateinit var mPagerAdapter: ScreenSlidePagerAdapter + + @TargetApi(Build.VERSION_CODES.KITKAT) + protected fun isValidFragment(fragmentName: String): Boolean { + for (c in validFragments) if (c.name == fragmentName) return true + return false + } + + override fun onStop() { + super.onStop() + } + + override fun onSaveInstanceState(outState: Bundle) { + outState.putString(intent.getStringExtra("$packageName.profileUUID"), mProfileUUID) + super.onSaveInstanceState(outState) + } + + override fun onResume() { + super.onResume() + profile + // When a profile is deleted from a category fragment in hadset mod we need to finish + // this activity as well when returning + if (mProfile == null || mProfile!!.profileDeleted) { + setResult(VPNProfileList.RESULT_VPN_DELETED) + finish() + } + if (mProfile!!.mTemporaryProfile) { + Toast.makeText(this, "Temporary profiles cannot be edited", Toast.LENGTH_LONG).show() + finish() + } + } + + private val profile: Unit + get() { + val intent = intent + + if (intent != null) { + var profileUUID = intent.getStringExtra("$packageName.profileUUID") + if (profileUUID == null) { + val initialArguments = + getIntent().getBundleExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS) + profileUUID = initialArguments!!.getString("$packageName.profileUUID") + } + if (profileUUID != null) { + mProfileUUID = profileUUID + mProfile = ProfileManager.get(this, mProfileUUID) + } + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + mProfileUUID = intent.getStringExtra("$packageName.profileUUID") + if (savedInstanceState != null) { + val savedUUID = savedInstanceState.getString("$packageName.profileUUID") + if (savedUUID != null) mProfileUUID = savedUUID + } + super.onCreate(savedInstanceState) + + mProfile = ProfileManager.get(this, mProfileUUID) + if (mProfile == null) { + Toast.makeText(this, "Profile to edit cannot be found.", Toast.LENGTH_LONG).show() + finish() + return + } + + title = getString(R.string.edit_profile_title, mProfile!!.name) + + + val rootview = layoutInflater.inflate(R.layout.main_activity, null) + setUpEdgeEdgeInsetsListener(rootview, R.id.root_linear_layout) + + disableToolbarElevation() + + // Instantiate a ViewPager and a PagerAdapter. + mPager = rootview.findViewById(R.id.pager) + val tablayout: TabLayout = rootview.findViewById(R.id.tab_layout) + mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, lifecycle, this) + + + val fragmentArguments = Bundle() + fragmentArguments.putString("$packageName.profileUUID", mProfileUUID) + mPagerAdapter.setFragmentArgs(fragmentArguments) + + if (mProfile!!.mUserEditable) { + mPagerAdapter.addTab(R.string.basic, Settings_Basic::class.java) + mPagerAdapter.addTab(R.string.server_list, Settings_Connections::class.java) + mPagerAdapter.addTab(R.string.ipdns, Settings_IP::class.java) + mPagerAdapter.addTab(R.string.routing, Settings_Routing::class.java) + mPagerAdapter.addTab(R.string.settings_auth, Settings_Authentication::class.java) + + mPagerAdapter.addTab(R.string.advanced, Settings_Obscure::class.java) + } else { + mPagerAdapter.addTab(R.string.basic, Settings_UserEditable::class.java) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mPagerAdapter.addTab(R.string.vpn_allowed_apps, Settings_Allowed_Apps::class.java) + } + mPagerAdapter.addTab(R.string.generated_config, ShowConfigFragment::class.java) + + + mPager.setAdapter(mPagerAdapter) + + //TabBarView tabs = (TabBarView) findViewById(R.id.sliding_tabs); + //tabs.setViewPager(mPager); + + TabLayoutMediator(tablayout, mPager) { tab, position -> + tab.text = mPagerAdapter.getPageTitle(position) + }.attach() + + setContentView(rootview) + } + + + override fun onBackPressed() { + setResult(RESULT_OK, intent) + super.onBackPressed() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.remove_vpn) askProfileRemoval() + if (item.itemId == R.id.duplicate_vpn) { + val data = Intent() + data.putExtra(VpnProfile.EXTRA_PROFILEUUID, mProfileUUID) + setResult(VPNProfileList.RESULT_VPN_DUPLICATE, data) + finish() + } + + return super.onOptionsItemSelected(item) + } + + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.vpnpreferences_menu, menu) + + return super.onCreateOptionsMenu(menu) + } + + private fun askProfileRemoval() { + val dialog = AlertDialog.Builder(this) + dialog.setTitle("Confirm deletion") + dialog.setMessage(getString(R.string.remove_vpn_query, mProfile!!.mName)) + + dialog.setPositiveButton( + android.R.string.yes + ) { dialog1: DialogInterface?, which: Int -> removeProfile(mProfile) } + dialog.setNegativeButton(android.R.string.no, null) + dialog.create().show() + } + + protected fun removeProfile(profile: VpnProfile?) { + ProfileManager.getInstance(this).removeProfile(this, profile) + setResult(VPNProfileList.RESULT_VPN_DELETED) + finish() + } + + private fun disableToolbarElevation() { + val toolbar = supportActionBar + toolbar!!.elevation = 0f + } + + companion object { + val validFragments: Array<Class<*>> = arrayOf( + Settings_Authentication::class.java, + Settings_Basic::class.java, + Settings_IP::class.java, + Settings_Obscure::class.java, + Settings_Routing::class.java, + ShowConfigFragment::class.java, + Settings_Connections::class.java, + Settings_Allowed_Apps::class.java, + ) + } +} diff --git a/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java b/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java index ad7a7c28..e4d6b32f 100644 --- a/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java +++ b/main/src/ui/java/de/blinkt/openvpn/core/OpenVPNThreadv3.java @@ -262,7 +262,6 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable void setUserPW() { if (mVp.isUserPWAuth()) { ClientAPI_ProvideCreds creds = new ClientAPI_ProvideCreds(); - creds.setCachePassword(true); creds.setPassword(mVp.getPasswordAuth()); creds.setUsername(mVp.mUsername); provide_creds(creds); @@ -294,7 +293,7 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable @Override public void sendCRResponse(String response) { mHandler.post(() -> { - post_cc_msg("CR_RESPONSE," + response + "\n"); + post_cc_msg("CR_RESPONSE," + response); }); } @@ -312,25 +311,30 @@ public class OpenVPNThreadv3 extends ClientAPI_OpenVPNClient implements Runnable public void event(ClientAPI_Event event) { String name = event.getName(); String info = event.getInfo(); - if (name.equals("INFO")) { - if (info.startsWith("OPEN_URL:") || info.startsWith("CR_TEXT:") - || info.startsWith("WEB_AUTH:")) { - mService.trigger_sso(info); - } else { - VpnStatus.logInfo(R.string.info_from_server, info); + switch (name) { + case "INFO" -> { + if (info.startsWith("OPEN_URL:") || info.startsWith("CR_TEXT:") + || info.startsWith("WEB_AUTH:")) { + mService.trigger_sso(info); + } else { + VpnStatus.logInfo(R.string.info_from_server, info); + } + } + case "COMPRESSION_ENABLED", "WARN" -> + VpnStatus.logInfo(String.format(Locale.US, "%s: %s", name, info)); + case "PAUSE" -> + VpnStatus.updateStateString(name, "VPN connection paused", R.string.state_userpause, ConnectionStatus.LEVEL_VPNPAUSED); + case "RESUME" -> + VpnStatus.updateStateString(name, "VPN connection resumed", R.string.state_reconnecting, ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET); + default -> + { + VpnStatus.updateStateString(name, info); + VpnStatus.logInfo(String.format("EVENT: %s: %s", name, info)); } - } else if (name.equals("COMPRESSION_ENABLED") || name.equals(("WARN"))) { - VpnStatus.logInfo(String.format(Locale.US, "%s: %s", name, info)); - } else { - VpnStatus.updateStateString(name, info); } - /* if (event.name.equals("DYNAMIC_CHALLENGE")) { - ClientAPI_DynamicChallenge challenge = new ClientAPI_DynamicChallenge(); - final boolean status = ClientAPI_OpenVPNClient.parse_dynamic_challenge(event.info, challenge); - - } else */ if (event.getError()) VpnStatus.logError(String.format("EVENT(Error): %s: %s", name, info)); + } @Override diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java index de6c83d8..15375a3f 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/AboutFragment.java @@ -27,6 +27,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; import com.android.vending.billing.IInAppBillingService; @@ -108,6 +111,16 @@ public class AboutFragment extends Fragment implements View.OnClickListener { TextView wv = (TextView) v.findViewById(R.id.full_licenses); wv.setText(Html.fromHtml(readHtmlFromAssets())); + + + + ViewCompat.setOnApplyWindowInsetsListener(v, (view, windowInsets) -> + { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); + view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), insets.bottom); + return WindowInsetsCompat.CONSUMED; + } + ); return v; } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java index dcdfd5e3..2863b242 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/FaqFragment.java @@ -9,6 +9,9 @@ import android.content.Context; import android.os.Build; import android.os.Bundle; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; @@ -196,9 +199,13 @@ public class FaqFragment extends Fragment { mRecyclerView.setAdapter(new FaqViewAdapter(getActivity(), getFAQEntries())); + Utils.applyInsetListener(v); + return v; } + + private FAQEntry[] getFAQEntries() { Vector<FAQEntry> faqItems = new Vector<>(); diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java index c5c48b0e..4a7cf735 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/LogFragment.java @@ -9,11 +9,9 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.app.Activity; -import android.app.PendingIntent; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.res.Resources; import android.database.DataSetObserver; @@ -38,8 +36,6 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemLongClickListener; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.LinearLayout; @@ -538,7 +534,7 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. (dialog1, which) -> { Intent intent = new Intent(getActivity(), LaunchVPN.class); intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUIDString()); - intent.putExtra(LaunchVPN.EXTRA_START_REASON, "restart from logwindow"); + intent.putExtra(OpenVPNService.EXTRA_START_REASON, "restart from logwindow"); intent.setAction(Intent.ACTION_MAIN); startActivity(intent); }); @@ -631,16 +627,26 @@ public class LogFragment extends ListFragment implements StateListener, SeekBar. }); if (mShowOptionsLayout) mOptionsLayout.setVisibility(View.VISIBLE); + +// ViewCompat.setOnApplyWindowInsetsListener(v, (view, windowInsets) -> +// { +// Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); +// view.setPadding(view.getPaddingLeft(), view.getPaddingTop(), view.getPaddingRight(), insets.bottom); +// return WindowInsetsCompat.CONSUMED; +// } +// ); + Utils.applyInsetListener(v); + return v; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - // Scroll to the end of the list end - //getListView().setSelection(getListView().getAdapter().getCount()-1); } + + @Override public void onAttach(@NonNull Context activity) { super.onAttach(activity); diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java b/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java index 802e0b49..0b2ebde2 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/OpenVpnPreferencesFragment.java @@ -52,7 +52,10 @@ public abstract class OpenVpnPreferencesFragment extends PreferenceFragmentCompa @Override public void onSaveInstanceState (Bundle outState) { super.onSaveInstanceState(outState); - saveSettings(); + if (getView() != null) { + //if we have no view, there is no point in trying to save anything. + saveSettings(); + } outState.putString(VpnProfile.EXTRA_PROFILEUUID, mProfile.getUUIDString()); } } diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.kt index 51a4e2e3..944aa41a 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Authentication.kt @@ -69,6 +69,9 @@ class Settings_Authentication : OpenVpnPreferencesFragment(), Preference.OnPrefe } override fun loadSettings() { + if (!this::mExpectTLSCert.isInitialized) { + return; + } mExpectTLSCert.isChecked = mProfile.mExpectTLSCert mCheckRemoteCN.isChecked = mProfile.mCheckRemoteCN mRemoteCN.setDN(mProfile.mRemoteCN) diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Connections.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Connections.kt index 8ed9d9ef..b06c3368 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Connections.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Connections.kt @@ -40,12 +40,8 @@ class Settings_Connections : Settings_Fragment(), View.OnClickListener { val v = inflater.inflate(R.layout.connections, container, false) mWarning = v.findViewById<View>(R.id.noserver_active_warning) as TextView mRecyclerView = v.findViewById<View>(R.id.connection_recycler_view) as RecyclerView - val dpwidth = (container!!.width / resources.displayMetrics.density).toInt() - var columns = dpwidth / 290 - columns = 1.coerceAtLeast(columns) mConnectionsAdapter = ConnectionsAdapter(activity, this, mProfile) - //mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(columns, StaggeredGridLayoutManager.VERTICAL)); mRecyclerView.setHasFixedSize(true) mRecyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.kt index ef69f3f4..fef4861b 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_IP.kt @@ -66,6 +66,10 @@ class Settings_IP : OpenVpnPreferencesFragment(), Preference.OnPreferenceChangeL override fun loadSettings() { + // Since we maybe not have preferences bound yet, check if we actually have them bound. + if (!this::mUsePull.isInitialized) { + return; + } if (mProfile.mAuthenticationType == VpnProfile.TYPE_STATICKEYS) mUsePull.isEnabled = false else mUsePull.isChecked = mProfile.mUsePull mIPv4.text = mProfile.mIPv4Address diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt index 55056424..e41dc337 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt @@ -13,12 +13,14 @@ import android.os.Build import android.provider.OpenableColumns import android.text.SpannableString import android.text.SpannableStringBuilder -import android.text.TextUtils import android.text.style.ForegroundColorSpan import android.util.Base64 +import android.view.View import android.webkit.MimeTypeMap +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import de.blinkt.openvpn.R -import kotlin.Throws import de.blinkt.openvpn.VpnProfile import de.blinkt.openvpn.core.Preferences import java.io.ByteArrayOutputStream @@ -319,4 +321,37 @@ object Utils { } } + @JvmStatic + fun applyInsetListener(v:View) + { + ViewCompat.setOnApplyWindowInsetsListener( + v + ) { view: View, windowInsets: WindowInsetsCompat -> + val insets = + windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()) + view.updatePadding( + bottom = insets.bottom + ) + WindowInsetsCompat.CONSUMED + } + v.requestApplyInsetsWhenAttached() + } +} + +fun View.requestApplyInsetsWhenAttached() { + if (isAttachedToWindow) { + // We're already attached, just request as normal + requestApplyInsets() + } else { + // We're not attached to the hierarchy, add a listener to + // request when we are + addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + v.removeOnAttachStateChangeListener(this) + v.requestApplyInsets() + } + + override fun onViewDetachedFromWindow(v: View) = Unit + }) + } }
\ No newline at end of file diff --git a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java index fa9438cb..28504268 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -68,6 +68,7 @@ import de.blinkt.openvpn.core.VpnStatus; import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT; import static de.blinkt.openvpn.core.OpenVPNService.DISCONNECT_VPN; import static de.blinkt.openvpn.core.OpenVPNService.EXTRA_CHALLENGE_TXT; +import static de.blinkt.openvpn.core.OpenVPNService.EXTRA_START_REASON; public class VPNProfileList extends ListFragment implements OnClickListener, VpnStatus.StateListener { @@ -242,7 +243,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn shortcutIntent.setClass(requireContext(), LaunchVPN.class); shortcutIntent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); shortcutIntent.setAction(Intent.ACTION_MAIN); - shortcutIntent.putExtra(LaunchVPN.EXTRA_START_REASON, "shortcut"); + shortcutIntent.putExtra(EXTRA_START_REASON, "shortcut"); shortcutIntent.putExtra("EXTRA_HIDELOG", true); PersistableBundle versionExtras = new PersistableBundle(); @@ -450,6 +451,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn if (context != null) { final EditText entry = new EditText(context); entry.setSingleLine(); + entry.setContentDescription(getString(R.string.name_of_the_vpn_profile)); AlertDialog.Builder dialog = new AlertDialog.Builder(context); if (mCopyProfile == null) @@ -563,7 +565,7 @@ public class VPNProfileList extends ListFragment implements OnClickListener, Vpn Intent intent = new Intent(getActivity(), LaunchVPN.class); intent.putExtra(LaunchVPN.EXTRA_KEY, profile.getUUID().toString()); - intent.putExtra(LaunchVPN.EXTRA_START_REASON, "main profile list"); + intent.putExtra(EXTRA_START_REASON, "main profile list"); intent.setAction(Intent.ACTION_MAIN); startActivity(intent); } diff --git a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java deleted file mode 100644 index 4f1150a2..00000000 --- a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2012-2016 Arne Schwabe - * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt - */ - -package de.blinkt.openvpn.views; - -import android.content.Context; -import android.content.res.Resources; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.StringRes; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; - -import java.util.Vector; - -/** -* Created by arne on 18.11.14. -*/ -public class ScreenSlidePagerAdapter extends FragmentPagerAdapter { - - private final Resources res; - private Bundle mFragmentArguments; - - public void setFragmentArgs(Bundle fragmentArguments) { - mFragmentArguments = fragmentArguments; - } - - static class Tab { - public Class<? extends Fragment> fragmentClass; - String mName; - - public Tab(Class<? extends Fragment> fClass, String name){ - mName = name; - fragmentClass = fClass; - } - - } - - - private Vector<Tab> mTabs = new Vector<Tab>(); - - public ScreenSlidePagerAdapter(FragmentManager fm, Context c) { - super(fm); - res = c.getResources(); - } - - @NonNull - @Override - public Fragment getItem(int position) { - try { - Fragment fragment = mTabs.get(position).fragmentClass.newInstance(); - if (mFragmentArguments!=null) - fragment.setArguments(mFragmentArguments); - return fragment; - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - return null; - } - - @Override - public CharSequence getPageTitle(int position) { - return mTabs.get(position).mName; - } - - @Override - public int getCount() { - return mTabs.size(); - } - - public void addTab(@StringRes int name, Class<? extends Fragment> fragmentClass) { - mTabs.add(new Tab(fragmentClass, res.getString(name))); - } -} diff --git a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt new file mode 100644 index 00000000..98036953 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012-2016 Arne Schwabe + * Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt + */ +package de.blinkt.openvpn.views + +import android.content.Context +import android.content.res.Resources +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import androidx.annotation.StringRes +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import androidx.core.view.updatePadding +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +import de.blinkt.openvpn.R +import java.util.Vector + +/** + * Created by arne on 18.11.14. + */ +class ScreenSlidePagerAdapter(fm: FragmentManager, lc: Lifecycle, c: Context) : + FragmentStateAdapter( + fm, lc + ) { + private val res: Resources = c.resources + private var mFragmentArguments: Bundle? = null + + fun setFragmentArgs(fragmentArguments: Bundle?) { + mFragmentArguments = fragmentArguments + } + + internal class Tab(var fragmentClass: Class<out Fragment>, var mName: String) + + private val mTabs = Vector<Tab>() + private var mBottomPadding= 0 + + override fun createFragment(position: Int): Fragment { + try { + val fragment = mTabs[position].fragmentClass.newInstance() + if (mFragmentArguments != null) fragment.arguments = mFragmentArguments + + return fragment + } catch (e: InstantiationException) { + e.printStackTrace() + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + throw IndexOutOfBoundsException("index wrong") + } + + fun getPageTitle(position: Int): CharSequence { + return mTabs[position].mName + } + + override fun getItemCount(): Int { + return mTabs.size + } + + fun addTab(@StringRes name: Int, fragmentClass: Class<out Fragment>) { + mTabs.add(Tab(fragmentClass, res.getString(name))) + } + + fun setBottomPadding(bottom: Int) { + mBottomPadding = bottom; + } +} diff --git a/main/src/ui/res/layout/about.xml b/main/src/ui/res/layout/about.xml index cd482996..f73768c7 100644 --- a/main/src/ui/res/layout/about.xml +++ b/main/src/ui/res/layout/about.xml @@ -9,6 +9,7 @@ android:layout_height="match_parent" android:paddingLeft="@dimen/stdpadding" android:paddingRight="@dimen/stdpadding" + android:clipToPadding="false" android:scrollbarStyle="outsideOverlay"> <LinearLayout diff --git a/main/src/ui/res/layout/basic_settings.xml b/main/src/ui/res/layout/basic_settings.xml index 8aa4c811..47f84e5f 100644 --- a/main/src/ui/res/layout/basic_settings.xml +++ b/main/src/ui/res/layout/basic_settings.xml @@ -21,6 +21,7 @@ style="@style/item" android:text="@string/profilename" android:textAppearance="?android:attr/textAppearanceSmall" + android:labelFor="@id/profilename" /> <EditText diff --git a/main/src/ui/res/layout/config_converter.xml b/main/src/ui/res/layout/config_converter.xml index 591de858..311e7557 100644 --- a/main/src/ui/res/layout/config_converter.xml +++ b/main/src/ui/res/layout/config_converter.xml @@ -2,114 +2,124 @@ ~ Copyright (c) 2012-2016 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> - <ScrollView + <RelativeLayout xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/root_layout_config_converter" android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="@dimen/stdpadding" - android:orientation="vertical"> + android:layout_height="match_parent"> - - <LinearLayout - android:id="@+id/config_convert_root" + <ScrollView android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:layout_margin="@dimen/stdpadding" android:orientation="vertical"> - <TextView - android:id="@+id/profilename_label" - style="@style/item" - android:labelFor="@id/profilename" - android:text="@string/profilename" - android:textAppearance="?android:attr/textAppearanceSmall" - android:visibility="gone" /> - - <EditText - android:id="@+id/profilename" - style="@style/item" - android:inputType="text" - android:visibility="gone" /> - - <TextView - android:layout_marginTop="@dimen/stdpadding" - android:id="@+id/compatmode_label" - style="@style/item" - android:text="@string/compat_mode_label" - android:textAppearance="?android:attr/textAppearanceSmall" - android:visibility="gone" - tools:visibility="visible" /> - - <Spinner - android:id="@+id/compatmode" - style="@style/item" - android:entries="@array/compat_mode" - android:prompt="@string/compatmode" - android:visibility="gone" - tools:visibility="visible" /> - - <TextView - android:layout_marginTop="@dimen/stdpadding" - android:id="@+id/tls_profile_label" - style="@style/item" - android:text="@string/tls_profile" - android:textAppearance="?android:attr/textAppearanceSmall" - android:visibility="gone" - tools:visibility="visible" /> - <Spinner - android:id="@+id/tls_profile" - style="@style/item" - android:entries="@array/tls_profile_entries" - android:prompt="@string/compatmode" - android:visibility="gone" - tools:visibility="visible" /> - <TextView - android:id="@+id/files_missing_hint" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/files_missing_hint" - android:visibility="gone" - tools:visibilty="visible" /> - <TextView - android:id="@+id/permssion_hint" + <LinearLayout + android:id="@+id/config_convert_root" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="5dp" - android:text="@string/query_permissions_sdcard" - android:textStyle="bold" - android:visibility="gone" - tools:visibility="visible" /> - - <CheckBox - android:layout_marginTop="@dimen/stdpadding" - android:id="@+id/importpkcs12" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:checked="true" - android:text="@string/importpkcs12fromconfig" - android:visibility="gone" - tools:visibilty="visible" /> - - <TextView - android:id="@+id/textView" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_margin="10dp" - android:text="@string/import_log" - android:textAppearance="?android:attr/textAppearanceMedium" /> + android:orientation="vertical"> + + <TextView + android:id="@+id/profilename_label" + style="@style/item" + android:labelFor="@id/profilename" + android:text="@string/profilename" + android:textAppearance="?android:attr/textAppearanceSmall" + android:visibility="gone" /> + + <EditText + android:id="@+id/profilename" + style="@style/item" + android:inputType="text" + android:visibility="gone" /> + + <TextView + android:id="@+id/compatmode_label" + style="@style/item" + android:layout_marginTop="@dimen/stdpadding" + android:text="@string/compat_mode_label" + android:textAppearance="?android:attr/textAppearanceSmall" + android:visibility="gone" + tools:visibility="visible" /> + + <Spinner + android:id="@+id/compatmode" + style="@style/item" + android:entries="@array/compat_mode" + android:prompt="@string/compatmode" + android:visibility="gone" + tools:visibility="visible" /> + + <TextView + android:id="@+id/tls_profile_label" + style="@style/item" + android:layout_marginTop="@dimen/stdpadding" + android:text="@string/tls_profile" + android:textAppearance="?android:attr/textAppearanceSmall" + android:visibility="gone" + tools:visibility="visible" /> + + <Spinner + android:id="@+id/tls_profile" + style="@style/item" + android:entries="@array/tls_profile_entries" + android:prompt="@string/compatmode" + android:visibility="gone" + tools:visibility="visible" /> + + <TextView + android:id="@+id/files_missing_hint" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/files_missing_hint" + android:visibility="gone" + tools:visibilty="visible" /> + + <TextView + android:id="@+id/permssion_hint" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="5dp" + android:text="@string/query_permissions_sdcard" + android:textStyle="bold" + android:visibility="gone" + tools:visibility="visible" /> + + <CheckBox + android:id="@+id/importpkcs12" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/stdpadding" + android:checked="true" + android:text="@string/importpkcs12fromconfig" + android:visibility="gone" + tools:visibilty="visible" /> + + <TextView + android:id="@+id/textView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="10dp" + android:text="@string/import_log" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + <Space + android:id="@+id/fab_footerspace" + android:layout_width="40dp" + android:layout_height="@dimen/round_button_diameter" + android:visibility="gone" /> + </LinearLayout> + + </ScrollView> - <Space - android:id="@+id/fab_footerspace" - android:layout_width="40dp" - android:layout_height="@dimen/round_button_diameter" - android:visibility="gone" /> - </LinearLayout> + <include layout="@layout/save_fab" /> - </ScrollView> + </RelativeLayout> - <include layout="@layout/save_fab" /> -</RelativeLayout> + <include layout="@layout/status_bg" /> +</FrameLayout> diff --git a/main/src/ui/res/layout/faq.xml b/main/src/ui/res/layout/faq.xml index 8cb79649..46b56e58 100644 --- a/main/src/ui/res/layout/faq.xml +++ b/main/src/ui/res/layout/faq.xml @@ -1,15 +1,15 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (c) 2012-2016 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> <androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/faq_recycler_view" - android:paddingLeft="@dimen/stdpadding" - android:paddingRight="@dimen/stdpadding" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:verticalSpacing="@dimen/stdpadding" - android:horizontalSpacing="@dimen/stdpadding" - />
\ No newline at end of file + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/faq_recycler_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:horizontalSpacing="@dimen/stdpadding" + android:paddingLeft="@dimen/stdpadding" + android:paddingRight="@dimen/stdpadding" + android:verticalSpacing="@dimen/stdpadding" />
\ No newline at end of file diff --git a/main/src/ui/res/layout/log_fragment.xml b/main/src/ui/res/layout/log_fragment.xml index df87d1c1..bac1fe94 100644 --- a/main/src/ui/res/layout/log_fragment.xml +++ b/main/src/ui/res/layout/log_fragment.xml @@ -35,6 +35,7 @@ /> <ListView + android:clipToPadding="false" android:id="@android:id/list" android:transcriptMode="normal" android:layout_width="fill_parent" diff --git a/main/src/ui/res/layout/log_silders.xml b/main/src/ui/res/layout/log_silders.xml index 4196e243..de6155fc 100644 --- a/main/src/ui/res/layout/log_silders.xml +++ b/main/src/ui/res/layout/log_silders.xml @@ -19,7 +19,8 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/log_verbosity_level"/> + android:text="@string/log_verbosity_level" + android:labelFor="@id/LogLevelSlider"/> <de.blinkt.openvpn.views.SeekBarTicks diff --git a/main/src/ui/res/layout/log_window.xml b/main/src/ui/res/layout/log_window.xml index 7c25dcfa..ffebc474 100644 --- a/main/src/ui/res/layout/log_window.xml +++ b/main/src/ui/res/layout/log_window.xml @@ -4,9 +4,18 @@ --> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context=".LogWindow" - tools:ignore="MergeRootFrame" />
\ No newline at end of file + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:ignore="MergeRootFrame"> + + + <FrameLayout + android:id="@+id/container" + tools:context=".LogWindow" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + + <include layout="@layout/status_bg" /> +</FrameLayout>
\ No newline at end of file diff --git a/main/src/ui/res/layout/main_activity.xml b/main/src/ui/res/layout/main_activity.xml index b2fe2251..6e0a3de9 100644 --- a/main/src/ui/res/layout/main_activity.xml +++ b/main/src/ui/res/layout/main_activity.xml @@ -2,30 +2,40 @@ ~ Copyright (c) 2012-2016 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - + android:layout_height="match_parent"> - <androidx.viewpager.widget.ViewPager - android:id="@+id/pager" + <LinearLayout + android:id="@+id/root_linear_layout" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:orientation="vertical"> + <com.google.android.material.tabs.TabLayout android:id="@+id/tab_layout" style="@style/blinkt.tabLayout" - app:tabMaxWidth="0dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" - app:tabMode="scrollable" + app:tabMaxWidth="0dp" + app:tabMode="scrollable" /> - /> + <androidx.viewpager2.widget.ViewPager2 + android:id="@+id/pager" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="3" /> + + </LinearLayout> - </androidx.viewpager.widget.ViewPager> -</LinearLayout> + <View + android:id="@+id/statusbar_background" + android:background="@color/primary_dark" + android:layout_width="match_parent" + android:layout_height="0dp" /> + +</FrameLayout>
\ No newline at end of file diff --git a/main/src/ui/res/layout/openssl_speed.xml b/main/src/ui/res/layout/openssl_speed.xml index c23d3567..d19b88d2 100644 --- a/main/src/ui/res/layout/openssl_speed.xml +++ b/main/src/ui/res/layout/openssl_speed.xml @@ -2,39 +2,47 @@ ~ Copyright (c) 2012-2017 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical"> - <LinearLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal"> - - - - <EditText - android:id="@+id/ciphername" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - android:ems="10" - android:text="@string/default_cipherlist_test" - android:hint="@string/openssl_cipher_name" - android:inputType="textPersonName" /> - - <Button - android:id="@+id/testSpecific" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_weight="1" - android:text="@string/test_algoirhtms" /> - </LinearLayout> + android:layout_height="match_parent"> - <ListView - android:id="@+id/results" - android:layout_width="match_parent" - android:layout_height="match_parent" /> + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/speed_root" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + + <EditText + android:id="@+id/ciphername" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:ems="10" + android:hint="@string/openssl_cipher_name" + android:inputType="textPersonName" + android:text="@string/default_cipherlist_test" /> + + <Button + android:id="@+id/testSpecific" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="@string/test_algoirhtms" /> + </LinearLayout> + + <ListView + android:id="@+id/results" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </LinearLayout> -</LinearLayout> + <include layout="@layout/status_bg" /> +</FrameLayout> diff --git a/main/src/ui/res/layout/server_card.xml b/main/src/ui/res/layout/server_card.xml index 1395cd8b..8a19ae8f 100644 --- a/main/src/ui/res/layout/server_card.xml +++ b/main/src/ui/res/layout/server_card.xml @@ -327,6 +327,7 @@ android:layout_below="@+id/connect_timeout_label" android:ems="3" tools:text="232" + android:labelFor="@id/connect_silder" android:gravity="end" android:inputType="numberDecimal"/> @@ -364,6 +365,7 @@ android:id="@+id/warnung_custom" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:labelFor="@id/customoptions" android:text="@string/custom_connection_options_warng"/> <EditText diff --git a/main/src/ui/res/layout/status_bg.xml b/main/src/ui/res/layout/status_bg.xml new file mode 100644 index 00000000..3754664b --- /dev/null +++ b/main/src/ui/res/layout/status_bg.xml @@ -0,0 +1,5 @@ +<View xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/statusbar_background" + android:layout_width="match_parent" + android:layout_height="0dp" + android:background="@color/primary_dark" /> diff --git a/main/src/ui/res/layout/webview_internal.xml b/main/src/ui/res/layout/webview_internal.xml index f1bf17c8..740589e1 100644 --- a/main/src/ui/res/layout/webview_internal.xml +++ b/main/src/ui/res/layout/webview_internal.xml @@ -2,23 +2,29 @@ ~ Copyright (c) 2012-2020 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> - -<LinearLayout android:layout_height="match_parent" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - xmlns:android="http://schemas.android.com/apk/res/android"> - - <TextView - android:id="@+id/url_textview" - android:layout_width="match_parent" - android:layout_height="wrap_content" - tools:text="https://foo.bar.baz" /> + android:layout_height="match_parent"> - <WebView - android:id="@+id/internal_webview" + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/webview_root" android:layout_width="match_parent" android:layout_height="match_parent" - /> + android:orientation="vertical"> + + <TextView + android:id="@+id/url_textview" + android:layout_width="match_parent" + android:layout_height="wrap_content" + tools:text="https://foo.bar.baz" /> + + <WebView + android:id="@+id/internal_webview" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + </LinearLayout> -</LinearLayout> + <include layout="@layout/status_bg" /> +</FrameLayout> diff --git a/main/src/ui/res/menu/connections.xml b/main/src/ui/res/menu/connections.xml index 631e01a8..f4dc1820 100644 --- a/main/src/ui/res/menu/connections.xml +++ b/main/src/ui/res/menu/connections.xml @@ -11,6 +11,7 @@ android:title="@string/add_remote" android:id="@+id/add_new_remote" android:titleCondensed="@string/add" + android:contentDescription="@string/add_remote" app:showAsAction="always" /> </menu>
\ No newline at end of file diff --git a/main/src/ui/res/values/styles.xml b/main/src/ui/res/values/styles.xml index 9e8e92b7..374a650d 100644 --- a/main/src/ui/res/values/styles.xml +++ b/main/src/ui/res/values/styles.xml @@ -10,6 +10,21 @@ <item name="colorPrimaryDark">@color/primary_dark</item> <item name="colorAccent">@color/accent</item> <item name="alertDialogTheme">@style/blinkt.alertDialog</item> + + + <item name="android:navigationBarColor"> + @android:color/transparent + </item> + + <!-- Optional: set to transparent if your app is drawing behind the status bar. --> + <item name="android:statusBarColor"> + @android:color/transparent + </item> + + <!-- Optional: set for a light status bar with dark content. --> + <item name="android:windowLightStatusBar"> + true + </item> </style> <style name="blinkt.dialog" parent="Theme.AppCompat.DayNight.Dialog"> @@ -89,10 +104,6 @@ <item name="tabTextAppearance">@style/AppTabTextAppearance</item> <item name="tabSelectedTextColor">@android:color/white</item> - <!-- <item name="tabPaddingStart">10dp</item>--> - <!-- <item name="tabPaddingEnd">10dp</item>--> - - <item name="tabGravity">fill</item> <item name="tabMaxWidth">0dp</item> diff --git a/remoteExample/build.gradle b/remoteExample/build.gradle index 179555bb..019fa10f 100644 --- a/remoteExample/build.gradle +++ b/remoteExample/build.gradle @@ -8,6 +8,9 @@ plugins { } android { + buildFeatures { + buildConfig true + } compileSdkVersion 33 defaultConfig { diff --git a/remoteExample/proguard-rules.pro b/remoteExample/proguard-rules.pro new file mode 100644 index 00000000..61d69340 --- /dev/null +++ b/remoteExample/proguard-rules.pro @@ -0,0 +1,26 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# Untested but reported to work (https://github.com/schwabe/ics-openvpn/issues/1755#issuecomment-2325652081) +-keep interface de.blinkt.openvpn.api.IOpenVPNAPIService { *; } +-keep interface de.blinkt.openvpn.api.IOpenVPNStatusCallback { *; } +-keep class de.blinkt.openvpn.api.APIVpnProfile { *; }
\ No newline at end of file diff --git a/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl index 273a0046..1989b771 100644 --- a/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl +++ b/remoteExample/src/main/aidl/de/blinkt/openvpn/api/IOpenVPNAPIService.aidl @@ -63,4 +63,10 @@ interface IOpenVPNAPIService { /** Use a profile with all certificates etc. embedded */
APIVpnProfile addNewVPNProfile (String name, boolean userEditable, String config);
+
+ /** Same as addNewVPNProfile(String, boolean, String) but giving possibility to pass a Bundle like
+ * in startVPNwithExtras(String, Bundle) to apply e.g. "allow vpn bypass" to profile.
+ * up to now the only extra that can be put is a boolean "de.blinkt.openvpn.api.ALLOW_VPN_BYPASS"
+ */
+ APIVpnProfile addNewVPNProfileWithExtras (String name, boolean userEditable, String config, in Bundle extras);
}
\ No newline at end of file diff --git a/runcoverity.sh b/runcoverity.sh index b716e548..b3519880 100755 --- a/runcoverity.sh +++ b/runcoverity.sh @@ -12,9 +12,10 @@ cov-configure --config .coverity/cfg.xml --kotlin cov-configure --config .coverity/cfg.xml --java ./gradlew -b build.gradle.kts --no-daemon clean -cov-build --dir .coverity/idir --config .coverity/cfg.xml ./gradlew -b build.gradle.kts --no-daemon assembleUiOvpn23Release +# Coverity needs the --fs-capture-search for Kotlin according to https://community.synopsys.com/s/article/How-to-analyze-Kotlin-project +cov-build --fs-capture-search main/src --dir .coverity/idir --config .coverity/cfg.xml ./gradlew -b build.gradle.kts --no-daemon assembleUiOvpn23Release -NDK_VER=${NDK_VER:-25.2.9519653} +NDK_VER=${NDK_VER:-27.0.12077973} cov-analyze --dir .coverity/idir --all --strip-path ${PWD}/main/src/main/cpp --strip-path ${PWD}/main/src --strip-path ${PWD} --strip-path ${ANDROID_HOME}/ndk/${NDK_VER}/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/ --strip-path ${ANDROID_HOME}/ndk/${NDK_VER}/toolchains/llvm/prebuilt/linux-x86_64/sysroot cov-commit-defects --dir .coverity/idir --ssl -host ${COVERITY_CONNECT_HOST} --stream icsopenvpn-styx-master --auth-key-file ~/.coverity/auth-key.txt |