From 4473ad1f1bc49c166fb8ab92ee7a7b91c608135e Mon Sep 17 00:00:00 2001 From: Elizaveta Date: Mon, 22 Apr 2024 16:50:45 +0300 Subject: [PATCH 01/16] Fix binding to external authenticator app on Android 14+ External authenticator apps may need to start background activities (for example, to allow users enter a pin code), but starting from Android 14, if the app bound to the service is targeting Android 14 or higher, it no longer allows the app that has the service to start a background activity by default. We need to add flag BIND_ALLOW_ACTIVITY_STARTS to allow the external authenticator app to start background activities. --- main/src/main/java/de/blinkt/openvpn/core/ExtAuthHelper.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 391516465..0da8abbbe 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 void onServiceDisconnected(ComponentName name) { 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()); } From 5bdac9a6be1716738bd90816247fd52672a16568 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 12 Dec 2023 12:15:40 +0100 Subject: [PATCH 02/16] Fix a few accessibility issues reported by pre check of Play store --- main/src/main/res/values/strings.xml | 1 + .../ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java | 1 + main/src/ui/res/layout/basic_settings.xml | 1 + main/src/ui/res/layout/log_silders.xml | 3 ++- main/src/ui/res/layout/server_card.xml | 2 ++ main/src/ui/res/menu/connections.xml | 1 + 6 files changed, 8 insertions(+), 1 deletion(-) diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml index fd0b30ce7..538936999 100755 --- a/main/src/main/res/values/strings.xml +++ b/main/src/main/res/values/strings.xml @@ -511,4 +511,5 @@ Permission to start a VPN connection is required VPN Service is missing permission to connect a VPN. Requesting permission via notification. VPN already running (%s). Ignoring request to start VPN. + Name of the VPN Profile 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 5b4c736fd..28504268f 100644 --- a/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/ui/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -451,6 +451,7 @@ private void onAddOrDuplicateProfile(final VpnProfile mCopyProfile) { 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) diff --git a/main/src/ui/res/layout/basic_settings.xml b/main/src/ui/res/layout/basic_settings.xml index 8aa4c811f..47f84e5f2 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" /> + android:text="@string/log_verbosity_level" + android:labelFor="@id/LogLevelSlider"/> @@ -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"/> \ No newline at end of file From 879ed38be52fbc1d7079ced9c31756c7635a1fa1 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 23 Jul 2024 14:18:13 +0200 Subject: [PATCH 03/16] Small coverity fixes --- main/build.gradle.kts | 1 - main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java | 2 +- runcoverity.sh | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/build.gradle.kts b/main/build.gradle.kts index 94f0af418..984f075f9 100644 --- a/main/build.gradle.kts +++ b/main/build.gradle.kts @@ -12,7 +12,6 @@ plugins { } android { - buildToolsVersion = "33.0.1" buildFeatures { aidl = true } 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 6f20d4145..a1411b5e4 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java +++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java @@ -309,7 +309,7 @@ else if (channel.equals(NOTIFICATION_CHANNEL_USERREQ_ID)) 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 { diff --git a/runcoverity.sh b/runcoverity.sh index 988b5cda7..eba44a7e6 100755 --- a/runcoverity.sh +++ b/runcoverity.sh @@ -12,7 +12,8 @@ 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:-26.1.10909125} 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 From aae95613927ad80794bb5683208172aa6269bd23 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Tue, 23 Jul 2024 15:00:57 +0200 Subject: [PATCH 04/16] Update version of OpenSSL, OpenVPN 2/3, Asio and many libraries --- gradle/libs.versions.toml | 26 +++++++++---------- gradle/wrapper/gradle-wrapper.properties | 2 +- main/src/main/cpp/asio | 2 +- main/src/main/cpp/openssl | 2 +- main/src/main/cpp/openvpn | 2 +- main/src/main/cpp/openvpn3 | 2 +- .../blinkt/openvpn/core/OpenVPNThreadv3.java | 1 - 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f841712e..9dffd27ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,27 +1,27 @@ [versions] -android-gradle-plugin = "8.1.4" -androidx-annotation = "1.7.0" -androidx-appcompat = "1.6.1" -android-view-material = "1.10.0" -androidx-core-ktx = "1.12.0" -androidx-fragment-ktx = "1.6.2" +android-gradle-plugin = "8.3.2" +androidx-annotation = "1.8.0" +androidx-appcompat = "1.7.0" +android-view-material = "1.12.0" +androidx-core-ktx = "1.13.1" +androidx-fragment-ktx = "1.8.1" androidx-preference-ktx = "1.2.1" -androidx-webkit = "1.9.0" -androidx-lifecycle-viewmodel-ktx = "2.6.2" -androidx-lifecycle-runtime-ktx = "2.6.2" +androidx-webkit = "1.11.0" +androidx-lifecycle-viewmodel-ktx = "2.8.3" +androidx-lifecycle-runtime-ktx = "2.8.3" androidx-security-crypto = "1.1.0-alpha06" androidx-constraintlayout = "2.1.4" androidx-cardview = "1.0.0" androidx-recyclerview = "1.3.2" -bouncycastle = "1.67" +bouncycastle = "1.69" mpandroidchart = "v3.1.0" -kotlin = "1.9.0" +kotlin = "1.9.20" square-okhttp = "4.10.0" # Test -androidx-test-core = "1.5.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] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 06fe19dc8..e59ea7eac 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip diff --git a/main/src/main/cpp/asio b/main/src/main/cpp/asio index 1f8d15482..12e0ce9e0 160000 --- a/main/src/main/cpp/asio +++ b/main/src/main/cpp/asio @@ -1 +1 @@ -Subproject commit 1f8d154829b902dbc45a651587c6c6df948358e8 +Subproject commit 12e0ce9e0500bf0f247dbd1ae894272656456079 diff --git a/main/src/main/cpp/openssl b/main/src/main/cpp/openssl index 479b1b838..bbc7c1c11 160000 --- a/main/src/main/cpp/openssl +++ b/main/src/main/cpp/openssl @@ -1 +1 @@ -Subproject commit 479b1b83891105c382cacdf71aeb35c14e994219 +Subproject commit bbc7c1c11fbd0eb47e3d2ee46c9986ce16b3e8a5 diff --git a/main/src/main/cpp/openvpn b/main/src/main/cpp/openvpn index 0ad449769..99788b64e 160000 --- a/main/src/main/cpp/openvpn +++ b/main/src/main/cpp/openvpn @@ -1 +1 @@ -Subproject commit 0ad449769ee88fe19fb23a0714639f98eba77088 +Subproject commit 99788b64e6aa82c32aa39915c441e0b3b9164545 diff --git a/main/src/main/cpp/openvpn3 b/main/src/main/cpp/openvpn3 index 7f2e42ce0..13d6b06e1 160000 --- a/main/src/main/cpp/openvpn3 +++ b/main/src/main/cpp/openvpn3 @@ -1 +1 @@ -Subproject commit 7f2e42ce0ba6651f6549146f641f1e4c196059a4 +Subproject commit 13d6b06e1c03174000f0a7bdfdcfb680e19f4cb8 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 ad7a7c280..4f8e33a6f 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 void external_pki_sign_request(ClientAPI_ExternalPKISignRequest signreq) 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); From 6b0a2e59b20fa328ff7f82afb6867f9f8e14e344 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 26 Jul 2024 15:26:17 +0200 Subject: [PATCH 05/16] Move to viewpager2 --- gradle/libs.versions.toml | 2 + main/build.gradle.kts | 1 + .../blinkt/openvpn/activities/MainActivity.kt | 14 +- .../openvpn/activities/VPNPreferences.java | 220 ------------------ .../openvpn/activities/VPNPreferences.kt | 208 +++++++++++++++++ .../views/ScreenSlidePagerAdapter.java | 80 ------- .../openvpn/views/ScreenSlidePagerAdapter.kt | 59 +++++ main/src/ui/res/layout/main_activity.xml | 27 ++- 8 files changed, 294 insertions(+), 317 deletions(-) delete mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.java create mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt delete mode 100644 main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java create mode 100644 main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9dffd27ac..a9c675c59 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ androidx-security-crypto = "1.1.0-alpha06" androidx-constraintlayout = "2.1.4" androidx-cardview = "1.0.0" androidx-recyclerview = "1.3.2" +androidx-viewpager2 = "1.1.0" bouncycastle = "1.69" mpandroidchart = "v3.1.0" kotlin = "1.9.20" @@ -29,6 +30,7 @@ android-view-material = { group = "com.google.android.material", name = "materia androidx-annotation = { group = "androidx.annotation", name = "annotation", version.ref = "androidx-annotation" } 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" } diff --git a/main/build.gradle.kts b/main/build.gradle.kts index 984f075f9..20dc6479e 100644 --- a/main/build.gradle.kts +++ b/main/build.gradle.kts @@ -237,6 +237,7 @@ dependencies { uiImplementation(libs.android.view.material) 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/ui/java/de/blinkt/openvpn/activities/MainActivity.kt b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt index 68117b52e..49778b88c 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt @@ -11,23 +11,26 @@ 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) - // Instantiate a ViewPager and a PagerAdapter. mPager = findViewById(R.id.pager) - mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, this) + val tablayout: TabLayout = 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,6 +44,11 @@ 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() + } private fun disableToolbarElevation() { 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 2c9eb7617..000000000 --- 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 000000000..fd9fd43e5 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt @@ -0,0 +1,208 @@ +/* + * 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) + + + setContentView(R.layout.main_activity) + + disableToolbarElevation() + + // Instantiate a ViewPager and a PagerAdapter. + mPager = findViewById(R.id.pager) + val tablayout: TabLayout = 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() + } + + + 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> = 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/views/ScreenSlidePagerAdapter.java b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.java deleted file mode 100644 index 4f1150a28..000000000 --- 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 fragmentClass; - String mName; - - public Tab(Class fClass, String name){ - mName = name; - fragmentClass = fClass; - } - - } - - - private Vector mTabs = new Vector(); - - 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 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 000000000..0ce0afaa1 --- /dev/null +++ b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt @@ -0,0 +1,59 @@ +/* + * 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.StringRes +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Lifecycle +import androidx.viewpager2.adapter.FragmentStateAdapter +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, var mName: String) + + private val mTabs = Vector() + + 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) { + mTabs.add(Tab(fragmentClass, res.getString(name))) + } +} diff --git a/main/src/ui/res/layout/main_activity.xml b/main/src/ui/res/layout/main_activity.xml index b2fe2251e..edc64ecee 100644 --- a/main/src/ui/res/layout/main_activity.xml +++ b/main/src/ui/res/layout/main_activity.xml @@ -9,23 +9,22 @@ android:layout_height="match_parent" android:orientation="vertical"> - - + android:layout_height="wrap_content" + android:layout_gravity="top" + app:tabMode="scrollable" - - /> + - From 291acb38ddf8cf8c8f7dab2f1aad6bb860ae6109 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Fri, 26 Jul 2024 15:30:22 +0200 Subject: [PATCH 06/16] Update versions and enable Android V build --- gradle/libs.versions.toml | 15 +++++++++------ gradle/wrapper/gradle-wrapper.properties | 4 ++-- main/build.gradle.kts | 8 +++++--- .../blinkt/openvpn/activities/InternalWebView.kt | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a9c675c59..eafb4d847 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,15 @@ [versions] -android-gradle-plugin = "8.3.2" -androidx-annotation = "1.8.0" +android-gradle-plugin = "8.5.1" +androidx-activity = "1.9.1" +androidx-annotation = "1.8.1" androidx-appcompat = "1.7.0" android-view-material = "1.12.0" androidx-core-ktx = "1.13.1" -androidx-fragment-ktx = "1.8.1" +androidx-fragment-ktx = "1.8.2" androidx-preference-ktx = "1.2.1" androidx-webkit = "1.11.0" -androidx-lifecycle-viewmodel-ktx = "2.8.3" -androidx-lifecycle-runtime-ktx = "2.8.3" +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" @@ -16,7 +17,7 @@ androidx-recyclerview = "1.3.2" androidx-viewpager2 = "1.1.0" bouncycastle = "1.69" mpandroidchart = "v3.1.0" -kotlin = "1.9.20" +kotlin = "1.9.22" square-okhttp = "4.10.0" # Test @@ -28,6 +29,8 @@ 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" } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e59ea7eac..618ee5ccd 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +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.4-all.zip diff --git a/main/build.gradle.kts b/main/build.gradle.kts index 20dc6479e..a52cf8351 100644 --- a/main/build.gradle.kts +++ b/main/build.gradle.kts @@ -16,15 +16,15 @@ android { aidl = true } namespace = "de.blinkt.openvpn" - compileSdk = 34 + compileSdk = 35 //compileSdkPreview = "UpsideDownCake" // Also update runcoverity.sh - ndkVersion = "26.1.10909125" + ndkVersion = "26.3.11579264" defaultConfig { minSdk = 21 - targetSdk = 34 + targetSdk = 35 //targetSdkPreview = "UpsideDownCake" versionCode = 206 versionName = "0.7.51" @@ -235,6 +235,8 @@ 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) 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 1a16829e4..e46356e98 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/InternalWebView.kt @@ -94,7 +94,7 @@ class InternalWebView : AppCompatActivity() { } - override fun onNewIntent(intent: Intent?) { + override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) } From c2d625eef66c2ede30f0e5c8d03ca279975eefc8 Mon Sep 17 00:00:00 2001 From: Arne Schwabe Date: Mon, 29 Jul 2024 17:21:39 +0200 Subject: [PATCH 07/16] Implement enough of edge-to-edge to work well on Android V --- .../openvpn/activities/BaseActivity.java | 45 ---- .../blinkt/openvpn/activities/BaseActivity.kt | 81 +++++++ .../openvpn/activities/ConfigConverter.kt | 4 +- .../openvpn/activities/InternalWebView.kt | 4 +- .../blinkt/openvpn/activities/LogWindow.java | 37 ---- .../de/blinkt/openvpn/activities/LogWindow.kt | 33 +++ .../blinkt/openvpn/activities/MainActivity.kt | 11 +- .../blinkt/openvpn/activities/OpenSSLSpeed.kt | 1 + .../openvpn/activities/VPNPreferences.kt | 9 +- .../openvpn/fragments/AboutFragment.java | 13 ++ .../blinkt/openvpn/fragments/FaqFragment.java | 7 + .../blinkt/openvpn/fragments/LogFragment.java | 18 +- .../openvpn/fragments/Settings_Connections.kt | 4 - .../java/de/blinkt/openvpn/fragments/Utils.kt | 39 +++- .../openvpn/views/ScreenSlidePagerAdapter.kt | 13 ++ main/src/ui/res/layout/about.xml | 1 + main/src/ui/res/layout/config_converter.xml | 204 +++++++++--------- main/src/ui/res/layout/faq.xml | 20 +- main/src/ui/res/layout/log_fragment.xml | 1 + main/src/ui/res/layout/log_window.xml | 21 +- main/src/ui/res/layout/main_activity.xml | 47 ++-- main/src/ui/res/layout/openssl_speed.xml | 72 ++++--- main/src/ui/res/layout/status_bg.xml | 5 + main/src/ui/res/layout/webview_internal.xml | 36 ++-- main/src/ui/res/values/styles.xml | 19 +- 25 files changed, 460 insertions(+), 285 deletions(-) delete mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.java create mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/BaseActivity.kt delete mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.java create mode 100644 main/src/ui/java/de/blinkt/openvpn/activities/LogWindow.kt create mode 100644 main/src/ui/res/layout/status_bg.xml 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 cca8b1555..000000000 --- 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 000000000..9e6dd4625 --- /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(contentViewId) + + ViewCompat.setOnApplyWindowInsetsListener(contentView) { v, windowInsets -> + val insets = + windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout()) + val statusbarbg = findViewById(R.id.statusbar_background); + + val statusBarInsets = windowInsets.getInsets(WindowInsetsCompat.Type.statusBars()) + + statusbarbg.layoutParams.height = statusBarInsets.top + + + v.updateLayoutParams { + 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 b85eaa260..a80afcab5 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(R.id.fab_save) if (fab_button != null) { 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 e46356e98..fec69768e 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) 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 5277a25d5..000000000 --- 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 000000000..afc18a035 --- /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 49778b88c..a15de1146 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/MainActivity.kt @@ -10,7 +10,6 @@ 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 @@ -22,13 +21,14 @@ import de.blinkt.openvpn.views.ScreenSlidePagerAdapter class MainActivity : BaseActivity() { 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) - val tablayout: TabLayout = findViewById(R.id.tab_layout) + mPager = view.findViewById(R.id.pager) + val tablayout: TabLayout = view.findViewById(R.id.tab_layout) mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, lifecycle, this) @@ -49,8 +49,11 @@ class MainActivity : BaseActivity() { 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 324cd881b..e7c6c2dbc 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(R.id.testSpecific).setOnClickListener { _ -> runAlgorithms(mCipher.text.toString()) } diff --git a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt index fd9fd43e5..0dd617489 100644 --- a/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt +++ b/main/src/ui/java/de/blinkt/openvpn/activities/VPNPreferences.kt @@ -104,13 +104,14 @@ class VPNPreferences : BaseActivity() { title = getString(R.string.edit_profile_title, mProfile!!.name) - setContentView(R.layout.main_activity) + val rootview = layoutInflater.inflate(R.layout.main_activity, null) + setUpEdgeEdgeInsetsListener(rootview, R.id.root_linear_layout) disableToolbarElevation() // Instantiate a ViewPager and a PagerAdapter. - mPager = findViewById(R.id.pager) - val tablayout: TabLayout = findViewById(R.id.tab_layout) + mPager = rootview.findViewById(R.id.pager) + val tablayout: TabLayout = rootview.findViewById(R.id.tab_layout) mPagerAdapter = ScreenSlidePagerAdapter(supportFragmentManager, lifecycle, this) @@ -143,6 +144,8 @@ class VPNPreferences : BaseActivity() { TabLayoutMediator(tablayout, mPager) { tab, position -> tab.text = mPagerAdapter.getPageTitle(position) }.attach() + + setContentView(rootview) } 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 de6c83d8b..15375a3f2 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.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 View onCreateView(LayoutInflater inflater, ViewGroup container, 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 dcdfd5e38..2863b2429 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.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 View onCreateView(LayoutInflater inflater, ViewGroup container, mRecyclerView.setAdapter(new FaqViewAdapter(getActivity(), getFAQEntries())); + Utils.applyInsetListener(v); + return v; } + + private FAQEntry[] getFAQEntries() { Vector 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 9ce653160..4a7cf7350 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.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.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; @@ -631,16 +627,26 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { }); 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/Settings_Connections.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Settings_Connections.kt index 8ed9d9efd..b06c33685 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(R.id.noserver_active_warning) as TextView mRecyclerView = v.findViewById(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/Utils.kt b/main/src/ui/java/de/blinkt/openvpn/fragments/Utils.kt index 550564244..e41dc3375 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/views/ScreenSlidePagerAdapter.kt b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt index 0ce0afaa1..98036953b 100644 --- a/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt +++ b/main/src/ui/java/de/blinkt/openvpn/views/ScreenSlidePagerAdapter.kt @@ -7,11 +7,18 @@ 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 /** @@ -31,11 +38,13 @@ class ScreenSlidePagerAdapter(fm: FragmentManager, lc: Lifecycle, c: Context) : internal class Tab(var fragmentClass: Class, var mName: String) private val mTabs = Vector() + 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() @@ -56,4 +65,8 @@ class ScreenSlidePagerAdapter(fm: FragmentManager, lc: Lifecycle, c: Context) : fun addTab(@StringRes name: Int, fragmentClass: Class) { 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 cd482996c..f73768c79 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"> - - + android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + - - + - + - - + + diff --git a/main/src/ui/res/layout/faq.xml b/main/src/ui/res/layout/faq.xml index 8cb79649a..46b56e588 100644 --- a/main/src/ui/res/layout/faq.xml +++ b/main/src/ui/res/layout/faq.xml @@ -1,15 +1,15 @@ - - \ 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 df87d1c1e..bac1fe94e 100644 --- a/main/src/ui/res/layout/log_fragment.xml +++ b/main/src/ui/res/layout/log_fragment.xml @@ -35,6 +35,7 @@ /> \ 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"> + + + + + + + \ 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 edc64ecee..6e0a3de94 100644 --- a/main/src/ui/res/layout/main_activity.xml +++ b/main/src/ui/res/layout/main_activity.xml @@ -2,29 +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 --> - - + android:layout_height="match_parent"> - - /> - + + + + + + + - + \ 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 c23d3567a..d19b88d22 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 --> - - - - - - - -