You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 0f2943d
Browse filesBrowse the repository at this point in the historyBrowse files
Copy file name to clipboardExpand all lines: ARCHITECTURE.md
+17-14Lines changed: 17 additions & 14 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -59,6 +59,7 @@ Strategy: **layer-and-subsystem based**, not feature-by-feature. The Gradle buil
59
59
|---|---|---|
60
60
| Application |`app`| The IDE itself — activities, fragments, services, DI, agent, web server. Wires everything together. |
61
61
| Build engine |`subprojects:tooling-api*`, `gradle-plugin*`, `subprojects:projects`, `subprojects:builder-model-impl`| Runs a real Gradle build of the user's project out-of-process and streams events back. |
62
+
| Quick Build (experimental, ADFA-4128) |`quickbuild:core`, `quickbuild:daemon`, `quickbuild:protocol`, `quickbuild:runtime`| Live-reloads the user's app on every save in seconds, by running it as a generated proxy app instead of doing a full Gradle rebuild. |
62
63
| Language tooling |`lsp:{api,java,kotlin,xml,indexing,…}`, `lexers`, `editor*`, `editor-treesitter`| Language servers, indexing, the Sora-based editor and highlighting. |
63
64
| UI design tooling |`layouteditor`, `uidesigner`, `xml-inflater`, `vectormaster`, `compose-preview`| Visual/XML design surfaces for the *user's* app. |
64
65
| Shell |`termux:{termux-app,termux-shared,termux-view,termux-emulator}`| Embedded Termux shell and terminal. |
@@ -68,6 +69,7 @@ Strategy: **layer-and-subsystem based**, not feature-by-feature. The Gradle buil
68
69
| Testing |`testing:{android,unit,lsp,tooling,common}`| Shared test harnesses, split by what's under test. |
69
70
70
71
**Dependency rules (enforced):**
72
+
71
73
-**`app` depends inward; libraries never depend on `app`.** Subsystems are consumed by `app`, not vice versa.
72
74
-**Vendored forks are substituted, not imported ad hoc.**`composite-builds/build-deps` and `build-deps-common` provide forked `javac`/`jdt`/`layoutlib`/etc.; `settings.gradle.kts` substitutes them in for `com.itsaky.androidide.build:*`. Don't add a Maven coordinate for something already substituted.
73
75
-**All module config flows through `composite-builds/build-logic`.** Every Android module gets the `v7`/`v8` ABI flavors centrally (`AndroidModuleConf.kt`) — there is no flavorless `assembleDebug`. `:plugin-api` is intentionally excluded from flavors.
@@ -87,16 +89,16 @@ These structural facts shape every module. Day-to-day build *commands* live in `
87
89
88
90
## Technology Stack
89
91
90
-
| Concern | Library / Approach |
91
-
|---|---|
92
-
| UI |**Jetpack Compose for all new UI** ([ADR 0009](docs/adr/0009-jetpack-compose-for-new-ui.md)). The existing majority is still Android Views + Fragments + `RecyclerView` (Material Components); those legacy screens stay until reworked, but new IDE UI is Compose-only. |
93
-
| Dependency Injection |**Koin** (`org.koin`) — `coreModule`/`pluginModule`, `startKoin` in `IDEApplication`, plus a `ServiceLocator : KoinComponent` for lazy post-startup access. No Hilt/Dagger. |
94
-
| Asynchronous work |**Kotlin Coroutines + Flow** (`StateFlow`/`SharedFlow`, `viewModelScope`, app-scoped `CoroutineScope(SupervisorJob() + Dispatchers.IO)`); **GreenRobot EventBus** for cross-subsystem events. |
95
-
| Networking | Offline-first; no general REST layer. External I/O is **Google GenAI SDK** (Gemini), **on-device llama.cpp**, and **JGit** (git). Retrofit is in the catalog but effectively unused in app code. |
| UI |**Jetpack Compose for all new UI** ([ADR 0009](docs/adr/0009-jetpack-compose-for-new-ui.md)). The existing majority is still Android Views + Fragments + `RecyclerView` (Material Components); those legacy screens stay until reworked, but new IDE UI is Compose-only. |
95
+
| Dependency Injection |**Koin** (`org.koin`) — `coreModule`/`pluginModule`, `startKoin` in `IDEApplication`, plus a `ServiceLocator : KoinComponent` for lazy post-startup access. No Hilt/Dagger. |
96
+
| Asynchronous work |**Kotlin Coroutines + Flow** (`StateFlow`/`SharedFlow`, `viewModelScope`, app-scoped `CoroutineScope(SupervisorJob() + Dispatchers.IO)`); **GreenRobot EventBus** for cross-subsystem events. |
97
+
| Networking | Offline-first; no general REST layer. External I/O is **Google GenAI SDK** (Gemini), **on-device llama.cpp**, and **JGit** (git). Retrofit is in the catalog but effectively unused in app code. |
96
98
| Database / Persistence |**Room** is the default for relational/queryable data; **filesystem + preferences (DataStore)** for non-relational settings. **Raw SQLite** (`SQLiteDatabase` / `SupportSQLiteOpenHelper`) only for justified exceptions (see policy below). |
97
-
| Serialization |`kotlinx.serialization` and Gson. |
98
-
| Parceling | Kotlin **`@Parcelize`** (`kotlin-parcelize` plugin) for `Parcelable` data classes — never hand-implement `Parcelable`. Do it manually only if `@Parcelize` genuinely can't express it (custom serialization logic, unsupported member types). |
99
-
| AI agent | Google GenAI (cloud) + llama (local), behind `GeminiRepository` / `SwitchableGeminiRepository`, with planner/critic/executor agents in `agent/repository`. |
99
+
| Serialization |`kotlinx.serialization` and Gson.|
100
+
| Parceling | Kotlin **`@Parcelize`** (`kotlin-parcelize` plugin) for `Parcelable` data classes — never hand-implement `Parcelable`. Do it manually only if `@Parcelize` genuinely can't express it (custom serialization logic, unsupported member types). |
101
+
| AI agent | Google GenAI (cloud) + llama (local), behind `GeminiRepository` / `SwitchableGeminiRepository`, with planner/critic/executor agents in `agent/repository`. |
100
102
101
103
> **Persistence policy (authoritative):** new relational/queryable persistence uses **Room** (`@Entity` + DAO + `RoomDatabase` with explicit migrations, provided via Koin). Non-relational settings use the **filesystem/preferences (DataStore)**. **Raw SQLite is the exception, not the default** — see [ADR 0001](docs/adr/0001-prefer-room-for-persistence.md).
102
104
>
@@ -184,13 +186,14 @@ fun onEvent(event: PluginManagerUiEvent) = viewModelScope.launch(Dispatchers.IO)
184
186
185
187
Test code lives both alongside each module and in the shared `testing:{unit,android,lsp,tooling,common}` harnesses. Run with the flox wrapper, e.g. `flox activate -d flox/local -- ./gradlew :testing:unit:test` or a module's `:module:test --tests "…"`.
186
188
187
-
| Layer | Runner / Tools | What to test |
188
-
|---|---|---|
189
-
| Unit (pure JVM) |**JUnit Jupiter (5)**, some legacy **JUnit 4**; assertions via **Google Truth**; mocking via **MockK** (primary) and **Mockito-Kotlin** (legacy) | ViewModels (state transitions over a fake repository), repositories, parsers, builder/tooling logic. Keep these off the device. |
190
-
| JVM + Android framework |**Robolectric**| Code needing `Context`/resources/`SQLiteOpenHelper` without an emulator. |
191
-
| Instrumented / UI |**Espresso** + **AndroidX Test** + **UiAutomator**, run under **Test Orchestrator**; `mockk-android` for on-device mocks | End-to-end IDE flows (create/build/deploy, editor, terminal). |
| Unit (pure JVM) |**JUnit Jupiter (5)**, some legacy **JUnit 4**; assertions via **Google Truth**; mocking via **MockK** (primary) and **Mockito-Kotlin** (legacy) | ViewModels (state transitions over a fake repository), repositories, parsers, builder/tooling logic. Keep these off the device. |
192
+
| JVM + Android framework |**Robolectric**| Code needing `Context`/resources/`SQLiteOpenHelper` without an emulator. |
193
+
| Instrumented / UI |**Espresso** + **AndroidX Test** + **UiAutomator**, run under **Test Orchestrator**; `mockk-android` for on-device mocks | End-to-end IDE flows (create/build/deploy, editor, terminal). |
192
194
193
195
Preferences and conventions:
196
+
194
197
-**Assertions: Google Truth** (`assertThat(x).isEqualTo(...)`) over raw JUnit asserts.
195
198
-**Mocking: MockK** for new code; relax it deliberately rather than over-stubbing.
196
199
- For UDF ViewModels, drive `onEvent(...)`/method calls against a fake or mocked repository and assert the emitted `UiState` sequence (collect the `StateFlow`); assert effects by collecting the effect `SharedFlow`.
* Identity of the installed APK for the extraction stamp: versionName plus the package's lastUpdateTime, which changes on every (re)install - exactly when the bundled jar can change. Null (extract unconditionally) if the lookup fails.
Copy file name to clipboardExpand all lines: common/src/main/java/com/itsaky/androidide/models/SaveResult.java
+17-9Lines changed: 17 additions & 9 deletions
Original file line number
Diff line number
Diff line change
@@ -20,16 +20,24 @@
20
20
/** Result obtained when files are saved */
21
21
publicfinalclassSaveResult {
22
22
23
-
/** Were any Gradle files saved? */
24
-
publicbooleangradleSaved = false;
23
+
/** Were any Gradle files saved? */
24
+
publicbooleangradleSaved = false;
25
25
26
-
/** Were any XML files saved? */
27
-
publicbooleanxmlSaved = false;
26
+
/** Were any XML files saved? */
27
+
publicbooleanxmlSaved = false;
28
28
29
-
publicSaveResult() {}
29
+
/**
30
+
* Were any Android resource XML files (files under a module's {@code res/} directory) saved?
31
+
*
32
+
* <p>
33
+
* Narrower than {@link #xmlSaved} on purpose: only a resource save can change {@code R}, and the Gradle {@code generateSources()} run that follows a save is load-bearing exactly there. Java resolves {@code R.string.*} from the regenerated {@code R.jar} on the compile classpath (the run posts {@code ProjectInitializedEvent}, which makes {@code JavaLanguageServer} drop its stale jar-FS cache), and with view binding on, only {@code dataBindingGenBaseClasses} writes the accessor for an id just added to a layout. Manifest edits and other non-resource XML cannot change {@code R}, so they skip that run.
0 commit comments