Skip to content

Commit dc3d1d6

Browse files
author
code3-dev
committed
fix http
1 parent c710394 commit dc3d1d6

File tree

12 files changed

+730
-75
lines changed

12 files changed

+730
-75
lines changed

android/app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ android {
2626
// For more information, see: https://flutter.dev/to/review-gradle-config.
2727
minSdk = flutter.minSdkVersion
2828
targetSdk = flutter.targetSdkVersion
29-
versionCode = 26
30-
versionName = "3.0.0"
29+
versionCode = 28
30+
versionName = "3.1.0"
3131
}
3232

3333
buildTypes {

android/app/src/main/AndroidManifest.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
<uses-permission android:name="android.permission.INTERNET" />
44
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
55
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
6+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
67
<uses-permission android:name="android.permission.WAKE_LOCK" />
78
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
89
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
910
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
11+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
1012
<application
1113
android:label="Proxy Cloud"
1214
android:name="${applicationName}"
@@ -33,6 +35,14 @@
3335
<category android:name="android.intent.category.LAUNCHER"/>
3436
</intent-filter>
3537
</activity>
38+
39+
<!-- VPN Traffic Background Service -->
40+
<service
41+
android:name=".VpnTrafficService"
42+
android:enabled="true"
43+
android:exported="false"
44+
android:foregroundServiceType="dataSync" />
45+
3646
<!-- Don't delete the meta-data below.
3747
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
3848
<meta-data

android/app/src/main/kotlin/com/cloud/pira/MainActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ class MainActivity : FlutterActivity() {
88
super.configureFlutterEngine(flutterEngine)
99
AppListMethodChannel.registerWith(flutterEngine, context)
1010
PingMethodChannel.registerWith(flutterEngine, context)
11+
VpnTrafficMethodChannel.registerWith(flutterEngine, context)
1112
}
1213
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.cloud.pira
2+
3+
import android.content.Context
4+
import android.content.Intent
5+
import io.flutter.embedding.engine.FlutterEngine
6+
import io.flutter.plugin.common.MethodCall
7+
import io.flutter.plugin.common.MethodChannel
8+
import io.flutter.plugin.common.MethodChannel.Result
9+
10+
class VpnTrafficMethodChannel {
11+
companion object {
12+
private const val CHANNEL = "com.cloud.pira/vpn_traffic"
13+
14+
fun registerWith(flutterEngine: FlutterEngine, context: Context) {
15+
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
16+
channel.setMethodCallHandler { call, result ->
17+
when (call.method) {
18+
"startTrafficService" -> {
19+
startTrafficService(context)
20+
result.success(true)
21+
}
22+
"stopTrafficService" -> {
23+
stopTrafficService(context)
24+
result.success(true)
25+
}
26+
"updateTraffic" -> {
27+
val upload = call.argument<Long>("upload") ?: 0L
28+
val download = call.argument<Long>("download") ?: 0L
29+
updateTraffic(context, upload, download)
30+
result.success(true)
31+
}
32+
"isServiceRunning" -> {
33+
result.success(VpnTrafficService.isRunning())
34+
}
35+
"getTrafficData" -> {
36+
val data = getTrafficData(context)
37+
result.success(data)
38+
}
39+
"resetTrafficData" -> {
40+
resetTrafficData(context)
41+
result.success(true)
42+
}
43+
else -> {
44+
result.notImplemented()
45+
}
46+
}
47+
}
48+
}
49+
50+
private fun startTrafficService(context: Context) {
51+
val intent = Intent(context, VpnTrafficService::class.java).apply {
52+
action = VpnTrafficService.ACTION_START_SERVICE
53+
}
54+
context.startForegroundService(intent)
55+
}
56+
57+
private fun stopTrafficService(context: Context) {
58+
val intent = Intent(context, VpnTrafficService::class.java).apply {
59+
action = VpnTrafficService.ACTION_STOP_SERVICE
60+
}
61+
context.startService(intent)
62+
}
63+
64+
private fun updateTraffic(context: Context, upload: Long, download: Long) {
65+
val intent = Intent(context, VpnTrafficService::class.java).apply {
66+
action = VpnTrafficService.ACTION_UPDATE_TRAFFIC
67+
putExtra("upload", upload)
68+
putExtra("download", download)
69+
}
70+
context.startService(intent)
71+
}
72+
73+
private fun getTrafficData(context: Context): Map<String, Any> {
74+
val prefs = context.getSharedPreferences("vpn_traffic_prefs", Context.MODE_PRIVATE)
75+
return mapOf(
76+
"uploadBytes" to prefs.getLong("upload_bytes", 0),
77+
"downloadBytes" to prefs.getLong("download_bytes", 0),
78+
"totalConnectedTime" to prefs.getLong("total_connected_time", 0),
79+
"sessionStartTime" to prefs.getLong("session_start_time", 0)
80+
)
81+
}
82+
83+
private fun resetTrafficData(context: Context) {
84+
val prefs = context.getSharedPreferences("vpn_traffic_prefs", Context.MODE_PRIVATE)
85+
prefs.edit().apply {
86+
putLong("upload_bytes", 0)
87+
putLong("download_bytes", 0)
88+
putLong("total_connected_time", 0)
89+
putLong("session_start_time", System.currentTimeMillis())
90+
putLong("last_update_time", System.currentTimeMillis())
91+
apply()
92+
}
93+
}
94+
}
95+
}
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
package com.cloud.pira
2+
3+
import android.app.*
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.content.SharedPreferences
7+
import android.os.Build
8+
import android.os.IBinder
9+
import android.os.Handler
10+
import android.os.Looper
11+
import androidx.core.app.NotificationCompat
12+
import androidx.core.app.NotificationManagerCompat
13+
import io.flutter.Log
14+
import java.text.SimpleDateFormat
15+
import java.util.*
16+
17+
class VpnTrafficService : Service() {
18+
companion object {
19+
const val CHANNEL_ID = "VPN_TRAFFIC_CHANNEL"
20+
const val NOTIFICATION_ID = 1001
21+
const val ACTION_START_SERVICE = "START_VPN_TRAFFIC_SERVICE"
22+
const val ACTION_STOP_SERVICE = "STOP_VPN_TRAFFIC_SERVICE"
23+
const val ACTION_UPDATE_TRAFFIC = "UPDATE_TRAFFIC"
24+
25+
private const val PREFS_NAME = "vpn_traffic_prefs"
26+
private const val KEY_UPLOAD_BYTES = "upload_bytes"
27+
private const val KEY_DOWNLOAD_BYTES = "download_bytes"
28+
private const val KEY_SESSION_START = "session_start_time"
29+
private const val KEY_TOTAL_CONNECTED_TIME = "total_connected_time"
30+
private const val KEY_LAST_UPDATE_TIME = "last_update_time"
31+
32+
private var isServiceRunning = false
33+
34+
fun isRunning(): Boolean {
35+
return isServiceRunning
36+
}
37+
}
38+
39+
private lateinit var notificationManager: NotificationManagerCompat
40+
private lateinit var sharedPreferences: SharedPreferences
41+
private val handler = Handler(Looper.getMainLooper())
42+
private var updateRunnable: Runnable? = null
43+
44+
private var sessionStartTime: Long = 0
45+
private var lastUpdateTime: Long = 0
46+
private var uploadBytes: Long = 0
47+
private var downloadBytes: Long = 0
48+
private var totalConnectedTime: Long = 0
49+
50+
override fun onCreate() {
51+
super.onCreate()
52+
notificationManager = NotificationManagerCompat.from(this)
53+
sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
54+
createNotificationChannel()
55+
loadTrafficData()
56+
Log.d("VpnTrafficService", "Service created")
57+
}
58+
59+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
60+
when (intent?.action) {
61+
ACTION_START_SERVICE -> {
62+
startTrafficMonitoring()
63+
isServiceRunning = true
64+
}
65+
ACTION_STOP_SERVICE -> {
66+
stopTrafficMonitoring()
67+
isServiceRunning = false
68+
stopSelf()
69+
}
70+
ACTION_UPDATE_TRAFFIC -> {
71+
val upload = intent.getLongExtra("upload", 0)
72+
val download = intent.getLongExtra("download", 0)
73+
updateTrafficData(upload, download)
74+
}
75+
}
76+
return START_STICKY
77+
}
78+
79+
override fun onBind(intent: Intent?): IBinder? = null
80+
81+
private fun createNotificationChannel() {
82+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
83+
val channel = NotificationChannel(
84+
CHANNEL_ID,
85+
"VPN Traffic Monitor",
86+
NotificationManager.IMPORTANCE_LOW
87+
).apply {
88+
description = "Shows VPN traffic usage in real-time"
89+
setShowBadge(false)
90+
enableVibration(false)
91+
setSound(null, null)
92+
}
93+
94+
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
95+
notificationManager.createNotificationChannel(channel)
96+
}
97+
}
98+
99+
private fun startTrafficMonitoring() {
100+
sessionStartTime = System.currentTimeMillis()
101+
lastUpdateTime = sessionStartTime
102+
saveSessionStartTime()
103+
104+
val notification = createNotification()
105+
startForeground(NOTIFICATION_ID, notification)
106+
107+
// Update notification every 2 seconds
108+
updateRunnable = object : Runnable {
109+
override fun run() {
110+
updateNotification()
111+
handler.postDelayed(this, 2000)
112+
}
113+
}
114+
updateRunnable?.let { handler.post(it) }
115+
116+
Log.d("VpnTrafficService", "Traffic monitoring started")
117+
}
118+
119+
private fun stopTrafficMonitoring() {
120+
updateRunnable?.let { handler.removeCallbacks(it) }
121+
saveTrafficData()
122+
stopForeground(true)
123+
Log.d("VpnTrafficService", "Traffic monitoring stopped")
124+
}
125+
126+
private fun updateTrafficData(upload: Long, download: Long) {
127+
uploadBytes = upload
128+
downloadBytes = download
129+
130+
// Update total connected time
131+
val currentTime = System.currentTimeMillis()
132+
if (lastUpdateTime > 0) {
133+
totalConnectedTime += (currentTime - lastUpdateTime) / 1000
134+
}
135+
lastUpdateTime = currentTime
136+
137+
saveTrafficData()
138+
updateNotification()
139+
}
140+
141+
private fun createNotification(): Notification {
142+
val stopIntent = Intent(this, VpnTrafficService::class.java).apply {
143+
action = ACTION_STOP_SERVICE
144+
}
145+
val stopPendingIntent = PendingIntent.getService(
146+
this, 0, stopIntent,
147+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
148+
)
149+
150+
val openAppIntent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
151+
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
152+
}
153+
val openAppPendingIntent = PendingIntent.getActivity(
154+
this, 0, openAppIntent,
155+
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
156+
)
157+
158+
return NotificationCompat.Builder(this, CHANNEL_ID)
159+
.setContentTitle("VPN Connected")
160+
.setContentText(getTrafficSummary())
161+
.setSmallIcon(android.R.drawable.ic_menu_info_details)
162+
.setOngoing(true)
163+
.setPriority(NotificationCompat.PRIORITY_LOW)
164+
.setContentIntent(openAppPendingIntent)
165+
.addAction(
166+
android.R.drawable.ic_media_pause,
167+
"Disconnect",
168+
stopPendingIntent
169+
)
170+
.setStyle(NotificationCompat.BigTextStyle()
171+
.bigText(getDetailedTrafficInfo()))
172+
.build()
173+
}
174+
175+
private fun updateNotification() {
176+
val notification = createNotification()
177+
notificationManager.notify(NOTIFICATION_ID, notification)
178+
}
179+
180+
private fun getTrafficSummary(): String {
181+
val totalTraffic = uploadBytes + downloadBytes
182+
val connectedTime = getFormattedConnectedTime()
183+
return "${formatBytes(uploadBytes)}${formatBytes(downloadBytes)} | $connectedTime"
184+
}
185+
186+
private fun getDetailedTrafficInfo(): String {
187+
val totalTraffic = uploadBytes + downloadBytes
188+
val connectedTime = getFormattedConnectedTime()
189+
val currentTime = SimpleDateFormat("HH:mm", Locale.getDefault()).format(Date())
190+
191+
return """
192+
Total Traffic: ${formatBytes(totalTraffic)}
193+
Upload: ${formatBytes(uploadBytes)}
194+
Download: ${formatBytes(downloadBytes)}
195+
Connected Time: $connectedTime
196+
Last Update: $currentTime
197+
""".trimIndent()
198+
}
199+
200+
private fun getFormattedConnectedTime(): String {
201+
val currentSessionTime = if (sessionStartTime > 0) {
202+
(System.currentTimeMillis() - sessionStartTime) / 1000
203+
} else 0
204+
205+
val totalTime = totalConnectedTime + currentSessionTime
206+
val hours = totalTime / 3600
207+
val minutes = (totalTime % 3600) / 60
208+
val seconds = totalTime % 60
209+
210+
return String.format("%02d:%02d:%02d", hours, minutes, seconds)
211+
}
212+
213+
private fun formatBytes(bytes: Long): String {
214+
return when {
215+
bytes < 1024 -> "${bytes}B"
216+
bytes < 1024 * 1024 -> "${(bytes / 1024.0).let { "%.1f".format(it) }}KB"
217+
bytes < 1024 * 1024 * 1024 -> "${(bytes / (1024.0 * 1024)).let { "%.1f".format(it) }}MB"
218+
else -> "${(bytes / (1024.0 * 1024 * 1024)).let { "%.1f".format(it) }}GB"
219+
}
220+
}
221+
222+
private fun loadTrafficData() {
223+
uploadBytes = sharedPreferences.getLong(KEY_UPLOAD_BYTES, 0)
224+
downloadBytes = sharedPreferences.getLong(KEY_DOWNLOAD_BYTES, 0)
225+
totalConnectedTime = sharedPreferences.getLong(KEY_TOTAL_CONNECTED_TIME, 0)
226+
sessionStartTime = sharedPreferences.getLong(KEY_SESSION_START, 0)
227+
lastUpdateTime = sharedPreferences.getLong(KEY_LAST_UPDATE_TIME, 0)
228+
}
229+
230+
private fun saveTrafficData() {
231+
sharedPreferences.edit().apply {
232+
putLong(KEY_UPLOAD_BYTES, uploadBytes)
233+
putLong(KEY_DOWNLOAD_BYTES, downloadBytes)
234+
putLong(KEY_TOTAL_CONNECTED_TIME, totalConnectedTime)
235+
putLong(KEY_LAST_UPDATE_TIME, lastUpdateTime)
236+
apply()
237+
}
238+
}
239+
240+
private fun saveSessionStartTime() {
241+
sharedPreferences.edit().apply {
242+
putLong(KEY_SESSION_START, sessionStartTime)
243+
apply()
244+
}
245+
}
246+
247+
override fun onDestroy() {
248+
super.onDestroy()
249+
updateRunnable?.let { handler.removeCallbacks(it) }
250+
saveTrafficData()
251+
isServiceRunning = false
252+
Log.d("VpnTrafficService", "Service destroyed")
253+
}
254+
}

0 commit comments

Comments
 (0)