From 23286dd061a7638a1d71cea4533758f8c059d650 Mon Sep 17 00:00:00 2001 From: michael Date: Sat, 21 Oct 2023 00:18:34 +0300 Subject: [PATCH] bugged FileObserver --- app/build.gradle.kts | 1 + .../java/com/ea/ironmonkey/GameActivity.kt | 18 ++-- .../devmenu/data/util/BaseObserver.kt | 90 +++++++++++++++++++ .../megboyzz/devmenu/data/util/FileEvent.kt | 22 +++++ .../devmenu/data/util/SaveFileObserver.kt | 48 ++++++++-- 5 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/BaseObserver.kt create mode 100644 devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/FileEvent.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 204ba88..178e52f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -79,6 +79,7 @@ dependencies { implementation(libs.ui.graphics) implementation(libs.ui.tooling.preview) implementation(libs.material3) + implementation(project(":devmenu:data")) testImplementation(libs.junit.v412) //testImplementation(libs.junit) androidTestImplementation(libs.androidx.test.ext.junit) diff --git a/app/src/main/java/com/ea/ironmonkey/GameActivity.kt b/app/src/main/java/com/ea/ironmonkey/GameActivity.kt index 4b06239..ff2b981 100644 --- a/app/src/main/java/com/ea/ironmonkey/GameActivity.kt +++ b/app/src/main/java/com/ea/ironmonkey/GameActivity.kt @@ -25,6 +25,7 @@ import com.ea.easp.EASPHandler import com.ea.games.nfs13_na.BuildConfig import com.ea.games.nfs13_na.R import com.ea.nimble.ApplicationLifecycle +import com.megboyzz.devmenu.data.util.SaveFileObserver import java.io.BufferedReader import java.io.File import java.io.FileReader @@ -52,12 +53,15 @@ class GameActivity : ComponentActivity(), DrawFrameListener { private lateinit var gameRenderer: GameRenderer lateinit var runLoop: RunLoop lateinit var gameGLSurfaceView: GameGLSurfaceView - // get() = field - // private set - private lateinit var accelerometer: Accelerometer + lateinit var accelerometer: Accelerometer private lateinit var mWakeLock: WakeLock private lateinit var mAudioManager: AudioManager + private val saveFile = "/data/data/${BuildConfig.APPLICATION_ID}/files/var/nfstr_save.sb" + private val dest = "/sdcard/Android/data/${BuildConfig.APPLICATION_ID}/files/saveTracking" + + private val saveFileObserver = SaveFileObserver(saveFile, dest) + companion object { const val STATE_RESTORE_CONTEXT = 7 @@ -92,8 +96,6 @@ class GameActivity : ComponentActivity(), DrawFrameListener { Log.d(this.localClassName, "Call garbage collector") System.gc() } - //fun getGameGLSurfaceView() = gameGLSurfaceView - fun getAccelerometer() = accelerometer private fun ForceHideVirtualKeyboard() { @@ -292,6 +294,9 @@ class GameActivity : ComponentActivity(), DrawFrameListener { } public override fun onCreate(bundle: Bundle?) { + + saveFileObserver.startWatching() + super.onCreate(bundle) val className = this.localClassName @@ -346,6 +351,9 @@ class GameActivity : ComponentActivity(), DrawFrameListener { public override fun onDestroy() { + + saveFileObserver.stopWatching() + Log.i("Debug", "onDestroy") Log.i(this.localClassName, "onDestroy") super.onDestroy() diff --git a/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/BaseObserver.kt b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/BaseObserver.kt new file mode 100644 index 0000000..feecd52 --- /dev/null +++ b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/BaseObserver.kt @@ -0,0 +1,90 @@ +package com.megboyzz.devmenu.data.util + +import android.os.FileObserver +import android.util.Log +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import java.util.LinkedList +import java.util.Queue +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +abstract class BaseObserver(source: String) : FileObserver(source) { + + private val eventQueue : Queue = LinkedList() + + @OptIn(ExperimentalStdlibApi::class) + final override fun onEvent(event: Int, path: String?) { + + if(eventQueue.size < 3) eventQueue.add(event) + + val e = FileEvent.entries.find { it.value == event } + + val fileEvent = e ?: FileEvent.UNKNOWN + + Log.i("BaseObserver", "event code = ${event.toHexString()}, FileEvent is $fileEvent") + + if(hasLastThreeEvents()) onEvent(fileEvent, path) + } + + private fun hasLastThreeEvents() = runCatching { + eventQueue.size == 3 && eventQueue.poll() == OPEN && eventQueue.poll() == ACCESS && eventQueue.poll() == CLOSE_NOWRITE + }.getOrNull() ?: false + + abstract fun onEvent(event: FileEvent, path: String?) + + protected fun File.list(regex: String = ".") = + this.list { dir, _ -> Regex(regex).matches(dir.absolutePath) }?.asList() ?: listOf() + + + protected infix fun File.copyToThe(destination: File) = this.copyTo(destination, overwrite = true) + + protected fun zipAll(outputFolder: String, filesToCompress: List) { + if(filesToCompress.isEmpty()){ + Log.i("BaseObserver", "no files exit!") + return + } + try { + // Проверяем, существует ли выходная папка. Если нет, создаем её. + val outputDir = File(outputFolder) + if (!outputDir.exists()) outputDir.mkdirs() + + // Генерируем имя ZIP-архива на основе времени + val timestamp = System.currentTimeMillis() + val zipFileName = "$outputFolder/saves_$timestamp.zip" + val zipOutputStream = ZipOutputStream(FileOutputStream(zipFileName)) + + for (fileToCompress in filesToCompress) { + // Проверяем, существует ли файл + val inputFile = File(fileToCompress) + if (inputFile.exists()) { + // Создаем объект ZipEntry для каждого файла + val zipEntry = ZipEntry(inputFile.name) + zipOutputStream.putNextEntry(zipEntry) + + // Читаем содержимое файла и записываем его в ZIP-архив + val fileInputStream = FileInputStream(inputFile) + val buffer = ByteArray(1024) + var len: Int + while (fileInputStream.read(buffer).also { len = it } > 0) { + zipOutputStream.write(buffer, 0, len) + } + fileInputStream.close() + + // Закрываем текущий ZipEntry + zipOutputStream.closeEntry() + } else { + Log.i("SaveFileObserver", "Файл не существует: $fileToCompress") + } + } + + // Закрываем ZipOutputStream + zipOutputStream.close() + Log.i("SaveFileObserver", "Архивация завершена. Архив создан: $zipFileName") + } catch (e: Exception) { + e.printStackTrace() + } + } + +} \ No newline at end of file diff --git a/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/FileEvent.kt b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/FileEvent.kt new file mode 100644 index 0000000..2e0ae60 --- /dev/null +++ b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/FileEvent.kt @@ -0,0 +1,22 @@ +package com.megboyzz.devmenu.data.util + +import android.os.FileObserver + +enum class FileEvent(val value: Int) { + + ACCESS(FileObserver.ACCESS), + MODIFY(FileObserver.MODIFY), + ATTRIB(FileObserver.ATTRIB), + CLOSE_WRITE(FileObserver.CLOSE_WRITE), + CLOSE_NOWRITE(FileObserver.CLOSE_NOWRITE), + OPEN(FileObserver.OPEN), + MOVED_FROM(FileObserver.MOVED_FROM), + MOVED_TO(FileObserver.MOVED_TO), + CREATE(FileObserver.CREATE), + DELETE(FileObserver.DELETE), + DELETE_SELF(FileObserver.DELETE_SELF), + MOVE_SELF(FileObserver.MOVE_SELF), + UNKNOWN(-1); + + override fun toString() = this.name +} \ No newline at end of file diff --git a/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/SaveFileObserver.kt b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/SaveFileObserver.kt index 50c24ab..88f0d52 100644 --- a/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/SaveFileObserver.kt +++ b/devmenu/data/src/main/java/com/megboyzz/devmenu/data/util/SaveFileObserver.kt @@ -1,22 +1,52 @@ package com.megboyzz.devmenu.data.util -import android.os.FileObserver import android.util.Log import java.io.File +import kotlin.concurrent.thread class SaveFileObserver( - private val source: File, - private val destinationFolder: File -) : FileObserver(source.absolutePath) { + private val source: String, + private val destinationFolder: String +) : BaseObserver(source) { - override fun onEvent(event: Int, path: String?) { - if (event and CREATE != 0 || event and MODIFY != 0) { - Log.d("FileObserver", "File created or modified: $source") - source copyToThe destinationFolder + private var countSnapshots: Int = 1 + + private val checkList = listOf( + FileEvent.CLOSE_WRITE, + FileEvent.MODIFY, + FileEvent.DELETE, + FileEvent.CREATE, + FileEvent.ACCESS, + FileEvent.OPEN, + FileEvent.CLOSE_NOWRITE + ) + init { + val dest = File(destinationFolder) + if(dest.isFile) dest.delete() + if(!dest.exists()) dest.mkdir() + val list = dest.list{ + dir, name -> name.endsWith(".sb") + } + list?.forEach { File(dest, it).delete() } + File(source) copyToThe File(destinationFolder, "BASE.sb") + } + + override fun onEvent(event: FileEvent, path: String?) { + Log.i("SaveFileObserver", "onEvent $event") + if (event in checkList) { + Log.d("SaveFileObserver", "File created or modified: $source") + File(source) copyToThe File(destinationFolder, "$event-$countSnapshots.sb") + countSnapshots++ } } - private infix fun File.copyToThe(destination: File) = this.copyTo(destination) + override fun stopWatching() { + super.stopWatching() + val filesList = File(destinationFolder).list{ _, name -> name.endsWith(".sb") }?.map { it } + if (filesList != null) { + zipAll(destinationFolder, filesList) + } + } }