Compare commits
18
Commits
4787302f25
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f7036c2c25
|
||
|
|
0c27088640
|
||
|
|
11a349bbad
|
||
|
|
3418e63dde
|
||
|
|
659c79e5dd
|
||
|
|
0512662466
|
||
|
|
842bb0553e
|
||
|
|
96e5cc1213
|
||
|
|
621618a3da
|
||
|
|
b6b05a71de
|
||
|
|
72645c054a
|
||
|
|
676df8e8b3
|
||
|
|
b15c7c8213
|
||
|
|
7c2f06bceb
|
||
|
|
4a0817b222
|
||
|
|
2b34d3f2ac
|
||
|
|
085552b759
|
||
|
|
4379656f16
|
@@ -0,0 +1,3 @@
|
||||
KEYSTORE_PASSWORD=your_keystore_password_here
|
||||
KEY_ALIAS=your_key_alias_here
|
||||
KEY_PASSWORD=your_key_password_here
|
||||
@@ -0,0 +1,2 @@
|
||||
.env
|
||||
release/
|
||||
@@ -0,0 +1,11 @@
|
||||
services:
|
||||
release:
|
||||
# image: git.shihaam.dev/dockerfiles/android-builder
|
||||
image: git.shihaam.dev/dockerfiles/runners/gradle
|
||||
hostname: isodroid
|
||||
network_mode: host
|
||||
env_file: .env
|
||||
volumes:
|
||||
- ./release:/release
|
||||
- ../../:/source
|
||||
- /root/.cache/cache-runners/gradle:/root/.gradle
|
||||
@@ -0,0 +1,70 @@
|
||||
name: Auto Tag on Version Change
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
check-version:
|
||||
runs-on: docker-compose
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.PAT_GITEA }}
|
||||
|
||||
- name: Check version and prepare release
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(grep 'versionName = ' app/build.gradle.kts | sed 's/.*versionName = "\(.*\)".*/\1/')
|
||||
VERSION_CODE=$(grep 'versionCode = ' app/build.gradle.kts | sed 's/.*versionCode = \([0-9]*\).*/\1/')
|
||||
echo "version=$VERSION" >> $GITHUB_OUTPUT
|
||||
echo "version_code=$VERSION_CODE" >> $GITHUB_OUTPUT
|
||||
|
||||
if git tag -l | grep -q "^v${VERSION}$"; then
|
||||
echo "Tag v${VERSION} already exists, skipping"
|
||||
echo "should_release=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "New version detected: v${VERSION}"
|
||||
echo "should_release=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: Extract and commit fastlane changelog
|
||||
if: steps.version.outputs.should_release == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
VERSION_CODE="${{ steps.version.outputs.version_code }}"
|
||||
FASTLANE_DIR="fastlane/metadata/android/en-US/changelogs"
|
||||
|
||||
mkdir -p "$FASTLANE_DIR"
|
||||
|
||||
awk -v ver="$VERSION" '
|
||||
BEGIN { found=0 }
|
||||
/^## \[/ {
|
||||
if (found) exit
|
||||
if ($0 ~ "\\[" ver "\\]") { found=1; next }
|
||||
}
|
||||
found { print }
|
||||
' CHANGELOG.md > "$FASTLANE_DIR/${VERSION_CODE}.txt"
|
||||
|
||||
git config user.name "Gitea Actions"
|
||||
git config user.email "actions@gitea.local"
|
||||
|
||||
if git diff --quiet "$FASTLANE_DIR/${VERSION_CODE}.txt" 2>/dev/null; then
|
||||
echo "No changelog changes to commit"
|
||||
else
|
||||
git add "$FASTLANE_DIR/${VERSION_CODE}.txt"
|
||||
git commit -m "Add fastlane changelog for v${VERSION}"
|
||||
git push origin main
|
||||
fi
|
||||
|
||||
- name: Create and push tag
|
||||
if: steps.version.outputs.should_release == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
git tag "v${VERSION}"
|
||||
git push origin "v${VERSION}"
|
||||
echo "Created and pushed tag v${VERSION}"
|
||||
@@ -0,0 +1,79 @@
|
||||
name: Build and Release APK
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: docker-compose
|
||||
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup keystore and environment
|
||||
run: |
|
||||
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > app/key.jks
|
||||
echo "${{ secrets.DOTENV_BASE64 }}" | base64 -d > .build/release/.env
|
||||
|
||||
- name: Build APK
|
||||
working-directory: .build/release
|
||||
run: docker compose run --rm release
|
||||
|
||||
- name: Extract release notes
|
||||
run: |
|
||||
VERSION="${{ gitea.ref_name }}"
|
||||
VERSION="${VERSION#v}"
|
||||
|
||||
awk -v ver="$VERSION" '
|
||||
BEGIN { found=0 }
|
||||
/^## \[/ {
|
||||
if (found) exit
|
||||
if ($0 ~ "\\[" ver "\\]") { found=1; next }
|
||||
}
|
||||
found { print }
|
||||
' CHANGELOG.md > release_notes.md
|
||||
|
||||
- name: Create Gitea Release
|
||||
env:
|
||||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
||||
GITEA_TOKEN: ${{ secrets.PAT_GITEA }}
|
||||
run: |
|
||||
TAG="${{ gitea.ref_name }}"
|
||||
TITLE="ISODroid ${{ gitea.ref_name }}"
|
||||
NOTES_FILE="release_notes.md"
|
||||
ASSET_PATH=".build/release/release/ISODroid-${{ gitea.ref_name }}.apk"
|
||||
|
||||
API_URL="${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases"
|
||||
|
||||
RELEASE_RESPONSE=$(curl -s -X POST "$API_URL" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"tag_name\": \"${TAG}\",
|
||||
\"name\": \"${TITLE}\",
|
||||
\"body\": $(jq -Rs . < "$NOTES_FILE")
|
||||
}")
|
||||
|
||||
RELEASE_ID=$(echo "$RELEASE_RESPONSE" | jq -r '.id')
|
||||
|
||||
if [ "$RELEASE_ID" = "null" ] || [ -z "$RELEASE_ID" ]; then
|
||||
echo "Failed to create release:"
|
||||
echo "$RELEASE_RESPONSE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Created release with ID: $RELEASE_ID"
|
||||
|
||||
ASSET_NAME=$(basename "$ASSET_PATH")
|
||||
UPLOAD_URL="${API_URL}/${RELEASE_ID}/assets?name=${ASSET_NAME}"
|
||||
|
||||
curl -s -X POST "$UPLOAD_URL" \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@${ASSET_PATH}"
|
||||
|
||||
echo "Uploaded asset: $ASSET_NAME"
|
||||
@@ -16,3 +16,5 @@ local.properties
|
||||
|
||||
app/release/
|
||||
app/debug/
|
||||
app/key.jks
|
||||
.build/release/release/*.apk
|
||||
|
||||
Generated
-10
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,5 +1,14 @@
|
||||
# ISO Droid
|
||||
|
||||
[](https://sladge.net)
|
||||
[](LICENSE)
|
||||

|
||||

|
||||

|
||||
[](docs/DISCLAIMER.md)
|
||||

|
||||
[](https://git.shihaam.dev/shihaam/ISODroid/releases/latest)
|
||||
|
||||
> **Note:** This app requires root access and was developed with AI assistance (Claude).
|
||||
> I use it on my own devices, but as with any root tool, understand what you're running and keep backups.
|
||||
>
|
||||
@@ -34,7 +43,7 @@ Android app for mounting ISO/IMG files as USB mass storage or CD-ROM devices on
|
||||
|
||||
| F-Droid | Gitea Releases |
|
||||
|:--:|:--:|
|
||||
| [](https://f-droid.org/en/packages/sh.sar.isodroid) | [Download latest APK](https://git.shihaam.dev/shihaam/ISODroid/releases) |
|
||||
| <!-- [](https://f-droid.org/en/packages/sh.sar.isodroid) --> [Coming Soon](https://gitlab.com/fdroid/fdroiddata/-/merge_requests/34539) | [Download latest APK](https://git.shihaam.dev/shihaam/ISODroid/releases/latest) |
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -67,14 +76,19 @@ Android app for mounting ISO/IMG files as USB mass storage or CD-ROM devices on
|
||||
|
||||
Around 2014/2015 I rooted my first phone and went down the rabbit hole of what you could do with a rooted Android.
|
||||
That's when I found [DriveDroid](https://softwarebakery.com/projects/drivedroid) ([archive](https://web.archive.org/web/20160901000000*/https://softwarebakery.com/projects/drivedroid)) by Software Bakery. Teenage me thought it was the coolest thing ever.
|
||||
fastforwrd few years: DriveDroid stopped being maintained, 404 on playstore, and even if you find apk floating around ([Kali NetHunter](https://store.nethunter.com/packages/com.softwarebakery.drivedroid/) keeps one) it does not work on modern Android. I've wanted something like it for years.
|
||||
Then I found [isodrive](https://github.com/nitanmarcel/isodrive) by nitanmarcel ([mirror](https://git.shihaam.dev/shihaam/isodrive)). Tried it. It worked. So here we are. ISO Droid is basically me chasing that teenage nostalgia and trying to make something simple that just works.
|
||||
The name comes from **Drive**Droid + **iso**drive = **ISO Droid**. Not very creative, but it fits.
|
||||
|
||||
Fast forward a 10 years: DriveDroid is no longer maintained, 404 on Play Store, and even if you find an APK ([Kali NetHunter App Store](https://store.nethunter.com/packages/com.softwarebakery.drivedroid/) keeps one) it does not work on modern Android.
|
||||
|
||||
As someone who gives tech support often to relatives and friends, I've wanted something like it for years. Having Linux to chroot or HBCD for Windows password reset at anytime without having to carry around USB drives is very useful for me.
|
||||
Then I found [isodrive](https://github.com/nitanmarcel/isodrive) by nitanmarcel ([mirror](https://git.shihaam.dev/shihaam/isodrive)). I tried the Magisk module and Termux, it worked. So here we are.
|
||||
|
||||
ISO Droid is basically me chasing that teenage nostalgia and trying to make something simple that just works.
|
||||
The name comes from **iso**drive + Drive**Droid** = **ISO Droid**.
|
||||
|
||||
Special thanks to:
|
||||
- [isodrive](https://github.com/nitanmarcel/isodrive) by nitanmarcel - The CLI tool that powers ISO Droid
|
||||
- [ISODriveUT](https://github.com/fredldotme/ISODriveUT) by fredldotme - Original inspiration for isodrive
|
||||
- [DriveDroid](https://softwarebakery.com/projects/drivedroid) by Software Bakery - where it all started
|
||||
- [DriveDroid](https://softwarebakery.com/projects/drivedroid) by Software Bakery - where it all started for me
|
||||
- [Simple Icons](https://simpleicons.org/) and [SVG Repo](https://www.svgrepo.com/) for OS icons
|
||||
|
||||
## License
|
||||
|
||||
@@ -136,8 +136,18 @@ android {
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
storeFile = file("key.jks")
|
||||
storePassword = System.getenv("KEYSTORE_PASSWORD")
|
||||
keyAlias = System.getenv("KEY_ALIAS")
|
||||
keyPassword = System.getenv("KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
|
||||
@@ -12,8 +12,6 @@ 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
|
||||
@@ -103,7 +101,6 @@ fun DownloadsScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Download ISOs") },
|
||||
@@ -127,7 +124,6 @@ fun DownloadsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
groupedDownloads.forEach { (category, osList) ->
|
||||
|
||||
@@ -10,8 +10,6 @@ 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
|
||||
@@ -143,7 +141,6 @@ fun MainScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("ISO Droid") },
|
||||
@@ -207,7 +204,6 @@ fun MainScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.nestedScroll(pullToRefreshState.nestedScrollConnection)
|
||||
) {
|
||||
if (uiState.isLoading && !pullToRefreshState.isRefreshing) {
|
||||
|
||||
@@ -22,8 +22,6 @@ 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
|
||||
@@ -153,7 +151,6 @@ fun SettingsScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
contentWindowInsets = WindowInsets(0),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Settings") },
|
||||
@@ -177,7 +174,6 @@ fun SettingsScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.navigationBarsPadding()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
// Storage section
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
package sh.sar.isodroid.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -13,7 +14,11 @@ 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,
|
||||
@@ -42,6 +47,15 @@ fun ISODroidTheme(
|
||||
else -> LightColorScheme
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = !darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
|
||||
Reference in New Issue
Block a user