Compare commits
4
Commits
148b494926
...
e9dfbce049
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9dfbce049
|
||
|
|
ccb2af558b
|
||
|
|
d0817240ec
|
||
|
|
b1abee3579
|
Generated
-8
@@ -4,14 +4,6 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-03-12T19:54:01.237140412Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="PhysicalDevice" identifier="serial=a703e092" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
<DialogSelection />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
|
||||
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [1.7] - 2026-03-13
|
||||
|
||||
### Changed
|
||||
- Always use app-bundled isodrive binary instead of system binary
|
||||
- Prep for edge-to-edge compatibility
|
||||
|
||||
## [1.6] - 2026-03-13
|
||||
|
||||
### Added
|
||||
|
||||
+35
-29
@@ -1,3 +1,6 @@
|
||||
import javax.inject.Inject
|
||||
import org.gradle.process.ExecOperations
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
@@ -5,25 +8,22 @@ plugins {
|
||||
}
|
||||
|
||||
// Build isodrive native binary from submodule (only if binaries are missing)
|
||||
val buildIsodrive by tasks.registering {
|
||||
group = "native"
|
||||
description = "Build isodrive binary for all Android architectures"
|
||||
abstract class BuildIsodriveTask @Inject constructor(
|
||||
private val execOps: ExecOperations
|
||||
) : DefaultTask() {
|
||||
|
||||
val isodriveDir = rootProject.file("isodrive")
|
||||
val outputDir = file("src/main/assets/bin")
|
||||
val architectures = listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
||||
@get:InputDirectory
|
||||
abstract val isodriveDir: DirectoryProperty
|
||||
|
||||
// Check if all binaries exist
|
||||
val allBinariesExist = architectures.all { arch ->
|
||||
outputDir.resolve("$arch/isodrive").exists()
|
||||
}
|
||||
@get:OutputDirectory
|
||||
abstract val outputDir: DirectoryProperty
|
||||
|
||||
// Skip if all binaries already exist
|
||||
onlyIf { !allBinariesExist }
|
||||
@TaskAction
|
||||
fun build() {
|
||||
val isodrivePath = isodriveDir.get().asFile
|
||||
val outputPath = outputDir.get().asFile
|
||||
|
||||
doLast {
|
||||
// Verify submodule is initialized
|
||||
if (!isodriveDir.resolve("src").exists()) {
|
||||
if (!isodrivePath.resolve("src").exists()) {
|
||||
throw GradleException("isodrive submodule not initialized. Run: git submodule update --init")
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ val buildIsodrive by tasks.registering {
|
||||
"src/androidusbisomanager.cpp",
|
||||
"src/main.cpp"
|
||||
)
|
||||
val srcs = sourceFiles.joinToString(" ") { "$isodriveDir/$it" }
|
||||
val cflags = "-I$isodriveDir/src/include -static-libstdc++ -Os -s"
|
||||
val srcs = sourceFiles.joinToString(" ") { "$isodrivePath/$it" }
|
||||
val cflags = "-I$isodrivePath/src/include -static-libstdc++ -Os -s"
|
||||
|
||||
val compilers = mapOf(
|
||||
"arm64-v8a" to "aarch64-linux-android26-clang++",
|
||||
@@ -43,16 +43,14 @@ val buildIsodrive by tasks.registering {
|
||||
"x86" to "i686-linux-android26-clang++"
|
||||
)
|
||||
|
||||
// Check if we're on NixOS (nix-shell available)
|
||||
val useNixShell = File("/etc/NIXOS").exists() ||
|
||||
Runtime.getRuntime().exec(arrayOf("which", "nix-shell")).waitFor() == 0
|
||||
|
||||
if (useNixShell) {
|
||||
println("NixOS detected, using nix-shell for NDK...")
|
||||
|
||||
// Build all architectures in one nix-shell invocation
|
||||
val buildScript = compilers.entries.joinToString("\n") { (arch, compiler) ->
|
||||
val archDir = outputDir.resolve(arch)
|
||||
val archDir = outputPath.resolve(arch)
|
||||
val output = archDir.resolve("isodrive")
|
||||
if (output.exists()) {
|
||||
"echo 'isodrive for $arch already exists, skipping'"
|
||||
@@ -65,9 +63,7 @@ val buildIsodrive by tasks.registering {
|
||||
}
|
||||
}
|
||||
|
||||
exec {
|
||||
environment("SRCS", srcs)
|
||||
environment("CFLAGS", cflags)
|
||||
execOps.exec {
|
||||
environment("NIXPKGS_ALLOW_UNFREE", "1")
|
||||
commandLine("nix-shell", "-p", "androidenv.androidPkgs.ndk-bundle", "--run", """
|
||||
SDK_ROOT=${'$'}(find /nix/store -maxdepth 1 -name "*android-sdk-ndk*" -type d 2>/dev/null | head -1)
|
||||
@@ -79,7 +75,6 @@ val buildIsodrive by tasks.registering {
|
||||
""".trimIndent())
|
||||
}
|
||||
} else {
|
||||
// Standard NDK lookup for non-NixOS
|
||||
val ndkDir = listOfNotNull(
|
||||
System.getenv("ANDROID_NDK_HOME"),
|
||||
System.getenv("ANDROID_NDK")
|
||||
@@ -92,7 +87,7 @@ val buildIsodrive by tasks.registering {
|
||||
}
|
||||
|
||||
compilers.forEach { (arch, compiler) ->
|
||||
val archDir = outputDir.resolve(arch)
|
||||
val archDir = outputPath.resolve(arch)
|
||||
val output = archDir.resolve("isodrive")
|
||||
|
||||
if (output.exists()) {
|
||||
@@ -102,7 +97,7 @@ val buildIsodrive by tasks.registering {
|
||||
|
||||
archDir.mkdirs()
|
||||
println("Building isodrive for $arch...")
|
||||
exec {
|
||||
execOps.exec {
|
||||
commandLine("sh", "-c", "$toolchain/$compiler $cflags $srcs -o $output")
|
||||
}
|
||||
}
|
||||
@@ -111,7 +106,18 @@ val buildIsodrive by tasks.registering {
|
||||
}
|
||||
}
|
||||
|
||||
// Hook into the build process - build isodrive before merging assets
|
||||
val buildIsodrive by tasks.registering(BuildIsodriveTask::class) {
|
||||
group = "native"
|
||||
description = "Build isodrive binary for all Android architectures"
|
||||
|
||||
isodriveDir.set(rootProject.file("isodrive"))
|
||||
outputDir.set(file("src/main/assets/bin"))
|
||||
|
||||
val architectures = listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
||||
val outDir = file("src/main/assets/bin")
|
||||
onlyIf { !architectures.all { arch -> outDir.resolve("$arch/isodrive").exists() } }
|
||||
}
|
||||
|
||||
tasks.matching { it.name.startsWith("merge") && it.name.endsWith("Assets") }.configureEach {
|
||||
dependsOn(buildIsodrive)
|
||||
}
|
||||
@@ -124,8 +130,8 @@ android {
|
||||
applicationId = "sh.sar.isodroid"
|
||||
minSdk = 26
|
||||
targetSdk = 36
|
||||
versionCode = 6
|
||||
versionName = "1.6"
|
||||
versionCode = 7
|
||||
versionName = "1.7"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -22,8 +24,8 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.OpenInNew
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.OpenInNew
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -68,9 +70,9 @@ private fun loadOsDownloads(context: Context): List<OsDownload> {
|
||||
OsDownload(
|
||||
name = obj.getString("name"),
|
||||
category = obj.getString("category"),
|
||||
subcategory = if (obj.isNull("subcategory")) null else obj.optString("subcategory", null),
|
||||
subcategory = if (obj.isNull("subcategory")) null else obj.optString("subcategory"),
|
||||
description = obj.optString("description", ""),
|
||||
icon = if (obj.isNull("icon")) null else obj.optString("icon", null),
|
||||
icon = if (obj.isNull("icon")) null else obj.optString("icon"),
|
||||
url = obj.getString("url")
|
||||
)
|
||||
)
|
||||
@@ -101,6 +103,7 @@ fun DownloadsScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Download ISOs") },
|
||||
@@ -124,6 +127,7 @@ fun DownloadsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
groupedDownloads.forEach { (category, osList) ->
|
||||
@@ -280,7 +284,7 @@ private fun DownloadItem(
|
||||
}
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.OpenInNew,
|
||||
imageVector = Icons.AutoMirrored.Filled.OpenInNew,
|
||||
contentDescription = "Open in browser",
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.size(20.dp)
|
||||
|
||||
@@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -141,6 +143,7 @@ fun MainScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("ISO Droid") },
|
||||
@@ -204,6 +207,7 @@ fun MainScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.nestedScroll(pullToRefreshState.nestedScrollConnection)
|
||||
) {
|
||||
if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
|
||||
|
||||
@@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
@@ -151,6 +153,7 @@ fun SettingsScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Settings") },
|
||||
@@ -174,6 +177,7 @@ fun SettingsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
// Storage section
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
package sh.sar.isodroid.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -14,11 +13,7 @@ import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
@@ -47,15 +42,6 @@ fun ISODroidTheme(
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = android.graphics.Color.TRANSPARENT
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
- Always use app-bundled isodrive binary instead of system binary
|
||||
- Prep for edge-to-edge compatibility
|
||||
Reference in New Issue
Block a user