buildscript { repositories { google() mavenCentral() } // Detect flavors from the task name. def taskName = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase() def isFdroid = taskName.contains('fdroid') def isBeta = taskName.contains('beta') dependencies { classpath libs.android.tools classpath(libs.triplet.play.publisher) classpath(libs.huawei.publish) } } repositories { google() mavenCentral() maven { url 'https://www.jitpack.io' } // MPAndroidChart } apply plugin: 'com.android.application' apply plugin: 'com.github.triplet.play' apply plugin: 'ru.cian.huawei-publish-gradle-plugin' def run(cmd) { def stdout = new ByteArrayOutputStream() exec { commandLine = cmd standardOutput = stdout } return stdout.toString() } import com.github.triplet.gradle.androidpublisher.ReleaseStatus import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform def getVersion() { def isWindows = DefaultNativePlatform.getCurrentOperatingSystem().isWindows() def bash = isWindows ? 'C:\\Program Files\\Git\\bin\\bash.exe' : 'bash' def versionCode = Integer.parseInt(run([bash, '../../tools/unix/version.sh', 'android_code']).trim()) def versionName = run([bash, '../../tools/unix/version.sh', 'android_name']).trim() return new Tuple2(versionCode, versionName) } def getCommitMessage() { return run(['git', '--no-pager', 'show', '-s', '--format=%s%n%n%b', 'HEAD']).trim() } def osName = System.properties['os.name'].toLowerCase() project.ext.appId = 'app.comaps' project.ext.appName = 'CoMaps' // I have Java 21 installed, but this doesn't work on MacOS. //java { // toolchain { // languageVersion.set(JavaLanguageVersion.of(17)) // } //} android { namespace 'app.organicmaps' buildFeatures { dataBinding = true buildConfig = true } // All properties are read from gradle.properties file compileSdk propCompileSdkVersion.toInteger() ndkVersion '27.2.12479018' defaultConfig { // Default package name is taken from the manifest and should be app.comaps def ver = getVersion() versionCode = ver.V1 versionName = ver.V2 println('Version: ' + versionName) println('VersionCode: ' + versionCode) minSdk propMinSdkVersion.toInteger() targetSdk propTargetSdkVersion.toInteger() applicationId project.ext.appId buildConfigField 'String', 'SUPPORT_MAIL', '"android@comaps.app"' // Should be customized in flavors. buildConfigField 'String', 'REVIEW_URL', '""' externalNativeBuild { def pchFlag = 'OFF' if (project.hasProperty('pch')) pchFlag = 'ON' def njobs = '' if (project.hasProperty('njobs')) njobs = project.getProperty('njobs') def enableVulkanDiagnostics = 'OFF' if (project.hasProperty('enableVulkanDiagnostics')) { enableVulkanDiagnostics = project.getProperty('enableVulkanDiagnostics') } def enableTrace = 'OFF' if (project.hasProperty('enableTrace')) { enableTrace = project.getProperty('enableTrace') } cmake { cppFlags '-fexceptions', '-frtti' // There is no sense to enable sections without gcc's --gc-sections flag. cFlags '-fno-function-sections', '-fno-data-sections', '-Wno-extern-c-compat' arguments '-DANDROID_TOOLCHAIN=clang', '-DANDROID_STL=c++_static', "-DOS=$osName", '-DSKIP_TESTS=ON', '-DSKIP_TOOLS=ON', "-DUSE_PCH=$pchFlag", "-DNJOBS=$njobs", "-DENABLE_VULKAN_DIAGNOSTICS=$enableVulkanDiagnostics", "-DENABLE_TRACE=$enableTrace" targets 'organicmaps' } } // Use, for example, -Parm32 gradle parameter to build only for armeabi-v7a. ndk { abiFilters = new HashSet<>() if (project.hasProperty('arm32') || project.hasProperty('armeabi-v7a')) { abiFilters.add('armeabi-v7a') } if (project.hasProperty('arm64') || project.hasProperty('arm64-v8a')) { abiFilters.add('arm64-v8a') } if (project.hasProperty('x86')) { abiFilters.add('x86') } if (project.hasProperty('x86_64') || project.hasProperty('x64')) { abiFilters.add('x86_64') } if (abiFilters.isEmpty()) { abiFilters.add('armeabi-v7a') abiFilters.add('arm64-v8a') // For the emulator, chromebooks and some Intel Atom devices. abiFilters.add('x86_64') } println('Building for ' + abiFilters + ' archs.') } setProperty('archivesBaseName', appName.replaceAll('\\s','') + '-' + defaultConfig.versionCode) } flavorDimensions += 'default' productFlavors { // 01 is a historical artefact, sorry. final int HUAWEI_VERSION_CODE_BASE = 01_00_00_00_00 google { dimension 'default' applicationIdSuffix '.google' versionName = android.defaultConfig.versionName + '-Google' buildConfigField 'String', 'SUPPORT_MAIL', '"gplay@comaps.app"' buildConfigField 'String', 'REVIEW_URL', '"market://details?id=app.comaps.google"' } // Distributed directly by the project, e.g. in repo releases, chats, etc. web { dimension 'default' versionName = android.defaultConfig.versionName buildConfigField 'String', 'SUPPORT_MAIL', '"apk@comaps.app"' } fdroid { dimension 'default' applicationIdSuffix '.fdroid' versionName = android.defaultConfig.versionName + '-FDroid' buildConfigField 'String', 'SUPPORT_MAIL', '"fdroid@comaps.app"' } huawei { dimension 'default' applicationIdSuffix '.huawei' versionName = android.defaultConfig.versionName + '-Huawei' versionCode = HUAWEI_VERSION_CODE_BASE + android.defaultConfig.versionCode buildConfigField 'String', 'SUPPORT_MAIL', '"huawei@comaps.app"' buildConfigField 'String', 'REVIEW_URL', '"appmarket://details?id=app.comaps"' } } playConfigs { googleRelease { enabled.set(true) } } splits.abi { boolean enabled = project.hasProperty('splitApk') println ('Create separate apks: ' + enabled) enable enabled reset() include 'x86', 'armeabi-v7a', 'arm64-v8a', 'x86_64' universalApk true } lint { disable 'MissingTranslation' // https://github.com/organicmaps/organicmaps/issues/3551 disable 'MissingQuantity', 'UnusedQuantity' // https://github.com/organicmaps/organicmaps/issues/3550 disable 'ByteOrderMark' // https://github.com/organicmaps/organicmaps/issues/1077 disable 'CustomSplashScreen' // https://github.com/organicmaps/organicmaps/issues/3610 disable 'InsecureBaseConfiguration' abortOnError true } gradle.projectsEvaluated { android.applicationVariants.all { variant -> def task = variant.name.capitalize() project.task(type: Exec, "run${task}", dependsOn: "install${task}") { commandLine android.getAdbExe(), 'shell', 'am', 'start', '-n', "$applicationId/app.organicmaps.DownloadResourcesActivity", '-a', 'android.intent.action.MAIN', '-c', 'android.intent.category.LAUNCHER' } } } def secureReleasePropertiesFileExists = file('secure.properties.release').exists() if (secureReleasePropertiesFileExists) { apply from: 'secure.properties.release' } def secureTestPropertiesFileExists = file('secure.properties.test').exists() if (secureTestPropertiesFileExists) { apply from: 'secure.properties.test' } signingConfigs { debug { storeFile file('comaps-debug.keystore') storePassword '12345678' keyAlias 'CoMaps Debug' keyPassword '12345678' } test { if (secureTestPropertiesFileExists) { storeFile file(secretTestStoreFile) storePassword secretTestStorePassword keyAlias secretTestKeyAlias keyPassword secretTestKeyPassword } else { println('secure.properties.test doesn\'t exist') } } release { if (secureReleasePropertiesFileExists) { storeFile file(secretReleaseStoreFile) storePassword secretReleaseStorePassword keyAlias secretReleaseKeyAlias keyPassword secretReleaseKeyPassword } else { println('secure.properties.release doesn\'t exist') } } } buildTypes { def taskName = getGradle().getStartParameter().getTaskRequests().toString().toLowerCase() debug { applicationIdSuffix '.debug' // Allows to install debug and release builds together versionNameSuffix '-debug' jniDebuggable true // Enable jni debug build zipAlignEnabled true signingConfig signingConfigs.debug resValue 'string', 'app_name', 'CoMaps Debug' // Do not generate separate debug symbols for debug apps, because we don't distribute them. ndk.debugSymbolLevel = 'none' } release { if (taskName.contains('release')) { if (secureReleasePropertiesFileExists) { println('Using RELEASE signing keys from secure.properties.release') signingConfig signingConfigs.release } else { println('NO RELEASE signing keys found') println('Using DEBUG signing keys') signingConfig signingConfigs.debug } } minifyEnabled true shrinkResources true // Includes the default ProGuard rules files that are packaged with the Android Gradle plugin. // To learn more, go to the documentation section about R8 configuration files. proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' resValue 'string', 'app_name', project.ext.appName // Full size symbols are too big for Google, 217mb aab vs 95mb. ndk.debugSymbolLevel = 'symbol_table' } // TODO(@pastk): rename to "test" everywhere in code beta { applicationIdSuffix '.test' versionNameSuffix '-test' if (taskName.contains('beta')) { if (secureTestPropertiesFileExists) { println('Using TEST signing keys from secure.properties.test') signingConfig signingConfigs.test } else { println('NO TEST signing keys found') println('Using DEBUG signing keys') signingConfig signingConfigs.debug } } minifyEnabled true shrinkResources true // Includes the default ProGuard rules files that are packaged with the Android Gradle plugin. // To learn more, go to the documentation section about R8 configuration files. proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' matchingFallbacks = ['release'] // use dependencies of "release" build type resValue 'string', 'app_name', 'CoMaps Test' // Full size symbols are too big for Google, 217mb aab vs 95mb. ndk.debugSymbolLevel = 'symbol_table' } } externalNativeBuild { cmake { version '3.22.1+' buildStagingDirectory './nativeOutputs' path '../../CMakeLists.txt' } } // We don't compress these extensions in assets/ because our random FileReader can't read zip-compressed files from apk. // TODO: Load all minor files via separate call to ReadAsString which can correctly handle compressed files in zip containers. androidResources { ignoreAssetsPattern '!.svn:!.git:!.DS_Store:!*.scc:.*:_*:!CVS:!thumbs.db:!picasa.ini:!*~' noCompress = ['txt', 'bin', 'html', 'png', 'json', 'mwm', 'ttf', 'sdf', 'ui', 'config', 'csv', 'spv', 'obj'] localeFilters += [ "af", "ar", "az", "be", "bg", "ca", "cs", "da", "de", "el", "en", "en-rGB", "es", "es-rMX", "et", "eu", "fa", "fi", "fr", "fr-rCA", "iw", "hi", "hu", "in", "it", "ja", "ko", "lt", "lv", "mr", "mt", "nb", "nl", "pl", "pt", "pt-rBR", "ro", "ru", "sk", "sr", "sv", "sw", "th", "tr", "uk", "vi", "zh", "zh-rHK", "zh-rMO", "zh-rTW" ] } compileOptions { coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } dependencies { coreLibraryDesugaring libs.android.tools.desugar // Google Play Location Services // TODO(@pastk): enabled via microG in all flavors, // so move google/java/app/organicmaps/location/* into main/ and remove symlinks. // // Please add symlinks to google/java/app/organicmaps/location for each new gms-enabled flavor below: // ``` // mkdir -p src/$flavor/java/app/organicmaps/ // ln -sf ../../../../google/java/app/organicmaps/location src/$flavor/java/app/organicmaps/ // ls -la src/$flavor/java/app/organicmaps/location/GoogleFusedLocationProvider.java // ``` // // microG project's FOSS re-implementation of the proprietary libs.google.services.location implementation libs.microg.services.location // This line is added as a workaround for duplicate classes error caused by some outdated dependency: // > A failure occurred while executing com.android.build.gradle.internal.tasks.CheckDuplicatesRunnable // We don't use Kotlin, but some dependencies are actively using it. // See https://stackoverflow.com/a/75719642 implementation libs.androidx.core implementation(platform(libs.jetbrains.kotlin.bom)) implementation libs.androidx.annotation implementation libs.androidx.appcompat implementation libs.androidx.car.app implementation libs.androidx.car.app.projected implementation libs.androidx.constraintlayout implementation libs.androidx.fragment implementation libs.androidx.preference implementation libs.androidx.recyclerview implementation libs.androidx.work.runtime implementation libs.androidx.lifecycle.process implementation libs.android.material // Fix for app/organicmaps/util/FileUploadWorker.java:14: error: cannot access ListenableFuture // https://github.com/organicmaps/organicmaps/issues/6106 implementation libs.google.guava implementation libs.devnullorthrow.mpandroidchart implementation libs.jcip.annotations // Test Dependencies androidTestImplementation libs.androidx.test.junit testImplementation libs.junit testImplementation libs.mockito.core } tasks.withType(JavaCompile) { options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' } android.applicationVariants.all { variant -> def authorityValue = variant.applicationId + ".provider" def authority = "\"" + authorityValue + "\"" variant.buildConfigField 'String', 'FILE_PROVIDER_AUTHORITY', authority def flavor = variant.getMergedFlavor() flavor.manifestPlaceholders += [FILE_PROVIDER_PLACEHOLDER : authorityValue] variant.resValue 'string', 'app_id', variant.applicationId } play { enabled.set(false) track.set('production') defaultToAppBundles.set(true) releaseStatus.set(ReleaseStatus.IN_PROGRESS) serviceAccountCredentials.set(file('google-play.json')) } huaweiPublish { instances { huaweiRelease { credentialsPath = "$projectDir/huawei-appgallery.json" buildFormat = 'aab' deployType = 'draft' // confirm manually def releaseDescriptions = [] def localeOverride = [ 'am' : 'am-ET', 'gu': 'gu_IN', 'iw-IL': 'he_IL', 'kn-IN': 'kn_IN', 'ml-IN': 'ml_IN', 'mn-MN': 'mn_MN', 'mr-IN': 'mr_IN', 'ta-IN': 'ta_IN', 'te-IN': 'te_IN', ] def files = fileTree(dir: "$projectDir/src/fdroid/play/listings", include: '**/release-notes.txt') files.each { File file -> def path = file.getPath() def locale = file.parentFile.getName() locale = localeOverride.get(locale, locale) releaseDescriptions.add(new ru.cian.huawei.publish.ReleaseNote(locale, path)) } releaseNotes = new ru.cian.huawei.publish.ReleaseNotesExtension(releaseDescriptions, true) } } }