Skip to content

Commit 18d9536

Browse files
committed
增加磁贴按钮
1 parent f57db8a commit 18d9536

File tree

12 files changed

+541
-178
lines changed

12 files changed

+541
-178
lines changed

android/app/src/main/AndroidManifest.xml

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
1+
<manifest xmlns:tools="http://schemas.android.com/tools"
2+
xmlns:android="http://schemas.android.com/apk/res/android">
23
<uses-permission android:name="android.permission.INTERNET" />
34
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
45
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
6+
<uses-permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
7+
tools:ignore="ProtectedPermissions" />
8+
59
<application
610
android:name="${applicationName}"
711
android:icon="@mipmap/ic_launcher"
@@ -14,6 +18,23 @@
1418
<action android:name="android.net.VpnService" />
1519
</intent-filter>
1620
</service>
21+
<service
22+
android:name=".MyTileService"
23+
android:icon="@mipmap/ic_launcher"
24+
android:label="VNT"
25+
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
26+
android:exported="true">
27+
<intent-filter>
28+
<action android:name="android.service.quicksettings.action.QS_TILE" />
29+
</intent-filter>
30+
<meta-data
31+
android:name="android.service.quicksettings.ACTIVE_TILE"
32+
android:value="false" />
33+
34+
<meta-data
35+
android:name="android.service.quicksettings.TOGGLEABLE_TILE"
36+
android:value="true" />
37+
</service>
1738
<meta-data
1839
android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
1940
android:value="true" />
@@ -25,7 +46,9 @@
2546
android:launchMode="singleTop"
2647
android:taskAffinity=""
2748
android:theme="@style/LaunchTheme"
28-
android:windowSoftInputMode="adjustResize">
49+
android:enableOnBackInvokedCallback="false"
50+
android:windowSoftInputMode="adjustResize"
51+
tools:targetApi="tiramisu">
2952
<!-- Specifies an Android theme to apply to this Activity as soon as
3053
the Android process has started. This theme is visible to the user
3154
while the Flutter UI initializes. After that, this theme continues
@@ -37,6 +60,9 @@
3760
<action android:name="android.intent.action.MAIN" />
3861
<category android:name="android.intent.category.LAUNCHER" />
3962
</intent-filter>
63+
<intent-filter>
64+
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
65+
</intent-filter>
4066
</activity>
4167
<!-- Don't delete the meta-data below.
4268
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
package top.wherewego.vnt_app;
2+
3+
import android.os.Build;
4+
import android.service.quicksettings.Tile;
5+
import android.util.Log;
6+
7+
import androidx.annotation.NonNull;
8+
import androidx.annotation.Nullable;
9+
10+
import java.util.ArrayList;
11+
import java.util.List;
12+
import java.util.Map;
13+
import java.util.function.Function;
14+
15+
import io.flutter.embedding.engine.FlutterEngine;
16+
import io.flutter.plugin.common.MethodChannel;
17+
import top.wherewego.vnt_app.vpn.DeviceConfig;
18+
import top.wherewego.vnt_app.vpn.IpUtils;
19+
20+
public class FlutterMethodChannel {
21+
private static final String CHANNEL = "top.wherewego.vnt/vpn";
22+
23+
private volatile static MethodChannel channel;
24+
private volatile static MethodChannel.Result pendingResult;
25+
private volatile static boolean tileStart = false;
26+
27+
public static void setTileStart(boolean tileStart) {
28+
FlutterMethodChannel.tileStart = tileStart;
29+
}
30+
31+
public static boolean initialized() {
32+
return channel != null;
33+
}
34+
35+
public static void init(FlutterEngine flutterEngine, Callback callback) {
36+
channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL);
37+
channel.setMethodCallHandler((call, result) -> {
38+
switch (call.method) {
39+
case "startVpn":
40+
DeviceConfig config;
41+
try {
42+
Map<String, Object> arguments = call.arguments();
43+
config = parseDeviceConfig(arguments);
44+
} catch (Exception e) {
45+
result.error("VPN_ERROR", "Invalid DeviceConfig", e);
46+
return;
47+
}
48+
try {
49+
pendingResult = result;
50+
callback.startVpn(config);
51+
} catch (Exception e) {
52+
result.error("VPN_ERROR", e.getMessage(), e);
53+
}
54+
55+
break;
56+
case "stopVpn":
57+
callback.stopVpn();
58+
result.success(null);
59+
break;
60+
case "moveTaskToBack":
61+
callback.moveToBack();
62+
result.success(null);
63+
break;
64+
case "isTileStart":
65+
result.success(tileStart);
66+
break;
67+
default:
68+
result.notImplemented();
69+
break;
70+
}
71+
}
72+
);
73+
}
74+
75+
public static void callSuccess(int fd) {
76+
if (pendingResult != null) {
77+
pendingResult.success(fd);
78+
}
79+
pendingResult = null;
80+
}
81+
82+
public static void callError(String msg, Exception e) {
83+
if (pendingResult != null) {
84+
pendingResult.error("VPN_ERROR", msg, e);
85+
}
86+
pendingResult = null;
87+
}
88+
89+
public static void startVnt(Function<Boolean, Void> function) {
90+
if (channel == null) {
91+
return;
92+
}
93+
channel.invokeMethod("startVnt", null, new MethodChannel.Result() {
94+
@Override
95+
public void success(@Nullable Object result) {
96+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
97+
function.apply(Boolean.TRUE.equals(result));
98+
}
99+
}
100+
101+
@Override
102+
public void error(@NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
103+
Log.e("FlutterChannel", errorMessage);
104+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
105+
function.apply(false);
106+
}
107+
}
108+
109+
@Override
110+
public void notImplemented() {
111+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
112+
function.apply(false);
113+
}
114+
}
115+
});
116+
}
117+
118+
public static void stopVnt() {
119+
if (channel == null) {
120+
return;
121+
}
122+
channel.invokeMethod("stopVnt", null);
123+
}
124+
125+
public static void isRunning(Function<Boolean, Void> function) {
126+
if (channel == null) {
127+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
128+
function.apply(false);
129+
}
130+
return;
131+
}
132+
channel.invokeMethod("isRunning", null, new MethodChannel.Result() {
133+
@Override
134+
public void success(@Nullable Object result) {
135+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
136+
function.apply(Boolean.TRUE.equals(result));
137+
}
138+
}
139+
140+
@Override
141+
public void error(@NonNull String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
142+
Log.e("FlutterChannel", errorMessage);
143+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
144+
function.apply(false);
145+
}
146+
}
147+
148+
@Override
149+
public void notImplemented() {
150+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
151+
function.apply(false);
152+
}
153+
}
154+
});
155+
}
156+
157+
private static DeviceConfig parseDeviceConfig(Map<String, Object> arguments) {
158+
String virtualIp = (String) arguments.get("virtualIp");
159+
String virtualNetmask = (String) arguments.get("virtualNetmask");
160+
String virtualGateway = (String) arguments.get("virtualGateway");
161+
Integer mtu = (Integer) arguments.get("mtu");
162+
163+
List<Map<String, String>> externalRouteList = (List<Map<String, String>>) arguments.get("externalRoute");
164+
List<DeviceConfig.Route> externalRoute = new ArrayList<>();
165+
if (externalRouteList != null) {
166+
for (Map<String, String> route : externalRouteList) {
167+
String destination = route.get("destination");
168+
String netmask = route.get("netmask");
169+
externalRoute.add(new DeviceConfig.Route(IpUtils.ipToInt(destination), IpUtils.ipToInt(netmask)));
170+
}
171+
}
172+
return new DeviceConfig(IpUtils.ipToInt(virtualIp), IpUtils.ipToInt(virtualNetmask), IpUtils.ipToInt(virtualGateway), mtu, externalRoute);
173+
}
174+
175+
public interface Callback {
176+
int startVpn(DeviceConfig config);
177+
178+
void stopVpn();
179+
180+
void moveToBack();
181+
}
182+
}

