mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-20 03:52:15 +02:00
703e6487ae
required me to build release with production signing key locally for upload vs on CI so I could manually work through foreground service justification process on Play Console
459 lines
19 KiB
Groovy
459 lines
19 KiB
Groovy
plugins {
|
|
// Gradle plugin portal
|
|
alias(libs.plugins.tripletPlay)
|
|
alias(libs.plugins.android.application)
|
|
alias(libs.plugins.amazonappstorepublisher)
|
|
alias(libs.plugins.kotlin.android)
|
|
alias(libs.plugins.kotlin.parcelize)
|
|
alias(libs.plugins.kotlin.serialization)
|
|
alias(libs.plugins.keeper)
|
|
id 'idea'
|
|
}
|
|
|
|
repositories {
|
|
google()
|
|
mavenCentral()
|
|
maven { url "https://jitpack.io" }
|
|
}
|
|
|
|
keeper {
|
|
traceReferences {
|
|
// Silence missing definitions
|
|
arguments.set(["--map-diagnostics:MissingDefinitionsDiagnostic", "error", "none"])
|
|
}
|
|
}
|
|
|
|
idea {
|
|
module {
|
|
downloadJavadoc = System.getenv("CI") != "true"
|
|
downloadSources = System.getenv("CI") != "true"
|
|
}
|
|
}
|
|
|
|
def homePath = System.properties['user.home']
|
|
/**
|
|
* @return the current git hash
|
|
* @example edf739d95bad7b370a6ed4398d46723f8219b3cd
|
|
*/
|
|
static def gitCommitHash() {
|
|
"git rev-parse HEAD".execute().text.trim()
|
|
}
|
|
|
|
android {
|
|
namespace "com.ichi2.anki"
|
|
|
|
compileSdk libs.versions.compileSdk.get().toInteger()
|
|
|
|
buildFeatures {
|
|
buildConfig = true
|
|
aidl = true
|
|
}
|
|
|
|
if (rootProject.testReleaseBuild) {
|
|
testBuildType "release"
|
|
} else {
|
|
testBuildType "debug"
|
|
}
|
|
|
|
defaultConfig {
|
|
applicationId "com.ichi2.anki"
|
|
buildConfigField "Boolean", "CI", (System.getenv("CI") == "true").toString()
|
|
buildConfigField "String", "ACRA_URL", '"https://ankidroid.org/acra/report"'
|
|
buildConfigField "String", "BACKEND_VERSION", "\"${libs.versions.ankiBackend.get()}\""
|
|
buildConfigField "Boolean", "ENABLE_LEAK_CANARY", "false"
|
|
buildConfigField "Boolean", "ALLOW_UNSAFE_MIGRATION", "false"
|
|
buildConfigField "String", "GIT_COMMIT_HASH", "\"${gitCommitHash()}\""
|
|
buildConfigField "long", "BUILD_TIME", System.currentTimeMillis().toString()
|
|
resValue "string", "app_name", "AnkiDroid"
|
|
|
|
// The version number is of the form:
|
|
// <major>.<minor>.<maintenance>[dev|alpha<build>|beta<build>|]
|
|
// The <build> is only present for alpha and beta releases (e.g., 2.0.4alpha2 or 2.0.4beta4), developer builds do
|
|
// not have a build number (e.g., 2.0.4dev) and official releases only have three components (e.g., 2.0.4).
|
|
//
|
|
// The version code is derived from the version name as follows:
|
|
// AbbCCtDD
|
|
// A: 1-digit decimal number representing the major version
|
|
// bb: 2-digit decimal number representing the minor version
|
|
// CC: 2-digit decimal number representing the maintenance version
|
|
// t: 1-digit decimal number representing the type of the build
|
|
// 0: developer build
|
|
// 1: alpha release
|
|
// 2: beta release
|
|
// 3: public release
|
|
// DD: 2-digit decimal number representing the build
|
|
// 00 for internal builds and public releases
|
|
// alpha/beta build number for alpha/beta releases
|
|
//
|
|
// This ensures the correct ordering between the various types of releases (dev < alpha < beta < release) which is
|
|
// needed for upgrades to be offered correctly.
|
|
versionCode=21900109
|
|
versionName="2.19alpha9"
|
|
minSdk libs.versions.minSdk.get().toInteger()
|
|
|
|
// Stays until this is in a release: https://github.com/google/desugar_jdk_libs/commit/c01a5446ca13586b801dbba4d83c6821337b3cc2
|
|
if (testReleaseBuild && minSdk < 24) {
|
|
minSdk 24
|
|
}
|
|
|
|
// After #13695: change .tests_emulator.yml
|
|
targetSdk libs.versions.targetSdk.get().toInteger()
|
|
testApplicationId "com.ichi2.anki.tests"
|
|
vectorDrawables.useSupportLibrary = true
|
|
testInstrumentationRunner 'com.ichi2.testutils.NewCollectionPathTestRunner'
|
|
}
|
|
signingConfigs {
|
|
release {
|
|
storeFile file(System.getenv("KEYSTOREPATH") ?: "${homePath}/src/android-keystore")
|
|
storePassword System.getenv("KEYSTOREPWD") ?: System.getenv("KSTOREPWD")
|
|
keyAlias System.getenv("KEYALIAS") ?: "nrkeystorealias"
|
|
keyPassword System.getenv("KEYPWD")
|
|
}
|
|
}
|
|
buildTypes {
|
|
named('debug') {
|
|
versionNameSuffix "-debug"
|
|
debuggable true
|
|
applicationIdSuffix ".debug"
|
|
splits.abi.universalApk = true // Build universal APK for debug always
|
|
// Check Crash Reports page on developer wiki for info on ACRA testing
|
|
// buildConfigField "String", "ACRA_URL", '"https://918f7f55-f238-436c-b34f-c8b5f1331fe5-bluemix.cloudant.com/acra-ankidroid/_design/acra-storage/_update/report"'
|
|
if (project.rootProject.file('local.properties').exists()) {
|
|
Properties localProperties = new Properties()
|
|
localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
|
|
// #6009 Allow optional disabling of JaCoCo for general build (assembleDebug).
|
|
// jacocoDebug task was slow, hung, and wasn't required unless I wanted coverage
|
|
testCoverageEnabled localProperties['enable_coverage'] != "false"
|
|
// not profiled: optimization for build times
|
|
if (localProperties['enable_languages'] == "false") {
|
|
android.defaultConfig.resConfigs "en"
|
|
}
|
|
// allows the scoped storage migration when the user is not logged in
|
|
if (localProperties["allow_unsafe_migration"] != null) {
|
|
buildConfigField "Boolean", "ALLOW_UNSAFE_MIGRATION", localProperties["allow_unsafe_migration"]
|
|
}
|
|
// allow disabling leak canary
|
|
if (localProperties["enable_leak_canary"] != null) {
|
|
buildConfigField "Boolean", "ENABLE_LEAK_CANARY", localProperties["enable_leak_canary"]
|
|
} else {
|
|
buildConfigField "Boolean", "ENABLE_LEAK_CANARY", "true"
|
|
}
|
|
} else {
|
|
testCoverageEnabled true
|
|
}
|
|
|
|
// make the icon red if in debug mode
|
|
resValue 'color', 'anki_foreground_icon_color_0', "#FFFF0000"
|
|
resValue 'color', 'anki_foreground_icon_color_1', "#FFFF0000"
|
|
resValue "string", "applicationId", "${defaultConfig.applicationId}${applicationIdSuffix}"
|
|
}
|
|
named('release') {
|
|
testCoverageEnabled = testReleaseBuild
|
|
minifyEnabled true
|
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
testProguardFile 'proguard-test-rules.pro'
|
|
splits.abi.universalApk = universalApkEnabled // Build universal APK for release with `-Duniversal-apk=true`
|
|
signingConfig signingConfigs.release
|
|
|
|
// syntax: assembleRelease -PcustomSuffix="suffix" -PcustomName="New name"
|
|
if (project.hasProperty("customSuffix")) {
|
|
// the suffix needs a '.' at the start
|
|
applicationIdSuffix project.property("customSuffix").replaceFirst(/^\.*/, ".")
|
|
resValue "string", "applicationId", "${defaultConfig.applicationId}${applicationIdSuffix}"
|
|
} else {
|
|
resValue "string", "applicationId", defaultConfig.applicationId
|
|
}
|
|
if (project.hasProperty("customName")) {
|
|
resValue "string", "app_name", project.property("customName")
|
|
}
|
|
|
|
resValue 'color', 'anki_foreground_icon_color_0', "#FF29B6F6"
|
|
resValue 'color', 'anki_foreground_icon_color_1', "#FF0288D1"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Product Flavors are used for Amazon App Store and Google Play Store.
|
|
* This is because we cannot use Camera Permissions in Amazon App Store (for FireTv etc...)
|
|
* Therefore, different AndroidManifest for Camera Permissions is used in Amazon flavor.
|
|
*
|
|
* This flavor block must stay in sync with the same block in testlib/build.gradle.kts
|
|
*/
|
|
flavorDimensions += "appStore"
|
|
productFlavors {
|
|
create('play') {
|
|
dimension "appStore"
|
|
}
|
|
create('amazon') {
|
|
dimension "appStore"
|
|
}
|
|
// A 'full' build has no restrictions on storage/camera. Distributed on GitHub/F-Droid
|
|
create('full') {
|
|
dimension "appStore"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set this to true to create five separate APKs instead of one:
|
|
* - 2 APKs that only work on ARM/ARM64 devices
|
|
* - 2 APKs that only works on x86/x86_64 devices
|
|
* - a universal APK that works on all devices
|
|
* The advantage is the size of most APKs is reduced by about 2.5MB.
|
|
* Upload all the APKs to the Play Store and people will download
|
|
* the correct one based on the CPU architecture of their device.
|
|
*/
|
|
def enableSeparateBuildPerCPUArchitecture = true
|
|
|
|
splits {
|
|
abi {
|
|
reset()
|
|
enable enableSeparateBuildPerCPUArchitecture
|
|
//universalApk enableUniversalApk // set in debug + release config blocks above
|
|
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
|
}
|
|
}
|
|
// applicationVariants are e.g. debug, release
|
|
applicationVariants.configureEach { variant ->
|
|
// We want the same version stream for all ABIs in debug but for release we can split them
|
|
if (variant.buildType.name == 'release') {
|
|
variant.outputs.configureEach { output ->
|
|
|
|
// For each separate APK per architecture, set a unique version code as described here:
|
|
// https://developer.android.com/studio/build/configure-apk-splits.html
|
|
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
|
def outputFile = output.outputFile
|
|
if (outputFile != null && outputFile.name.endsWith('.apk')) {
|
|
def abi = output.getFilter("ABI")
|
|
if (abi != null) { // null for the universal-debug, universal-release variants
|
|
// From: https://developer.android.com/studio/publish/versioning#appversioning
|
|
// "Warning: The greatest value Google Play allows for versionCode is 2100000000"
|
|
// AnkiDroid versionCodes have a budget 8 digits (through AnkiDroid 9)
|
|
// This style does ABI version code ranges with the 9th digit as 0-4.
|
|
// This consumes ~20% of the version range space, w/50 years of versioning at our major-version pace
|
|
output.versionCodeOverride =
|
|
// ex: 321200106 = 3 * 100000000 + 21200106
|
|
versionCodes.get(abi) * 100000000 + defaultConfig.versionCode
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
testOptions {
|
|
animationsDisabled true
|
|
kotlinOptions {
|
|
freeCompilerArgs += [
|
|
'-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi',
|
|
]
|
|
}
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_11
|
|
targetCompatibility JavaVersion.VERSION_11
|
|
coreLibraryDesugaringEnabled true
|
|
}
|
|
kotlinOptions {
|
|
jvmTarget = JavaVersion.VERSION_11
|
|
}
|
|
|
|
packagingOptions {
|
|
resources {
|
|
excludes += ['META-INF/DEPENDENCIES']
|
|
}
|
|
}
|
|
}
|
|
|
|
play {
|
|
serviceAccountCredentials.set(file("${homePath}/src/AnkiDroid-GCP-Publish-Credentials.json"))
|
|
track.set('alpha')
|
|
}
|
|
|
|
amazon {
|
|
securityProfile = file("${homePath}/src/AnkiDroid-Amazon-Publish-Security-Profile.json")
|
|
applicationId = "amzn1.devportal.mobileapp.524a424d314931494c55383833305539"
|
|
pathToApks = [ file("./build/outputs/apk/amazon/release/AnkiDroid-amazon-universal-release.apk") ]
|
|
replaceEdit = true
|
|
}
|
|
|
|
// Install Git pre-commit hook for Ktlint
|
|
tasks.register('installGitHook', Copy) {
|
|
from new File(rootProject.rootDir, 'pre-commit')
|
|
into { new File(rootProject.rootDir, '.git/hooks') }
|
|
fileMode 0755
|
|
}
|
|
// to run manually: `./gradlew installGitHook`
|
|
tasks.named('preBuild').configure { dependsOn('installGitHook') }
|
|
|
|
tasks.register('copyTestLibIntoAndroidTest', Copy) {
|
|
into new File(rootProject.rootDir, 'AnkiDroid/src/androidTest/java/com/ichi2/testutils')
|
|
from new File(rootProject.rootDir, 'testlib/src/main/java/com/ichi2/testutils')
|
|
into ('common') {
|
|
from 'common'
|
|
}
|
|
}
|
|
tasks.named('preBuild').configure { dependsOn('copyTestLibIntoAndroidTest') }
|
|
tasks.named('runKtlintCheckOverAndroidTestSourceSet').configure { mustRunAfter('copyTestLibIntoAndroidTest') }
|
|
|
|
// Issue 11078 - some emulators run, but run zero tests, and still report success
|
|
tasks.register('assertNonzeroAndroidTests') {
|
|
doLast {
|
|
// androidTest currently creates one .xml file per emulator with aggregate results in this dir
|
|
File folder = file("./build/outputs/androidTest-results/connected/flavors/play")
|
|
File[] listOfFiles = folder.listFiles({ d, f -> f ==~ /.*.xml/ } as FilenameFilter)
|
|
for (File file : listOfFiles) {
|
|
// The aggregate results file currently contains a line with this pattern holding test count
|
|
String[] matches = file.readLines().findAll { it.contains('<testsuite') }
|
|
if (matches.length != 1) {
|
|
throw new GradleScriptException("Unable to determine count of tests executed for " + file.name + ". Regex pattern out of date?", null)
|
|
}
|
|
if (!(matches[0] ==~ /.* tests="\d+" .*/) || matches[0].contains('tests="0"')) {
|
|
throw new GradleScriptException("androidTest executed 0 tests for " + file.name + " - Probably a bug with the emulator. Try another image.", null)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
afterEvaluate {
|
|
tasks.named(androidTestName).configure { finalizedBy('assertNonzeroAndroidTests') }
|
|
}
|
|
|
|
apply from: "./robolectricDownloader.gradle"
|
|
apply from: "./jacoco.gradle"
|
|
apply from: "../lint.gradle"
|
|
|
|
dependencies {
|
|
configurations.configureEach {
|
|
resolutionStrategy {
|
|
// Timber has this as a dependency but they are not up to date. We want to force our version.
|
|
force 'org.jetbrains:annotations:24.1.0'
|
|
}
|
|
}
|
|
api project(":api")
|
|
implementation libs.androidx.work.runtime
|
|
lintChecks project(":lint-rules")
|
|
coreLibraryDesugaring libs.desugar.jdk.libs.nio
|
|
|
|
compileOnly libs.jetbrains.annotations
|
|
compileOnly libs.auto.service.annotations
|
|
annotationProcessor libs.auto.service
|
|
|
|
// modules
|
|
implementation project(":common")
|
|
|
|
implementation libs.androidx.activity
|
|
implementation libs.androidx.annotation
|
|
implementation libs.androidx.appcompat
|
|
implementation libs.androidx.browser
|
|
implementation libs.androidx.core.ktx
|
|
implementation libs.androidx.exifinterface
|
|
implementation libs.androidx.fragment.ktx
|
|
implementation libs.androidx.media
|
|
implementation libs.androidx.preference.ktx
|
|
implementation libs.androidx.recyclerview
|
|
implementation libs.androidx.sqlite.framework
|
|
implementation libs.androidx.swiperefreshlayout
|
|
implementation libs.androidx.viewpager2
|
|
implementation libs.androidx.constraintlayout
|
|
implementation libs.androidx.webkit
|
|
implementation libs.google.material
|
|
implementation libs.android.image.cropper
|
|
implementation libs.nanohttpd
|
|
implementation libs.kotlinx.serialization.json
|
|
implementation libs.seismic
|
|
|
|
debugImplementation libs.androidx.fragment.testing.manifest
|
|
|
|
// Backend libraries
|
|
|
|
implementation libs.protobuf.kotlin.lite // This is required when loading from a file
|
|
|
|
Properties localProperties = new Properties()
|
|
if (project.rootProject.file('local.properties').exists()) {
|
|
localProperties.load(project.rootProject.file('local.properties').newDataInputStream())
|
|
}
|
|
if (localProperties['local_backend'] == "true") {
|
|
implementation files("../../Anki-Android-Backend/rsdroid/build/outputs/aar/rsdroid-release.aar")
|
|
testImplementation files("../../Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar")
|
|
} else {
|
|
implementation libs.ankiBackend.backend
|
|
testImplementation libs.ankiBackend.testing
|
|
}
|
|
|
|
// May need a resolution strategy for support libs to our versions
|
|
implementation libs.acra.limiter
|
|
implementation libs.acra.toast
|
|
implementation libs.acra.dialog
|
|
implementation libs.acra.http
|
|
|
|
implementation libs.commons.compress
|
|
implementation libs.commons.collections4 // SetUniqueList
|
|
implementation libs.commons.io // FileUtils.contentEquals
|
|
implementation libs.mikehardy.google.analytics.java7
|
|
implementation libs.okhttp
|
|
implementation libs.slf4j.timber
|
|
implementation libs.jakewharton.timber
|
|
implementation libs.jsoup
|
|
implementation libs.java.semver // For AnkiDroid JS API Versioning
|
|
implementation libs.drakeet.drawer
|
|
implementation libs.tapTargetPrompt
|
|
implementation libs.colorpicker
|
|
implementation libs.kotlin.reflect
|
|
implementation libs.kotlin.test
|
|
implementation libs.search.preference
|
|
|
|
// Cannot use debugImplementation since classes need to be imported in AnkiDroidApp
|
|
// and there's no no-op version for release build. Usage has been disabled for release
|
|
// build via AnkiDroidApp.
|
|
implementation libs.leakcanary.android
|
|
|
|
testImplementation project(':testlib')
|
|
|
|
// A path for a testing library which provide Parameterized Test
|
|
testImplementation libs.junit.jupiter
|
|
testImplementation libs.junit.jupiter.params
|
|
testImplementation libs.junit.vintage.engine
|
|
testImplementation libs.mockito.inline
|
|
testImplementation libs.mockito.kotlin
|
|
testImplementation libs.hamcrest
|
|
// robolectricDownloader.gradle *may* need a new SDK jar entry if they release one or if we change targetSdk. Instructions in that gradle file.
|
|
testImplementation libs.robolectric
|
|
testImplementation libs.androidx.test.core
|
|
testImplementation libs.androidx.test.junit
|
|
testImplementation libs.kotlin.reflect
|
|
testImplementation libs.kotlin.test
|
|
testImplementation libs.kotlin.test.junit5
|
|
testImplementation libs.kotlinx.coroutines.test
|
|
testImplementation libs.mockk
|
|
testImplementation libs.commons.exec // obtaining the OS
|
|
testImplementation libs.androidx.fragment.testing
|
|
// in a JvmTest we need org.json.JSONObject to not be mocked
|
|
testImplementation libs.json
|
|
testImplementation libs.ivanshafran.shared.preferences.mock
|
|
testImplementation libs.androidx.test.runner
|
|
testImplementation libs.androidx.test.rules
|
|
testImplementation libs.androidx.espresso.core
|
|
testImplementation(libs.androidx.espresso.contrib) {
|
|
exclude module: "protobuf-lite"
|
|
}
|
|
testImplementation libs.androidx.work.testing
|
|
// for testing flows
|
|
testImplementation libs.cashapp.turbine
|
|
|
|
// we should depend directly on the common testlib for androidTest, but we cannot
|
|
// until coverage-breaking issue is fixed https://issuetracker.google.com/issues/332746900
|
|
// androidTestImplementation project(':testlib')
|
|
|
|
// May need a resolution strategy for support libs to our versions
|
|
androidTestImplementation libs.androidx.espresso.core
|
|
androidTestImplementation(libs.androidx.espresso.contrib) {
|
|
exclude module: "protobuf-lite"
|
|
}
|
|
androidTestImplementation libs.androidx.test.core
|
|
androidTestImplementation libs.androidx.test.junit
|
|
androidTestImplementation libs.androidx.test.rules
|
|
androidTestImplementation libs.androidx.uiautomator
|
|
androidTestImplementation libs.kotlin.test
|
|
androidTestImplementation libs.kotlin.test.junit
|
|
androidTestImplementation libs.androidx.fragment.testing
|
|
}
|