4 Commits
Author SHA1 Message Date
shihaam e9dfbce049 Version 1.7 2026-03-13 23:46:48 +05:00
shihaam ccb2af558b Prep for compatbility for edgeing 2026-03-13 23:44:20 +05:00
shihaam d0817240ec fix deprecation warning by using newer API 2026-03-13 23:18:00 +05:00
shihaam b1abee3579 rtl support and fix some non critical warnings 2026-03-13 23:17:11 +05:00
8 changed files with 59 additions and 55 deletions
-8
View File
@@ -4,14 +4,6 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <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> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>
+6
View File
@@ -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/), 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). 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 ## [1.6] - 2026-03-13
### Added ### Added
+35 -29
View File
@@ -1,3 +1,6 @@
import javax.inject.Inject
import org.gradle.process.ExecOperations
plugins { plugins {
alias(libs.plugins.android.application) alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.android)
@@ -5,25 +8,22 @@ plugins {
} }
// Build isodrive native binary from submodule (only if binaries are missing) // Build isodrive native binary from submodule (only if binaries are missing)
val buildIsodrive by tasks.registering { abstract class BuildIsodriveTask @Inject constructor(
group = "native" private val execOps: ExecOperations
description = "Build isodrive binary for all Android architectures" ) : DefaultTask() {
val isodriveDir = rootProject.file("isodrive") @get:InputDirectory
val outputDir = file("src/main/assets/bin") abstract val isodriveDir: DirectoryProperty
val architectures = listOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
// Check if all binaries exist @get:OutputDirectory
val allBinariesExist = architectures.all { arch -> abstract val outputDir: DirectoryProperty
outputDir.resolve("$arch/isodrive").exists()
}
// Skip if all binaries already exist @TaskAction
onlyIf { !allBinariesExist } fun build() {
val isodrivePath = isodriveDir.get().asFile
val outputPath = outputDir.get().asFile
doLast { if (!isodrivePath.resolve("src").exists()) {
// Verify submodule is initialized
if (!isodriveDir.resolve("src").exists()) {
throw GradleException("isodrive submodule not initialized. Run: git submodule update --init") throw GradleException("isodrive submodule not initialized. Run: git submodule update --init")
} }
@@ -33,8 +33,8 @@ val buildIsodrive by tasks.registering {
"src/androidusbisomanager.cpp", "src/androidusbisomanager.cpp",
"src/main.cpp" "src/main.cpp"
) )
val srcs = sourceFiles.joinToString(" ") { "$isodriveDir/$it" } val srcs = sourceFiles.joinToString(" ") { "$isodrivePath/$it" }
val cflags = "-I$isodriveDir/src/include -static-libstdc++ -Os -s" val cflags = "-I$isodrivePath/src/include -static-libstdc++ -Os -s"
val compilers = mapOf( val compilers = mapOf(
"arm64-v8a" to "aarch64-linux-android26-clang++", "arm64-v8a" to "aarch64-linux-android26-clang++",
@@ -43,16 +43,14 @@ val buildIsodrive by tasks.registering {
"x86" to "i686-linux-android26-clang++" "x86" to "i686-linux-android26-clang++"
) )
// Check if we're on NixOS (nix-shell available)
val useNixShell = File("/etc/NIXOS").exists() || val useNixShell = File("/etc/NIXOS").exists() ||
Runtime.getRuntime().exec(arrayOf("which", "nix-shell")).waitFor() == 0 Runtime.getRuntime().exec(arrayOf("which", "nix-shell")).waitFor() == 0
if (useNixShell) { if (useNixShell) {
println("NixOS detected, using nix-shell for NDK...") 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 buildScript = compilers.entries.joinToString("\n") { (arch, compiler) ->
val archDir = outputDir.resolve(arch) val archDir = outputPath.resolve(arch)
val output = archDir.resolve("isodrive") val output = archDir.resolve("isodrive")
if (output.exists()) { if (output.exists()) {
"echo 'isodrive for $arch already exists, skipping'" "echo 'isodrive for $arch already exists, skipping'"
@@ -65,9 +63,7 @@ val buildIsodrive by tasks.registering {
} }
} }
exec { execOps.exec {
environment("SRCS", srcs)
environment("CFLAGS", cflags)
environment("NIXPKGS_ALLOW_UNFREE", "1") environment("NIXPKGS_ALLOW_UNFREE", "1")
commandLine("nix-shell", "-p", "androidenv.androidPkgs.ndk-bundle", "--run", """ 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) 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()) """.trimIndent())
} }
} else { } else {
// Standard NDK lookup for non-NixOS
val ndkDir = listOfNotNull( val ndkDir = listOfNotNull(
System.getenv("ANDROID_NDK_HOME"), System.getenv("ANDROID_NDK_HOME"),
System.getenv("ANDROID_NDK") System.getenv("ANDROID_NDK")
@@ -92,7 +87,7 @@ val buildIsodrive by tasks.registering {
} }
compilers.forEach { (arch, compiler) -> compilers.forEach { (arch, compiler) ->
val archDir = outputDir.resolve(arch) val archDir = outputPath.resolve(arch)
val output = archDir.resolve("isodrive") val output = archDir.resolve("isodrive")
if (output.exists()) { if (output.exists()) {
@@ -102,7 +97,7 @@ val buildIsodrive by tasks.registering {
archDir.mkdirs() archDir.mkdirs()
println("Building isodrive for $arch...") println("Building isodrive for $arch...")
exec { execOps.exec {
commandLine("sh", "-c", "$toolchain/$compiler $cflags $srcs -o $output") 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 { tasks.matching { it.name.startsWith("merge") && it.name.endsWith("Assets") }.configureEach {
dependsOn(buildIsodrive) dependsOn(buildIsodrive)
} }
@@ -124,8 +130,8 @@ android {
applicationId = "sh.sar.isodroid" applicationId = "sh.sar.isodroid"
minSdk = 26 minSdk = 26
targetSdk = 36 targetSdk = 36
versionCode = 6 versionCode = 7
versionName = "1.6" versionName = "1.7"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@@ -22,8 +24,8 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack 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.Download
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
@@ -68,9 +70,9 @@ private fun loadOsDownloads(context: Context): List<OsDownload> {
OsDownload( OsDownload(
name = obj.getString("name"), name = obj.getString("name"),
category = obj.getString("category"), 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", ""), 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") url = obj.getString("url")
) )
) )
@@ -101,6 +103,7 @@ fun DownloadsScreen(
} }
Scaffold( Scaffold(
contentWindowInsets = WindowInsets(0),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("Download ISOs") }, title = { Text("Download ISOs") },
@@ -124,6 +127,7 @@ fun DownloadsScreen(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.navigationBarsPadding()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
groupedDownloads.forEach { (category, osList) -> groupedDownloads.forEach { (category, osList) ->
@@ -280,7 +284,7 @@ private fun DownloadItem(
} }
} }
Icon( Icon(
imageVector = Icons.Default.OpenInNew, imageVector = Icons.AutoMirrored.Filled.OpenInNew,
contentDescription = "Open in browser", contentDescription = "Open in browser",
tint = MaterialTheme.colorScheme.onSurfaceVariant, tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(20.dp) 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.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer 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.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -141,6 +143,7 @@ fun MainScreen(
} }
Scaffold( Scaffold(
contentWindowInsets = WindowInsets(0),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("ISO Droid") }, title = { Text("ISO Droid") },
@@ -204,6 +207,7 @@ fun MainScreen(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.navigationBarsPadding()
.nestedScroll(pullToRefreshState.nestedScrollConnection) .nestedScroll(pullToRefreshState.nestedScrollConnection)
) { ) {
if (uiState.isLoading && !pullToRefreshState.isRefreshing) { 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer 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.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@@ -151,6 +153,7 @@ fun SettingsScreen(
} }
Scaffold( Scaffold(
contentWindowInsets = WindowInsets(0),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text("Settings") }, title = { Text("Settings") },
@@ -174,6 +177,7 @@ fun SettingsScreen(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues) .padding(paddingValues)
.navigationBarsPadding()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
// Storage section // Storage section
@@ -5,7 +5,6 @@
package sh.sar.isodroid.ui.theme package sh.sar.isodroid.ui.theme
import android.app.Activity
import android.os.Build import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -14,11 +13,7 @@ import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable 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.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
private val DarkColorScheme = darkColorScheme( private val DarkColorScheme = darkColorScheme(
primary = Purple80, primary = Purple80,
@@ -47,15 +42,6 @@ fun ISODroidTheme(
else -> LightColorScheme 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( MaterialTheme(
colorScheme = colorScheme, colorScheme = colorScheme,
typography = Typography, typography = Typography,
@@ -0,0 +1,2 @@
- Always use app-bundled isodrive binary instead of system binary
- Prep for edge-to-edge compatibility