From db6c806d5874c7e34d5d0729907f0d8fd545d8d7 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Mon, 12 Oct 2026 10:12:41 +0700 Subject: [PATCH] android: VPN notification as in the Final 2026 shade: protected title, pause and change actions Co-Authored-By: Claude Opus 5.5 --- .../android/logic/CharonVpnService.java | 28 +++++++++++++++++-- .../strongswan/android/logic/PauseManager.kt | 9 ++++++ .../strongswan/android/ui/MainActivity.java | 10 +++++++ .../app/src/main/res/values-ru/strings.xml | 4 +++ .../app/src/main/res/values/strings.xml | 4 +++ 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java index 251c0aceb..12fab5cd7 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/CharonVpnService.java @@ -734,6 +734,12 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe /** * Build a notification matching the current state */ + /** Whether «RU sites direct» is on (any mode). */ + private static boolean isRuSplitOn(Context context) + { + return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(Constants.PREF_RUSPLIT_GEOIP_ENABLED, false); + } + private Notification buildNotification(boolean publicVersion) { /* the state service may not be bound yet when startForeground() is @@ -818,7 +824,14 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe break; } } - builder.setContentTitle(getString(s)); + if (state == State.CONNECTED && error == ErrorState.NO_ERROR && !publicVersion && profile != null) + { /* «Protected · Amsterdam» like the mockup shade */ + builder.setContentTitle(getString(R.string.notif_protected, name)); + } + else + { + builder.setContentTitle(getString(s)); + } int flags = PendingIntent.FLAG_UPDATE_CURRENT; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) @@ -827,6 +840,17 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe } if (!publicVersion) { + if (state == State.CONNECTED && error == ErrorState.NO_ERROR && profile != null) + { /* «Pause 15 min» and «Change» as on the mockup shade */ + Intent pause = new Intent(getApplicationContext(), PauseReceiver.class).setAction(PauseManager.ACTION_PAUSE); + builder.addAction(R.drawable.ic_notification_connecting, getString(R.string.notif_pause), + PendingIntent.getBroadcast(getApplicationContext(), 1, pause, flags)); + Intent change = new Intent(getApplicationContext(), MainActivity.class) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP) + .putExtra(MainActivity.EXTRA_OPEN_SERVERS, true); + builder.addAction(R.drawable.ic_notification, getString(R.string.notif_change), + PendingIntent.getActivity(getApplicationContext(), 2, change, flags)); + } if (add_action) { Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class); @@ -837,7 +861,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe } if (error == ErrorState.NO_ERROR) { - builder.setContentText(name); + builder.setContentText(state == State.CONNECTED && isRuSplitOn(this) ? getString(R.string.notif_ru_direct) : name); } builder.setPublicVersion(buildNotification(true)); } diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/PauseManager.kt b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/PauseManager.kt index 905ba4463..4bf3561a0 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/logic/PauseManager.kt +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/logic/PauseManager.kt @@ -40,6 +40,8 @@ object PauseManager private const val PREF_UNTIL = "pref_pause_until" private const val PREF_PROFILE = "pref_pause_profile" private const val REQUEST_CODE = 45 + /** «Pause 15 min» from the VPN notification */ + const val ACTION_PAUSE = "org.strongswan.android.action.PAUSE_15" /* safety net of "until the network changes" */ private const val NETWORK_PAUSE_MAX_MS = 12L * 60 * 60 * 1000 @@ -329,6 +331,13 @@ class PauseReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent?) { + if (intent?.action == PauseManager.ACTION_PAUSE) + { + val service = VpnStateService.getLiveInstance() ?: return + val profile = service.profile ?: return + PauseManager.pause(context, service, profile.getUUID().toString(), 15) + return + } PauseManager.onAlarm(context) } } diff --git a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java index cba83a9a7..b2afd4bec 100644 --- a/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java +++ b/src/frontends/android/app/src/main/java/org/strongswan/android/ui/MainActivity.java @@ -47,6 +47,11 @@ public class MainActivity extends AppCompatActivity */ public static final String EXTRA_ADD_SERVER = "org.strongswan.android.extra.ADD_SERVER"; + /** + * Set as an intent extra to open the Servers tab (the «Change» action of the notification) + */ + public static final String EXTRA_OPEN_SERVERS = "org.strongswan.android.extra.OPEN_SERVERS"; + /** * Use "bring your own device" (BYOD) features */ @@ -111,5 +116,10 @@ public class MainActivity extends AppCompatActivity intent.removeExtra(EXTRA_ADD_SERVER); ShxNavigation.INSTANCE.requestAddServer(); } + if (intent != null && intent.getBooleanExtra(EXTRA_OPEN_SERVERS, false)) + { + intent.removeExtra(EXTRA_OPEN_SERVERS); + ShxNavigation.INSTANCE.request(ShxTab.PROFILES); + } } } diff --git a/src/frontends/android/app/src/main/res/values-ru/strings.xml b/src/frontends/android/app/src/main/res/values-ru/strings.xml index 23dca3e91..6fb1b6e57 100644 --- a/src/frontends/android/app/src/main/res/values-ru/strings.xml +++ b/src/frontends/android/app/src/main/res/values-ru/strings.xml @@ -1009,4 +1009,8 @@ по желанию Разрешить Готово + Защищено · %1$s + Пауза 15 м + Сменить + RU-сайты идут напрямую diff --git a/src/frontends/android/app/src/main/res/values/strings.xml b/src/frontends/android/app/src/main/res/values/strings.xml index 251dfcff3..d1a383dcd 100644 --- a/src/frontends/android/app/src/main/res/values/strings.xml +++ b/src/frontends/android/app/src/main/res/values/strings.xml @@ -994,4 +994,8 @@ optional Allow Done + Protected · %1$s + Pause 15 min + Change + RU sites go direct