Conversation
Updated comments and fixed parameter errors in the APK build workflow.
Updated the build workflow for APKs, including fixes to comments, environment setup, and build steps. Enhanced error handling and output reporting.
Updated comments and steps in the build APK workflow for clarity and accuracy.
Updated comments and fixed NDK version to match project requirements. Improved build steps and added detailed logging for APK builds.
Updated the build workflow to match the latest build.gradle configurations, including SDK versions and build tools.
Updated the build workflow to include diagnostic information and enhanced error logging for failed builds.
Updated the workflow to include detailed steps for building the RAL APK, including environment setup, SDK component installation, and artifact upload.
Updated build process for APKs to include stack traces and improved error handling for APK file detection. Adjusted paths for uploaded artifacts and modified logging messages for clarity.
[Bug] v2.0.1 Release 缺少 runtime_libs.tar.xz 文件
There was a problem hiding this comment.
Pull request overview
This PR primarily targets adding Android 7.1 (API 25) compatibility across the project by lowering minSdk, enabling desugaring, and removing usages of java.nio.file.Path/kotlin.io.path in favor of java.io.File, plus a few runtime/workflow adjustments to avoid crashes on older devices.
Changes:
- Lower Android
minSdkto 25 and align toolchains/JVM targets to Java 17; enable core library desugaring. - Replace
Path/kotlin.io.pathusage withFilethroughout patching, extraction, and shared storage code paths. - Add Android 7/old-API safety fallbacks (Haze blur opt-out, edge-to-edge guarding, linker namespace bypass gating), and adjust CI workflows.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
shared/src/commonMain/kotlin/com/app/ralaunch/shared/core/util/HazeUtils.kt |
Introduces expect modifier helper for conditional Haze usage. |
shared/src/androidMain/kotlin/com/app/ralaunch/shared/core/util/HazeUtils.android.kt |
Android actual implementation: only apply haze on API 31+. |
shared/src/commonMain/kotlin/com/app/ralaunch/shared/core/model/domain/GameItem.kt |
Switches path joining to java.io.File. |
shared/src/commonMain/kotlin/com/app/ralaunch/shared/core/data/repository/SettingsRepositoryImpl.kt |
Migrates settings file IO from kotlin.io.path to File + rename-based persistence. |
shared/src/commonMain/kotlin/com/app/ralaunch/shared/core/data/local/CommonGameListStorage.kt |
Migrates game list storage IO from kotlin.io.path to File. |
shared/src/commonMain/kotlin/com/app/ralaunch/shared/core/data/local/CommonControlLayoutStorage.kt |
Migrates control layout storage IO from kotlin.io.path to File. |
shared/src/androidMain/kotlin/com/app/ralaunch/core/platform/runtime/renderer/RendererRegistry.kt |
Replaces Path.resolve with File for MG dir env var; minor loop cleanup. |
shared/build.gradle.kts |
Lowers toolchain to 17, minSdk to 25, enables desugaring for shared module, adds desugar dependency. |
gradle/libs.versions.toml |
Adds version-catalog entry for desugar JDK libs. |
app/src/main/java/com/app/ralaunch/feature/patch/data/PatchManifest.kt |
Removes Path overloads and Files.* checks; uses File. |
app/src/main/java/com/app/ralaunch/feature/patch/data/PatchManagerConfig.kt |
Migrates patch config keys and IO from Path/Files to File. |
app/src/main/java/com/app/ralaunch/feature/patch/data/PatchManager.kt |
Migrates patch manager storage/discovery/installation from Path/Files to File. |
app/src/main/java/com/app/ralaunch/feature/patch/data/Patch.kt |
Migrates patch model from Path to File. |
app/src/main/java/com/app/ralaunch/feature/init/InitializationActivity.kt |
Guards edge-to-edge / insets behavior for older Android versions; formatting cleanups. |
app/src/main/java/com/app/ralaunch/feature/game/legacy/GamePresenter.kt |
Updates patch manager calls to pass File instead of Path. |
app/src/main/java/com/app/ralaunch/core/ui/dialog/PatchManagementDialogCompose.kt |
Migrates patch import/install flow from Path/Files to File streams. |
app/src/main/java/com/app/ralaunch/core/platform/runtime/GameLauncher.kt |
Replaces kotlin.io.path usage with File for existence checks and directory creation. |
app/src/main/java/com/app/ralaunch/core/platform/runtime/AssemblyPatcher.kt |
Migrates MonoMod install path and asset copy from Path/Files to File. |
app/src/main/java/com/app/ralaunch/core/platform/install/extractors/GogShFileExtractor.kt |
Migrates extractor paths from Path/Files to File. |
app/src/main/java/com/app/ralaunch/core/platform/install/extractors/ExtractorCollection.kt |
Updates extractor interface signatures from Path to File. |
app/src/main/java/com/app/ralaunch/core/platform/install/extractors/BasicSevenZipExtractor.kt |
Migrates extractor implementation to File and adds path traversal checks/prefix filtering. |
app/src/main/java/com/app/ralaunch/core/platform/install/GameExtractorUtils.kt |
Updates extraction utilities to use File-based extractor APIs. |
app/src/main/java/com/app/ralaunch/core/platform/android/provider/RaLaunchDocumentsProvider.kt |
Migrates delete recursion helper usage from Paths.get(...) to File. |
app/src/main/java/com/app/ralaunch/core/platform/android/ProcessLauncherService.kt |
Updates patch lookup call sites to use File(assemblyPath). |
app/src/main/java/com/app/ralaunch/core/common/util/TemporaryFileAcquirer.kt |
Migrates temp file management from Path to File. |
app/src/main/java/com/app/ralaunch/core/common/util/FileUtils.kt |
Replaces NIO-based recursive delete with File-based implementation; adds helpers. |
app/src/main/java/com/app/ralaunch/core/common/GameDeletionManager.kt |
Migrates delete calls to use File rather than Paths. |
app/src/main/java/com/app/ralaunch/RaLaunchApp.kt |
Adds global uncaught exception logger + startup-step logging; minor refactors. |
app/src/main/cpp/liblinkernsbypass/android_linker_ns.cpp |
Disables namespace-bypass logic on API < 28 and falls back to regular dlopen. |
app/src/main/cpp/CMakeLists.txt |
Makes SDL target selection dynamic and adjusts install target list. |
app/src/main/AndroidManifest.xml |
Adds tools:targetApi annotations and uses-sdk overrideLibrary; reorders some attributes. |
app/build.gradle |
Lowers minSdk, enables desugaring, aligns Java/Kotlin targets to 17, adjusts native platform argument. |
README_ZH.md |
Updates requirements badge/text to Android 7.1+. |
README.md |
Updates requirements badge/text to Android 7.1+. |
.github/workflows/sync-fork.yml |
Adds fork sync workflow that prefers local changes on conflict. |
.github/workflows/build-apk.yml |
Simplifies build workflow, pins JDK 17, builds release on push, signs/archives output. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| dependencies { | ||
| coreLibraryDesugaring(libs.desugar.jdk.libs) | ||
| implementation 'androidx.multidex:multidex:2.0.1' | ||
| } |
There was a problem hiding this comment.
dependencies { ... } is nested inside the android { ... } block, which is not valid in the Android Gradle plugin DSL and will fail configuration ("Could not find method dependencies()..."). Move these dependencies to the top-level dependencies { ... } block (and keep coreLibraryDesugaringEnabled in compileOptions).
| // Dùng File thay vì Path | ||
| val tempFile = File(settingsFile.parent, "${settingsFile.name}.tmp") | ||
| tempFile.writeText(serialized) | ||
|
|
||
| // Atomic move | ||
| if (!tempFile.renameTo(settingsFile)) { | ||
| // Nếu renameTo thất bại (khác partition), dùng copy + delete | ||
| settingsFile.writeText(serialized) | ||
| tempFile.delete() | ||
| } |
There was a problem hiding this comment.
renameTo() is not a reliable atomic replacement across Android filesystems/SAF providers, and the fallback path writes directly to the final settings file (risking partial/corrupted writes if the process is killed mid-write). Use an actual atomic file pattern (e.g., AndroidX/Android AtomicFile, or a temp + fsync + replace strategy) and handle failures explicitly.
| // Tinh toan target file va chong path traversal | ||
| val targetFile = File(destinationFile, relativeFilePath).canonicalFile | ||
| if (!targetFile.path.startsWith(destinationFile.canonicalPath)) { | ||
| throw SevenZipException("Path traversal detected: $targetFile") |
There was a problem hiding this comment.
The path traversal guard is too weak: targetFile.path.startsWith(destinationFile.canonicalPath) can be bypassed by paths like ${dest}../... (handled by canonicalization) but also by sibling prefixes (e.g., /dest_evil/... startsWith /dest). Compare against destCanonical + File.separator (or use Path-based startsWith) to ensure the extracted file is truly inside the destination directory.
| val prefix = sourceExtractionPrefix.path | ||
| val relativeFilePath = if (prefix.isEmpty() || prefix == ".") { | ||
| filePath | ||
| } else if (filePath.startsWith(prefix)) { |
There was a problem hiding this comment.
The prefix filtering uses a plain startsWith(prefix) check. This can incorrectly match entries like data/noarch/gameX/... when the prefix is data/noarch/game, causing unintended files to be extracted. Ensure the match is on a path segment boundary (e.g., require filePath == prefix or filePath.startsWith(prefix + "/")) before stripping the prefix.
| } else if (filePath.startsWith(prefix)) { | |
| } else if (filePath == prefix || filePath.startsWith("$prefix/") || filePath.startsWith("$prefix\\")) { |
| fun getEnabledPatchIds(gameAsmFile: File): ArrayList<String> { // CHANGED: Path to File | ||
| return enabledPatches.getOrDefault( | ||
| gameAsmPath.toAbsolutePath().normalize().toString(), | ||
| // CHANGED: toAbsolutePath().normalize().toString() to canonicalFile.absolutePath | ||
| gameAsmFile.canonicalFile.absolutePath, | ||
| arrayListOf() | ||
| ) |
There was a problem hiding this comment.
canonicalFile can throw an IOException. Using it directly here means patch enable/disable checks can crash the app if the path cannot be canonicalized (e.g., permission issues, broken symlinks). Wrap canonicalization in runCatching { ... } with a safe fallback (like absoluteFile) and make sure the same normalization strategy is used consistently across reads/writes.
| fun getEntryAssemblyAbsolutePath(): File { | ||
| return File(patchDir, manifest.entryAssemblyFile).canonicalFile | ||
| } |
There was a problem hiding this comment.
getEntryAssemblyAbsolutePath() uses canonicalFile, which can throw IOException at call sites (e.g., when constructing DOTNET_STARTUP_HOOKS). A failure here would crash the launch flow; consider catching canonicalization errors and falling back to absoluteFile (or returning a Result).
| // 本地 JAR/AAR 依赖 | ||
| // --- THAY ĐỔI 4: Thêm thư viện Desugaring --- | ||
| // Điều này bắt buộc để chạy các API Java hiện đại trên Android 7 | ||
| coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2' |
There was a problem hiding this comment.
coreLibraryDesugaring is declared twice and with different versions (version-catalog at libs.desugar.jdk.libs vs hardcoded 2.1.2). This can lead to confusing resolution and makes version bumps error-prone. Prefer a single declaration using the version catalog (e.g., coreLibraryDesugaring(libs.desugar.jdk.libs)) in the top-level dependencies block.
| coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.2' | |
| coreLibraryDesugaring(libs.desugar.jdk.libs) |
| // Bạn có thể thay bằng alias libs.desugar.jdk.libs nếu đã khai báo trong toml | ||
| // Hoặc dùng trực tiếp chuỗi này: | ||
| coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2") | ||
| } |
There was a problem hiding this comment.
The shared module enables core library desugaring, but the dependency is pinned to a hardcoded version (2.1.2) even though the version catalog defines desugarJdkLibs. Using the catalog here avoids version drift (the app module already references the catalog) and reduces the chance of runtime/compile classpath mismatches.
| // Bạn có thể thay bằng alias libs.desugar.jdk.libs nếu đã khai báo trong toml | |
| // Hoặc dùng trực tiếp chuỗi này: | |
| coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.2") | |
| } | |
| // Sử dụng alias từ version catalog để tránh lệch phiên bản giữa các module | |
| coreLibraryDesugaring(libs.desugarJdkLibs) | |
| } |
| if (!settingsFile.exists()) return | ||
| val backupFile = File( | ||
| settingsFile.parent, | ||
| "${settingsFile.name}.corrupt.${System.currentTimeMillis()}" | ||
| ) | ||
| settingsFilePathFull.moveTo(backupPathFull, overwrite = true) | ||
| // Dùng renameTo thay vì moveTo | ||
| settingsFile.renameTo(backupFile) | ||
| } |
There was a problem hiding this comment.
backupCorruptedFile() calls settingsFile.renameTo(backupFile) but ignores the boolean result. If the rename fails, the corrupted file remains in place and the app will keep failing to parse it on the next startup. Check the return value and consider falling back to copy+delete (or at least delete the corrupted file if it cannot be backed up).
| @Throws(IOException::class) | ||
| private fun getDefaultPatchStorageDirectories(customStoragePath: String?): Path { | ||
| private fun getDefaultPatchStorageDirectories(customStoragePath: String?): File { | ||
| val context: Context = KoinJavaComponent.get(Context::class.java) | ||
| val baseDir = customStoragePath ?: if (IS_DEFAULT_PATCH_STORAGE_DIR_EXTERNAL) { | ||
| Objects.requireNonNull(context.getExternalFilesDir(null))?.absolutePath | ||
| context.getExternalFilesDir(null)?.absolutePath | ||
| ?: context.filesDir.absolutePath | ||
| } else { | ||
| context.filesDir.absolutePath | ||
| } | ||
| return Paths.get(baseDir, PATCH_STORAGE_DIR).normalize() | ||
| // Thay Paths.get().normalize() bang File() | ||
| return File(baseDir, PATCH_STORAGE_DIR).canonicalFile | ||
| } |
There was a problem hiding this comment.
getDefaultPatchStorageDirectories() returns File(...).canonicalFile, which can throw IOException and crash during PatchManager initialization. Prefer a non-throwing normalization (absoluteFile) or catch IOException and fall back to the non-canonical path (while still normalizing separators) to avoid startup crashes.
Fix crash??
Fix crash??
No description provided.