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