android/app/src/main/java/top/wherewego/vnt_app/MainActivity.java

Lines changed: 23 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2,90 +2,57 @@
22

33
import android.content.Intent;
44
import android.net.VpnService;
5+
import android.os.Build;
56
import android.os.Bundle;
6-
import android.util.Log;
77

88
import androidx.annotation.NonNull;
99

10-
import java.util.ArrayList;
11-
import java.util.List;
12-
import java.util.Map;
13-
1410
import io.flutter.embedding.android.FlutterActivity;
1511
import io.flutter.embedding.engine.FlutterEngine;
16-
import io.flutter.plugin.common.MethodChannel;
1712
import top.wherewego.vnt_app.vpn.DeviceConfig;
18-
import top.wherewego.vnt_app.vpn.IpUtils;
1913
import top.wherewego.vnt_app.vpn.MyVpnService;
2014

2115
public class MainActivity extends FlutterActivity {
22-
private static final String CHANNEL = "top.wherewego.vnt/vpn";
2316
private static final int VPN_REQUEST_CODE = 1;
24-
private static final String TAG = "MainActivity";
25-
public static MethodChannel channel;
26-
2717
@Override
2818
protected void onCreate(Bundle savedInstanceState) {
2919
super.onCreate(savedInstanceState);
3020
}
21+
3122
@Override
3223
protected void onDestroy() {
3324
super.onDestroy();
3425
MyVpnService.stopVpn();
3526
}
27+
3628
@Override
3729
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
3830
super.configureFlutterEngine(flutterEngine);
39-
channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL);
40-
channel.setMethodCallHandler((call, result) -> {
41-
Log.i("moveTaskToBack","moveTaskToBack");
42-
if (call.method.equals("startVpn")) {
43-
try {
44-
Map<String, Object> arguments = call.arguments();
45-
DeviceConfig config = parseDeviceConfig(arguments);
46-
startVpnService(config, result);
47-
} catch (Exception e) {
48-
result.error("VPN_ERROR", "Invalid DeviceConfig", e);
49-
}
50-
} else if (call.method.equals("stopVpn")) {
51-
try {
52-
MyVpnService.stopVpn();
53-
} catch (Exception e) {
54-
result.error("VPN_ERROR", "stopVpn", e);
55-
}
56-
result.success(null);
57-
}else if (call.method.equals("moveTaskToBack")) {
58-
// 将应用移至后台而不是退出应用
59-
moveTaskToBack(true);
60-
result.success(null);
61-
} else {
62-
result.notImplemented();
63-
}
31+
FlutterMethodChannel.init(flutterEngine, new FlutterMethodChannel.Callback() {
32+
@Override
33+
public int startVpn(DeviceConfig config) {
34+
startVpnService(config);
35+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
36+
MyTileService.setState(true);
6437
}
65-
);
66-
67-
}
38+
return 0;
39+
}
6840

69-
private DeviceConfig parseDeviceConfig(Map<String, Object> arguments) {
70-
String virtualIp = (String) arguments.get("virtualIp");
71-
String virtualNetmask = (String) arguments.get("virtualNetmask");
72-
String virtualGateway = (String) arguments.get("virtualGateway");
73-
Integer mtu = (Integer) arguments.get("mtu");
41+
@Override
42+
public void stopVpn() {
43+
MyVpnService.stopVpn();
44+
}
7445

75-
List<Map<String, String>> externalRouteList = (List<Map<String, String>>) arguments.get("externalRoute");
76-
List<DeviceConfig.Route> externalRoute = new ArrayList<>();
77-
if (externalRouteList != null) {
78-
for (Map<String, String> route : externalRouteList) {
79-
String destination = route.get("destination");
80-
String netmask = route.get("netmask");
81-
externalRoute.add(new DeviceConfig.Route(IpUtils.ipToInt(destination), IpUtils.ipToInt(netmask)));
46+
@Override
47+
public void moveToBack() {
48+
moveTaskToBack(true);
8249
}
83-
}
84-
return new DeviceConfig(IpUtils.ipToInt(virtualIp), IpUtils.ipToInt(virtualNetmask), IpUtils.ipToInt(virtualGateway), mtu, externalRoute);
50+
});
8551
}
8652

87-
private void startVpnService(DeviceConfig config, MethodChannel.Result result) {
88-
MyVpnService.pendingResult = result;
53+
54+
55+
private void startVpnService(DeviceConfig config) {
8956
MyVpnService.pendingConfig = config;
9057
Intent intent = VpnService.prepare(this);
9158
if (intent != null) {
@@ -103,7 +70,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
10370
startService(serviceIntent);
10471
} else {
10572
// 用户拒绝授权,返回错误结果给 Flutter
106-
MyVpnService.callError("User denied VPN authorization", null);
73+
FlutterMethodChannel.callError("User denied VPN authorization", null);
10774
}
10875
}
10976
super.onActivityResult(requestCode, resultCode, data);

0 commit comments

Comments
 (0)