Skip to content

Commit 2e5650a

Browse files
committed
arch: split ':common:android' from ':common'
In preparation for moving libanki to be pure JVM, we need :common to be a java-library. JSON is supported by both android and JVM (with slightly differing apis so it can remain in `:common` Assisted-by: Claude Opus 4.6 Related to https://redirect.github.com/ankidroid/Anki-Android-Backend/issues/674
1 parent 368b788 commit 2e5650a

14 files changed

Lines changed: 129 additions & 116 deletions

File tree

AnkiDroid/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,7 @@ dependencies {
385385

386386
// modules
387387
implementation project(":common")
388+
implementation project(":common:android")
388389
implementation project(":compat")
389390
implementation project(":libanki")
390391
implementation project(":vbpd")

AnkiDroid/jacoco.gradle

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,10 @@ tasks.register('jacocoTestReport', JacocoReport) {
126126
dependsOn("connectedPlay${rootProject.androidTestVariantName}AndroidTest")
127127
}
128128

129-
// modules do not yet support flavors (play/full)
130-
def modulesToUnitTest = [":common", ":libanki", ":compat"]
129+
// Android library modules (do not yet support flavors play/full)
130+
def androidModulesToUnitTest = [":libanki", ":compat"]
131+
// JVM modules: use 'test' task and 'classes/kotlin/main' class dir
132+
def jvmModulesToUnitTest = [":common"]
131133

132134
// A unit-test only report task
133135
tasks.register('jacocoUnitTestReport', JacocoReport) {report ->
@@ -145,10 +147,15 @@ tasks.register('jacocoUnitTestReport', JacocoReport) {report ->
145147
includeUnitTestCoverage(report, getProject(), flavorClassDir)
146148
dependsOn('testPlayDebugUnitTest')
147149

148-
for (module in modulesToUnitTest) {
150+
for (module in androidModulesToUnitTest) {
149151
includeUnitTestCoverage(report, project(module), moduleClassDir)
150152
dependsOn("${module}:testDebugUnitTest")
151153
}
154+
155+
for (module in jvmModulesToUnitTest) {
156+
includeUnitTestCoverage(report, project(module), 'classes/kotlin/main')
157+
dependsOn("${module}:test")
158+
}
152159
}
153160

154161
gradle.projectsEvaluated {
@@ -159,12 +166,18 @@ gradle.projectsEvaluated {
159166
task.outputs.cacheIf { false }
160167
}
161168
}
162-
for (module in modulesToUnitTest) {
169+
for (module in androidModulesToUnitTest) {
163170
project(module).tasks.named('testDebugUnitTest')?.configure {task ->
164171
task.outputs.upToDateWhen { false }
165172
task.outputs.cacheIf { false }
166173
}
167174
}
175+
for (module in jvmModulesToUnitTest) {
176+
project(module).tasks.named('test')?.configure {task ->
177+
task.outputs.upToDateWhen { false }
178+
task.outputs.cacheIf { false }
179+
}
180+
}
168181
}
169182

170183

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ subprojects {
116116
compilerArgs += "-XXLanguage:+ExplicitBackingFields"
117117
}
118118

119-
if (project.path !in listOf(":api", ":common")) {
119+
if (project.path !in listOf(":api", ":common", ":common:android")) {
120120
compilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
121121
}
122122
if (project.path != ":api") {

common/README.md

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
## AnkiDroid Common
22

3-
AnkiDroid Common is a [Gradle module](https://developer.android.com/topic/modularization)
3+
AnkiDroid Common is a pure JVM [Gradle module](https://developer.android.com/topic/modularization)
44
containing utility functions, and definitions for core functionality used by other modules
55
within AnkiDroid. Common should be the base of the AnkiDroid dependency tree.
66

7-
Common is used by `libAnki` (which has no Android dependencies), so dependencies on the Android
8-
framework should be in packages named `android`.
7+
Common has no Android dependencies. Code requiring Android APIs belongs in `:common:android`.
98

109
This module is expected to define interfaces which are initialized in the `AnkiDroid` module
1110

@@ -19,48 +18,29 @@ These are to be initialized higher up the dependency tree, typically in `AnkiDro
1918

2019
### `com.ichi2.anki.common.utils`
2120

22-
Utility classes and methods without an Android dependency
23-
21+
Utility classes and methods
2422

2523
### `com.ichi2.anki.common.utils.ext`
2624

2725
Extension methods, universally applicable to the classes they extend
2826

29-
Examples:
30-
31-
* `Int.kt` - `ifNotZero`
32-
* `InputStream.kt` - `convertToString`
33-
34-
### `com.ichi2.anki.common.utils.android`
35-
36-
Utilities with a dependency on Android
37-
3827
## Context
3928

40-
This is a work in progress. As discussed in
29+
As discussed in
4130
[#12582](https://github.com/ankidroid/Anki-Android/issues/12582), AnkiDroid decided to split the
4231
codebase into two modules, `libAnki` (business logic) and `AnkiDroid` (code interacting with
43-
Android APIs).
44-
45-
At the time of writing, this split is not yet done. We expect to do it with the following steps:
32+
Android APIs). `common` existed for logic which both `AnkiDroid` and `libAnki` depended on.
4633

47-
* `com.ichi2.compat` was deemed to be an easy module to split out to trial this refactor
48-
but this had circular dependencies
49-
* A `common` module was proposed to fix this
50-
* To reduce the execution time of tests, `libAnki` should have no dependencies on Android
51-
* A lint rule will be applied to `libAnki` from using Android dependencies
52-
* The alternate: splitting modules based on architecture was deemed to be unwieldy
34+
Later, `compat` was split out, also depending on `common`, solidifying `common`
5335

54-
The following were blockers for `compat` to be split out
36+
[Backend - #647](https://github.com/ankidroid/Anki-Android-Backend/issues/674): `libAnki` is
37+
intended to be converted to a `java-library`, so `:common` was split into `:common:android`,
38+
ensuring that
5539

56-
* `isRobolectric`
57-
* `CrashReportService`
58-
* `showThemedToast`
59-
* `TimeManager` (maybe)
60-
* `@KotlinCleanup` (maybe)
6140

6241
Discussed on Discord: https://discord.gg/qjzcRTx
6342

6443
* Discussion: https://discord.com/channels/368267295601983490/701922522836369498/1243991110888591482
6544
* Thread: https://discord.com/channels/368267295601983490/1244372448233914438
6645
* https://github.com/ankidroid/Anki-Android/pull/16498
46+
* [#20547 - extract `:compat`](https://github.com/ankidroid/Anki-Android/issues/20547)

common/android/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
## AnkiDroid Common (Android)
2+
3+
Android-specific utilities which are generally applicable to AnkiDroid.
4+
5+
Split from `:common` to ensure that `:common` is a `java-library`, to support fast, pure-JVM tests.
6+
7+
## Packages
8+
9+
### `com.ichi2.anki.common.utils.android`
10+
11+
Android-specific utilities (e.g. `isRobolectric`)
12+
13+
### `com.ichi2.anki.common.utils.ext`
14+
15+
Extension methods on Android framework classes (e.g. `Intent`)
16+
17+
### `com.ichi2.anki.utils.android`
18+
19+
Color manipulation utilities (`darkenColor`, `lightenColorAbsolute`)

common/android/build.gradle.kts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2026 David Allison <davidallisongithub@gmail.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
17+
import com.android.build.api.dsl.LibraryExtension
18+
19+
plugins {
20+
alias(libs.plugins.android.library)
21+
}
22+
23+
configure<LibraryExtension> {
24+
namespace = "com.ichi2.anki.common.android"
25+
compileSdk =
26+
libs.versions.compileSdk
27+
.get()
28+
.toInt()
29+
30+
defaultConfig {
31+
minSdk =
32+
libs.versions.minSdk
33+
.get()
34+
.toInt()
35+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
36+
consumerProguardFiles("consumer-rules.pro")
37+
}
38+
39+
buildTypes {
40+
release {
41+
isMinifyEnabled = true
42+
proguardFiles(
43+
getDefaultProguardFile("proguard-android-optimize.txt"),
44+
"proguard-rules.pro",
45+
)
46+
}
47+
}
48+
49+
compileOptions {
50+
sourceCompatibility = JavaVersion.VERSION_17
51+
targetCompatibility = JavaVersion.VERSION_17
52+
}
53+
}
54+
55+
apply(from = "../../lint.gradle")
56+
apply(from = "../../jacocoSupport.gradle")
57+
58+
dependencies {
59+
implementation(project(":common"))
60+
61+
implementation(libs.androidx.annotation)
62+
implementation(libs.jakewharton.timber)
63+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest>
3+
4+
</manifest>

common/src/main/java/com/ichi2/anki/common/utils/android/TestUtils.kt renamed to common/android/src/main/java/com/ichi2/anki/common/utils/android/TestUtils.kt

File renamed without changes.

common/src/main/java/com/ichi2/anki/common/utils/ext/Intent.kt renamed to common/android/src/main/java/com/ichi2/anki/common/utils/ext/Intent.kt

File renamed without changes.

common/src/main/java/com/ichi2/anki/utils/android/ColorUtils.kt renamed to common/android/src/main/java/com/ichi2/anki/utils/android/ColorUtils.kt

File renamed without changes.

0 commit comments

Comments
 (0)