@@ -7,15 +7,23 @@ import android.os.StatFs
77import android.util.Log
88import android.view.KeyEvent
99import io.flutter.embedding.android.FlutterActivity
10+ import io.flutter.plugin.common.EventChannel
1011import io.flutter.plugin.common.MethodChannel
1112import java.io.*
13+ import java.util.concurrent.Executors
1214import java.util.zip.ZipInputStream
1315
1416class MainActivity : FlutterActivity () {
1517 private val CHANNEL = " com.retro.rshop/zip"
1618 private val STORAGE_CHANNEL = " com.retro.rshop/storage"
19+ private val PROGRESS_CHANNEL = " com.retro.rshop/zip_progress"
1720 private val TAG = " MainActivity"
1821
22+ private val extractorPool = Executors .newFixedThreadPool(2 )
23+ private val mainHandler = Handler (Looper .getMainLooper())
24+
25+ private var progressSink: EventChannel .EventSink ? = null
26+
1927 override fun onCreate (savedInstanceState : Bundle ? ) {
2028 super .onCreate(savedInstanceState)
2129 Log .d(TAG , " MainActivity onCreate" )
@@ -24,29 +32,40 @@ class MainActivity : FlutterActivity() {
2432 override fun configureFlutterEngine (flutterEngine : io.flutter.embedding.engine.FlutterEngine ) {
2533 super .configureFlutterEngine(flutterEngine)
2634
35+ EventChannel (flutterEngine.dartExecutor.binaryMessenger, PROGRESS_CHANNEL ).setStreamHandler(
36+ object : EventChannel .StreamHandler {
37+ override fun onListen (arguments : Any? , events : EventChannel .EventSink ? ) {
38+ progressSink = events
39+ }
40+ override fun onCancel (arguments : Any? ) {
41+ progressSink = null
42+ }
43+ }
44+ )
45+
2746 MethodChannel (flutterEngine.dartExecutor.binaryMessenger, CHANNEL ).setMethodCallHandler { call, result ->
2847 when (call.method) {
2948 " extractZip" -> {
3049 val zipPath = call.argument<String >(" zipPath" )
3150 val targetPath = call.argument<String >(" targetPath" )
32-
51+
3352 if (zipPath == null || targetPath == null ) {
3453 result.error(" INVALID_ARGS" , " zipPath and targetPath required" , null )
3554 return @setMethodCallHandler
3655 }
3756
38- Thread {
57+ extractorPool.execute {
3958 try {
4059 val extractedFiles = extractZip(zipPath, targetPath)
41- Handler ( Looper .getMainLooper()) .post {
60+ mainHandler .post {
4261 result.success(extractedFiles)
4362 }
4463 } catch (e: Exception ) {
45- Handler ( Looper .getMainLooper()) .post {
64+ mainHandler .post {
4665 result.error(" EXTRACT_ERROR" , e.message, null )
4766 }
4867 }
49- }.start()
68+ }
5069 }
5170 else -> result.notImplemented()
5271 }
@@ -69,11 +88,35 @@ class MainActivity : FlutterActivity() {
6988 result.error(" STAT_ERROR" , e.message, null )
7089 }
7190 }
91+ " getDeviceMemory" -> {
92+ try {
93+ val am = getSystemService(android.content.Context .ACTIVITY_SERVICE ) as android.app.ActivityManager
94+ val memInfo = android.app.ActivityManager .MemoryInfo ()
95+ am.getMemoryInfo(memInfo)
96+ result.success(mapOf (" totalBytes" to memInfo.totalMem))
97+ } catch (e: Exception ) {
98+ result.error(" MEMORY_ERROR" , e.message, null )
99+ }
100+ }
72101 else -> result.notImplemented()
73102 }
74103 }
75104 }
76105
106+ override fun onDestroy () {
107+ extractorPool.shutdown()
108+ super .onDestroy()
109+ }
110+
111+ private fun sendProgress (extractedBytes : Long , totalBytes : Long ) {
112+ val sink = progressSink ? : return
113+ if (totalBytes <= 0 ) return
114+ val percent = (extractedBytes.toDouble() / totalBytes * 100 ).toInt().coerceIn(0 , 100 )
115+ mainHandler.post {
116+ sink.success(mapOf (" extracted" to extractedBytes, " total" to totalBytes, " percent" to percent))
117+ }
118+ }
119+
77120 private fun extractZip (zipPath : String , targetPath : String ): List <String > {
78121 val extractedFiles = mutableListOf<String >()
79122 val buffer = ByteArray (8192 )
@@ -97,6 +140,7 @@ class MainActivity : FlutterActivity() {
97140 var entry = zis.nextEntry
98141
99142 val canonicalTarget = File (targetPath).canonicalPath
143+ var lastReportedPercent = - 1
100144
101145 while (entry != null ) {
102146 val file = File (targetPath, entry.name)
@@ -112,21 +156,30 @@ class MainActivity : FlutterActivity() {
112156 file.mkdirs()
113157 } else {
114158 file.parentFile?.mkdirs()
115-
159+
116160 FileOutputStream (file).use { fos ->
117161 var len: Int
118162 while (zis.read(buffer).also { len = it } > 0 ) {
119163 fos.write(buffer, 0 , len)
120164 extractedBytes + = len
121165 }
122166 }
123-
167+
124168 extractedFiles.add(entry.name)
169+
170+ // Report progress (throttle: only on percent change)
171+ if (totalBytes > 0 ) {
172+ val currentPercent = (extractedBytes.toDouble() / totalBytes * 100 ).toInt()
173+ if (currentPercent != lastReportedPercent) {
174+ lastReportedPercent = currentPercent
175+ sendProgress(extractedBytes, totalBytes)
176+ }
177+ }
125178 }
126-
179+
127180 entry = zis.nextEntry
128181 }
129-
182+
130183 zis.close()
131184 }
132185
0 commit comments