Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ app/build/outputs/apk/github/release/app-github-release-unsigned.apk
adb install -r app/build/outputs/apk/github/debug/app-github-debug.apk
```

debug ビルド(リリースに添付される `-debug.apk` を含む)は別アプリ `com.doublepi123.andcode.debug`(「DoubleCode Debug」、アイコンに虫のバッジ)としてインストールされ、データとローカル OpenCode のポート(4099)も別です。release 版を上書きせず、データも共有しません。両方のアプリで同じ OAuth プロバイダ(Codex、Antigravity)に同時にサインインすると、固定の localhost コールバックポートが衝突することがあります。

## ドキュメント

- [オンデバイスランタイム設計](docs/LOCAL_RUNTIME.md)
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ Install to device:
adb install -r app/build/outputs/apk/github/debug/app-github-debug.apk
```

Debug builds (including the `-debug.apk` attached to releases) install as a separate app, `com.doublepi123.andcode.debug` ("DoubleCode Debug", bug badge on the icon), with its own data and local OpenCode port 4099. They do not upgrade or share data with the release app. Signing in to the same OAuth provider (Codex, Antigravity) from both apps at the same time can collide on its fixed localhost callback port.

## Documentation

- [On-Device Runtime Design](docs/LOCAL_RUNTIME.md)
Expand Down
9 changes: 9 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ android {
versionCode = 66
versionName = "1.2.27-dp7"
buildConfigField("String", "GITHUB_CLIENT_ID", "\"$githubClientId\"")
// 0 keeps the port from the bundled runtime manifest; the debug build type sets its own.
buildConfigField("int", "LOCAL_RUNTIME_PORT_OVERRIDE", "0")

// The on-device runtime (see ANDROID_ABIS in scripts/prepare_android_runtime_native_libs.py)
// only exists for these two ABIs. Without the filter JNA and Vosk drag in libraries for
Expand Down Expand Up @@ -180,6 +182,13 @@ android {
}

buildTypes {
debug {
// Installs beside the release build: its own package (data, runtime, password), its
// own local OpenCode port, and its own launcher name and badged icon (src/debug/res).
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
buildConfigField("int", "LOCAL_RUNTIME_PORT_OVERRIDE", "4099")
}
release {
isMinifyEnabled = true
isShrinkResources = true
Expand Down
28 changes: 28 additions & 0 deletions app/src/debug/res/drawable/ic_launcher_debug_badge.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Debug-build launcher badge: an amber disc with a bug cut out of it, drawn over the release
foreground (see ic_launcher_debug_foreground.xml). It fills the empty space under the "A":
centre (41.7, 71.4), 22.1dp across in the 108dp layer. Its far edge is 32.4dp from the icon
centre, so it stays inside the 33dp safe-zone circle that no launcher mask clips, and it keeps
about 3.2dp clear of the logo strokes on both sides.

The bug is the Material "bug_report" glyph on its 24-unit grid, cut out with evenOdd so the
icon background shows through it. ic_launcher_debug_badge_monochrome.xml repeats this geometry
for the themed icon: keep the two in sync.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.85"
android:scaleY="0.85"
android:translateX="31.5"
android:translateY="61">
<path
android:fillColor="#FFB300"
android:fillType="evenOdd"
android:pathData="M-1,12.25A13,13 0,1 1,25,12.25A13,13 0,1 1,-1,12.25ZM19,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L16.3,5.11A1,1 0,0 0,14.89,3.7L13.42,5.17C12.96,5.06 12.49,5 12,5S11.04,5.06 10.59,5.17L9.11,3.7A1,1 0,0 0,7.7,5.11L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H5A1,1 0,0 0,5,10H6.09C6.04,10.33 6,10.66 6,11V12H5A1,1 0,0 0,5,14H6V15C6,15.34 6.04,15.67 6.09,16H5A1,1 0,0 0,5,18H6.81C7.85,19.79 9.78,21 12,21S16.15,19.79 17.19,18H19A1,1 0,0 0,19,16H17.91C17.96,15.67 18,15.34 18,15V14H19A1,1 0,0 0,19,12H18V11C18,10.66 17.96,10.33 17.91,10H19A1,1 0,0 0,19,8ZM11,10H13A1,1 0,0 1,13,12H11A1,1 0,0 1,11,10ZM11,14H13A1,1 0,0 1,13,16H11A1,1 0,0 1,11,14Z" />
</group>
</vector>
23 changes: 23 additions & 0 deletions app/src/debug/res/drawable/ic_launcher_debug_badge_monochrome.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
The debug badge for the themed-icon (monochrome) layer: the same geometry as
ic_launcher_debug_badge.xml, in a single colour like ic_launcher_monochrome. Launchers keep only
the alpha of this layer and recolour it, so the bug stays a see-through cut-out. Keep the group
transform and pathData identical to ic_launcher_debug_badge.xml.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<group
android:scaleX="0.85"
android:scaleY="0.85"
android:translateX="31.5"
android:translateY="61">
<path
android:fillColor="#FFFFFF"
android:fillType="evenOdd"
android:pathData="M-1,12.25A13,13 0,1 1,25,12.25A13,13 0,1 1,-1,12.25ZM19,8H17.19C16.74,7.22 16.12,6.55 15.37,6.04L16.3,5.11A1,1 0,0 0,14.89,3.7L13.42,5.17C12.96,5.06 12.49,5 12,5S11.04,5.06 10.59,5.17L9.11,3.7A1,1 0,0 0,7.7,5.11L8.62,6.04C7.88,6.55 7.26,7.22 6.81,8H5A1,1 0,0 0,5,10H6.09C6.04,10.33 6,10.66 6,11V12H5A1,1 0,0 0,5,14H6V15C6,15.34 6.04,15.67 6.09,16H5A1,1 0,0 0,5,18H6.81C7.85,19.79 9.78,21 12,21S16.15,19.79 17.19,18H19A1,1 0,0 0,19,16H17.91C17.96,15.67 18,15.34 18,15V14H19A1,1 0,0 0,19,12H18V11C18,10.66 17.96,10.33 17.91,10H19A1,1 0,0 0,19,8ZM11,10H13A1,1 0,0 1,13,12H11A1,1 0,0 1,11,10ZM11,14H13A1,1 0,0 1,13,16H11A1,1 0,0 1,11,14Z" />
</group>
</vector>
6 changes: 6 additions & 0 deletions app/src/debug/res/drawable/ic_launcher_debug_foreground.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The release foreground with the debug badge on top. Both layers fill the 108dp icon layer. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_launcher_foreground" />
<item android:drawable="@drawable/ic_launcher_debug_badge" />
</layer-list>
6 changes: 6 additions & 0 deletions app/src/debug/res/drawable/ic_launcher_debug_monochrome.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The release themed-icon layer with the single-colour debug badge on top. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@mipmap/ic_launcher_monochrome" />
<item android:drawable="@drawable/ic_launcher_debug_badge_monochrome" />
</layer-list>
6 changes: 6 additions & 0 deletions app/src/debug/res/mipmap-anydpi-v26/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Debug build only: the release icon plus a bug badge, so it can be told apart on the home screen. -->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_debug_foreground" />
</adaptive-icon>
6 changes: 6 additions & 0 deletions app/src/debug/res/mipmap-anydpi-v26/ic_launcher_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Debug build only: the release icon plus a bug badge, so it can be told apart on the home screen. -->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_debug_foreground" />
</adaptive-icon>
7 changes: 7 additions & 0 deletions app/src/debug/res/mipmap-anydpi-v33/ic_launcher.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Debug build only: the release icon plus a bug badge, on the themed (monochrome) icon too. -->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_debug_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_debug_monochrome" />
</adaptive-icon>
7 changes: 7 additions & 0 deletions app/src/debug/res/mipmap-anydpi-v33/ic_launcher_round.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Debug build only: the release icon plus a bug badge, on the themed (monochrome) icon too. -->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_debug_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_debug_monochrome" />
</adaptive-icon>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-es/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
4 changes: 4 additions & 0 deletions app/src/debug/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
7 changes: 7 additions & 0 deletions app/src/debug/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Debug builds install beside the release app, so they need their own launcher name. The
values-*/ copies are required: main's localized app_name would otherwise win over this
default-locale one on non-English devices. -->
<string name="app_name" translatable="false">DoubleCode Debug</string>
</resources>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.yugahashimoto.andcode.runtime.local

import android.content.Context
import com.yugahashimoto.andcode.BuildConfig
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
Expand All @@ -17,6 +18,9 @@ data class LocalRuntimeManifest(
fun architecture(abi: String): LocalRuntimeArchitecture =
requireNotNull(architectures[abi]) { "Local runtime does not support ABI $abi" }

/** This manifest with [port] replaced by [override], or unchanged when [override] is 0. */
fun withPortOverride(override: Int): LocalRuntimeManifest = if (override == 0) this else copy(port = override)

fun validate() {
require(schemaVersion == 1) { "Unsupported local runtime manifest schema: $schemaVersion" }
require(runtimeVersion.isNotBlank()) { "Runtime version is missing" }
Expand Down Expand Up @@ -53,13 +57,15 @@ class LocalRuntimeManifestReader(
ignoreUnknownKeys = true
isLenient = true
},
/** Replaces the manifest's port when non-zero, so a debug build can run beside a release one. */
private val portOverride: Int = BuildConfig.LOCAL_RUNTIME_PORT_OVERRIDE,
) {
/** The local server port this build uses; see [LocalRuntimeManifest.port]. */
fun port(): Int = read().port

fun read(): LocalRuntimeManifest {
val payload = context.assets.open(ASSET_NAME).bufferedReader().use { it.readText() }
return json.decodeFromString<LocalRuntimeManifest>(payload).also { it.validate() }
return json.decodeFromString<LocalRuntimeManifest>(payload).withPortOverride(portOverride).also { it.validate() }
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ class ReleaseMetadataTest {
fun `android package metadata matches the release tag`() {
val releaseVersion = repositoryRoot().resolve(".release-version").readText().trim().removePrefix("v")

assertEquals(releaseVersion, BuildConfig.VERSION_NAME)
// Debug builds carry a "-debug" suffix (versionNameSuffix) so they are told apart on device.
val expected = if (BuildConfig.DEBUG) "$releaseVersion-debug" else releaseVersion
assertEquals(expected, BuildConfig.VERSION_NAME)
assertEquals(66, BuildConfig.VERSION_CODE)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ class LocalRuntimeManifestTest {
assertEquals(architecture, manifest.architecture("arm64-v8a"))
}

@Test
fun `port override replaces the manifest port only when set`() {
val manifest =
LocalRuntimeManifest(
schemaVersion = 1,
runtimeVersion = "2026.07.18.1",
openCodeVersion = "1.18.3",
alpineVersion = "3.24.1",
port = 4098,
architectures = mapOf("arm64-v8a" to architecture),
)

assertEquals(4098, manifest.withPortOverride(0).port)
assertEquals(4099, manifest.withPortOverride(4099).port)
}

@Test
fun `manifest rejects insecure download URL`() {
val invalid = architecture.copy(alpineUrl = "http://example.com/alpine.tar.gz")
Expand Down
Loading