11package click .vpnclient .engine .example ;
22
3+ import android .content .Intent ;
4+ import android .net .VpnService ;
35import android .os .Bundle ;
6+ import android .util .Log ;
7+ import android .widget .Button ;
8+ import android .widget .TextView ;
9+ import android .widget .Toast ;
410
5- import androidx .activity .EdgeToEdge ;
11+ import androidx .activity .result .ActivityResultLauncher ;
12+ import androidx .activity .result .contract .ActivityResultContracts ;
613import androidx .appcompat .app .AppCompatActivity ;
7- import androidx .core .graphics .Insets ;
8- import androidx .core .view .ViewCompat ;
9- import androidx .core .view .WindowInsetsCompat ;
14+
15+ import java .io .File ;
16+ import java .io .FileOutputStream ;
17+ import java .io .IOException ;
18+
19+ import click .vpnclient .engine .VpnClientEngine ;
1020
1121public class MainActivity extends AppCompatActivity {
22+ private static final String TAG = "MainActivity" ;
23+
24+ private Button startButton ;
25+ private Button stopButton ;
26+ private TextView statusTextView ;
27+ private VpnClientEngine engine ;
28+ private boolean isVpnRunning = false ;
29+
30+ private final ActivityResultLauncher <Intent > vpnPermissionLauncher = registerForActivityResult (
31+ new ActivityResultContracts .StartActivityForResult (),
32+ result -> {
33+ if (result .getResultCode () == RESULT_OK ) {
34+ Log .d (TAG , "VPN permission granted" );
35+ startVpnConnection ();
36+ } else {
37+ Log .w (TAG , "VPN permission denied" );
38+ Toast .makeText (this , "VPN permission is required to start the connection" , Toast .LENGTH_LONG ).show ();
39+ updateUI (false );
40+ }
41+ }
42+ );
1243
1344 @ Override
1445 protected void onCreate (Bundle savedInstanceState ) {
1546 super .onCreate (savedInstanceState );
16- EdgeToEdge .enable (this );
1747 setContentView (R .layout .activity_main );
18- ViewCompat .setOnApplyWindowInsetsListener (findViewById (R .id .main ), (v , insets ) -> {
19- Insets systemBars = insets .getInsets (WindowInsetsCompat .Type .systemBars ());
20- v .setPadding (systemBars .left , systemBars .top , systemBars .right , systemBars .bottom );
21- return insets ;
22- });
48+
49+ startButton = findViewById (R .id .start_button );
50+ stopButton = findViewById (R .id .stop_button );
51+ statusTextView = findViewById (R .id .status_text );
52+
53+ engine = new VpnClientEngine (VpnClientEngine .DriverType .LibXray );
54+
55+ // Create a basic config file if it doesn't exist
56+ createConfigFileIfNeeded ();
57+
58+ startButton .setOnClickListener (v -> requestVpnPermission ());
59+
60+ stopButton .setOnClickListener (v -> stopVpnConnection ());
61+
62+ // Initialize UI
63+ updateUI (false );
64+ }
65+
66+ private void requestVpnPermission () {
67+ Intent vpnIntent = VpnService .prepare (this );
68+ if (vpnIntent != null ) {
69+ // Permission not granted, request it
70+ Log .d (TAG , "Requesting VPN permission" );
71+ vpnPermissionLauncher .launch (vpnIntent );
72+ } else {
73+ // Permission already granted
74+ Log .d (TAG , "VPN permission already granted" );
75+ startVpnConnection ();
76+ }
77+ }
78+
79+ private void startVpnConnection () {
80+ Log .d (TAG , "Starting VPN connection" );
81+
82+ try {
83+ // Start VPN engine
84+ String dataDir = getApplicationInfo ().dataDir ;
85+ String configFilePath = new File (dataDir , "config.json" ).getAbsolutePath ();
86+ boolean engineStarted = engine .start (dataDir , configFilePath );
87+
88+ if (!engineStarted ) {
89+ Log .e (TAG , "Failed to start VPN engine" );
90+ Toast .makeText (this , "Failed to start VPN engine" , Toast .LENGTH_SHORT ).show ();
91+ updateUI (false );
92+ return ;
93+ }
94+
95+ // Start VPN service
96+ Intent serviceIntent = new Intent (this , VPNService .class );
97+ startService (serviceIntent );
98+
99+ isVpnRunning = true ;
100+ updateUI (true );
101+ Toast .makeText (this , "VPN connection started" , Toast .LENGTH_SHORT ).show ();
102+
103+ } catch (Exception e ) {
104+ Log .e (TAG , "Error starting VPN connection" , e );
105+ Toast .makeText (this , "Error starting VPN: " + e .getMessage (), Toast .LENGTH_LONG ).show ();
106+ updateUI (false );
107+ }
108+ }
109+
110+ private void stopVpnConnection () {
111+ Log .d (TAG , "Stopping VPN connection" );
112+
113+ try {
114+ // Stop VPN service
115+ Intent serviceIntent = new Intent (this , VPNService .class );
116+ serviceIntent .setAction ("STOP_VPN" );
117+ startService (serviceIntent );
118+
119+ // Stop VPN engine
120+ engine .stop ();
121+
122+ isVpnRunning = false ;
123+ updateUI (false );
124+ Toast .makeText (this , "VPN connection stopped" , Toast .LENGTH_SHORT ).show ();
125+
126+ } catch (Exception e ) {
127+ Log .e (TAG , "Error stopping VPN connection" , e );
128+ Toast .makeText (this , "Error stopping VPN: " + e .getMessage (), Toast .LENGTH_SHORT ).show ();
129+ }
130+ }
131+
132+ private void updateUI (boolean isRunning ) {
133+ startButton .setEnabled (!isRunning );
134+ stopButton .setEnabled (isRunning );
135+
136+ if (isRunning ) {
137+ statusTextView .setText (R .string .status_running );
138+ } else {
139+ statusTextView .setText (R .string .status_stopped );
140+ }
141+ }
142+
143+ private void createConfigFileIfNeeded () {
144+ String dataDir = getApplicationInfo ().dataDir ;
145+ File configFile = new File (dataDir , "config.json" );
146+
147+ if (!configFile .exists ()) {
148+ try {
149+ // Create a basic configuration
150+ String basicConfig = "{\n " +
151+ " \" log\" : {\n " +
152+ " \" loglevel\" : \" info\" \n " +
153+ " },\n " +
154+ " \" inbounds\" : [\n " +
155+ " {\n " +
156+ " \" tag\" : \" tun\" ,\n " +
157+ " \" type\" : \" tun\" ,\n " +
158+ " \" interface_name\" : \" tun0\" ,\n " +
159+ " \" inet4_address\" : \" 172.19.0.1/30\" ,\n " +
160+ " \" auto_route\" : true,\n " +
161+ " \" strict_route\" : false,\n " +
162+ " \" sniff\" : true\n " +
163+ " }\n " +
164+ " ],\n " +
165+ " \" outbounds\" : [\n " +
166+ " {\n " +
167+ " \" tag\" : \" direct\" ,\n " +
168+ " \" type\" : \" direct\" \n " +
169+ " }\n " +
170+ " ]\n " +
171+ "}" ;
172+
173+ FileOutputStream fos = new FileOutputStream (configFile );
174+ fos .write (basicConfig .getBytes ());
175+ fos .close ();
176+
177+ Log .d (TAG , "Created basic config file at: " + configFile .getAbsolutePath ());
178+ } catch (IOException e ) {
179+ Log .e (TAG , "Failed to create config file" , e );
180+ }
181+ }
182+ }
183+
184+ @ Override
185+ protected void onDestroy () {
186+ super .onDestroy ();
187+ if (isVpnRunning ) {
188+ stopVpnConnection ();
189+ }
23190 }
24191}
0 commit comments