android: VPN notification as in the Final 2026 shade: protected title, pause and change actions
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6652970925
commit
db6c806d58
5 files changed
+53
-2
No files matched your search
+26
-2
@@ -734,6 +734,12 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
|||||||
/**
|
/**
|
||||||
* Build a notification matching the current state
|
* 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)
|
private Notification buildNotification(boolean publicVersion)
|
||||||
{
|
{
|
||||||
/* the state service may not be bound yet when startForeground() is
|
/* the state service may not be bound yet when startForeground() is
|
||||||
@@ -818,7 +824,14 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
|||||||
break;
|
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;
|
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||||
@@ -827,6 +840,17 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
|||||||
}
|
}
|
||||||
if (!publicVersion)
|
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)
|
if (add_action)
|
||||||
{
|
{
|
||||||
Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class);
|
Intent intent = new Intent(getApplicationContext(), VpnProfileControlActivity.class);
|
||||||
@@ -837,7 +861,7 @@ public class CharonVpnService extends VpnService implements Runnable, VpnStateSe
|
|||||||
}
|
}
|
||||||
if (error == ErrorState.NO_ERROR)
|
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));
|
builder.setPublicVersion(buildNotification(true));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ object PauseManager
|
|||||||
private const val PREF_UNTIL = "pref_pause_until"
|
private const val PREF_UNTIL = "pref_pause_until"
|
||||||
private const val PREF_PROFILE = "pref_pause_profile"
|
private const val PREF_PROFILE = "pref_pause_profile"
|
||||||
private const val REQUEST_CODE = 45
|
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" */
|
/* safety net of "until the network changes" */
|
||||||
private const val NETWORK_PAUSE_MAX_MS = 12L * 60 * 60 * 1000
|
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?)
|
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)
|
PauseManager.onAlarm(context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,6 +47,11 @@ public class MainActivity extends AppCompatActivity
|
|||||||
*/
|
*/
|
||||||
public static final String EXTRA_ADD_SERVER = "org.strongswan.android.extra.ADD_SERVER";
|
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
|
* Use "bring your own device" (BYOD) features
|
||||||
*/
|
*/
|
||||||
@@ -111,5 +116,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
intent.removeExtra(EXTRA_ADD_SERVER);
|
intent.removeExtra(EXTRA_ADD_SERVER);
|
||||||
ShxNavigation.INSTANCE.requestAddServer();
|
ShxNavigation.INSTANCE.requestAddServer();
|
||||||
}
|
}
|
||||||
|
if (intent != null && intent.getBooleanExtra(EXTRA_OPEN_SERVERS, false))
|
||||||
|
{
|
||||||
|
intent.removeExtra(EXTRA_OPEN_SERVERS);
|
||||||
|
ShxNavigation.INSTANCE.request(ShxTab.PROFILES);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1009,4 +1009,8 @@
|
|||||||
<string name="perm_tag_optional">по желанию</string>
|
<string name="perm_tag_optional">по желанию</string>
|
||||||
<string name="perm_allow">Разрешить</string>
|
<string name="perm_allow">Разрешить</string>
|
||||||
<string name="perm_done">Готово</string>
|
<string name="perm_done">Готово</string>
|
||||||
|
<string name="notif_protected">Защищено · %1$s</string>
|
||||||
|
<string name="notif_pause">Пауза 15 м</string>
|
||||||
|
<string name="notif_change">Сменить</string>
|
||||||
|
<string name="notif_ru_direct">RU-сайты идут напрямую</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -994,4 +994,8 @@
|
|||||||
<string name="perm_tag_optional">optional</string>
|
<string name="perm_tag_optional">optional</string>
|
||||||
<string name="perm_allow">Allow</string>
|
<string name="perm_allow">Allow</string>
|
||||||
<string name="perm_done">Done</string>
|
<string name="perm_done">Done</string>
|
||||||
|
<string name="notif_protected">Protected · %1$s</string>
|
||||||
|
<string name="notif_pause">Pause 15 min</string>
|
||||||
|
<string name="notif_change">Change</string>
|
||||||
|
<string name="notif_ru_direct">RU sites go direct</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in new issue
Block a user