import java.util.regex.Matcher import java.util.regex.Pattern apply plugin: 'com.android.application' android { ndkVersion "28.2.13676358" namespace = "se.leap.bitmaskclient" compileOptions { targetCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_21 } viewBinding { enabled = true } packaging { jniLibs { useLegacyPackaging = true } } buildFeatures { buildConfig true aidl true } defaultConfig { applicationId "se.leap.bitmaskclient" // the factor 1000 is used so that gplay users can upgrade from split apks ((current version number - 1) * 1000) + n // to extracted bundle apks, supplied by google // however we don't calculate the versionCode here, because F-Droid doesn't like that versionCode 183000 versionName "1.6.0" compileSdk 35 minSdkVersion 24 targetSdkVersion 35 vectorDrawables.useSupportLibrary = true buildConfigField 'boolean', 'openvpn3', 'false' //Build Config Fields for default donation details // preferred API version the client should client try in case the provider supports it // If the provider doesn't the client falls back to lower available API versions buildConfigField 'int', 'preferred_client_api_version', '5' //This is the default donation URL and should be set to the donation page of LEAP // and this should not be set/altered anywhere else. buildConfigField 'String', 'default_donation_url', '"https://riseuplabs.org/leap"' // custom donation url, only used in Custom flavor buildConfigField 'String', 'donation_url', 'null' //The field to enable donations in the app. buildConfigField 'boolean', 'enable_donation', 'false' //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled. buildConfigField 'boolean', 'enable_donation_reminder', 'true' //The duration in days to trigger the donation reminder buildConfigField 'int', 'donation_reminder_duration', '7' //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead buildConfigField 'boolean', 'priotize_anonymous_usage', 'false' //allow manual gateway selection buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true' // obfsvpn Debugging config fields to pin and configure a particular proxy // demo.bitmask.net provider buildConfigField "String", "obfsvpn_port", '"4431"' buildConfigField "String", "obfsvpn_ip", '"37.218.241.208"' buildConfigField "String", "obfsvpn_cert", '"k0L4LFg0Wk98v7P66xvgAx2ud+kggvjZX/qul3iFTJGH5X7xSHT+vVL4UZR0WI3SkmDzUg"' // obfsvpn_transport_protocol can be any of "tcp", "kcp" or "quic" buildConfigField "String", 'obfsvpn_transport_protocol', '"kcp"' // default to UDP usage buildConfigField 'boolean', 'prefer_udp', 'false' // static update url pointing to the latest stable release apk buildConfigField "String", "update_apk_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk"' // the the pgp signature file of the apk buildConfigField "String", "signature_url", '"https://dl.bitmask.net/client/android/Bitmask-Android-latest.apk.sig"' // the version file contains the versionCode of the latest release buildConfigField "String", "version_file_url", '"https://dl.bitmask.net/client/android/versioncode.txt"' // if center_actionbar_title is set to true, the actionbar title and subtitle will be centered, otherwise default buildConfigField 'boolean', 'actionbar_center_title', 'true' buildConfigField 'boolean', 'actionbar_capitalize_title', 'true' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' } bundle { language { enableSplit = true } density { enableSplit = true } abi { enableSplit = true } } } flavorDimensions "branding", "implementation", "abi" productFlavors { productFlavors.all { ext.appName = null ext.splitApk = false } production { dimension "implementation" } normal { dimension "branding" appName = "Bitmask" splitApk = true } custom { dimension "branding" //************************************************************************** //************************************************************************** //Configurations for custom branded app. // //In order to configure the URLs for your custom provider (required!), please //check out app/src/custom/README.md // //Change the package name as needed, e.g. "org.example.myapp" applicationId "se.leap.riseupvpn" //Set app name here appName = "Riseup VPN" //Change the versionCode as needed //versionCode 1 //Change the versionName as needed //versionName "0.9.9RC1" // preferred API version the client should client try in case the provider supports it // If the provider doesn't the client falls back to lower available API versions buildConfigField 'int', 'preferred_client_api_version', '5' //skip the account creation / login screen if the provider offers anonymous vpn usage, use directly the anonymous cert instead buildConfigField 'boolean', 'priotize_anonymous_usage', 'true' //allow manual gateway selection buildConfigField 'boolean', 'allow_manual_gateway_selection', 'true' // obfsvpn Debugging config fields to pin and configure a particular proxy buildConfigField "String", "obfsvpn_port", '"443"' buildConfigField "String", "obfsvpn_ip", '"159.223.173.205"' buildConfigField "String", "obfsvpn_cert", '"8nuAbPJwFrKc/29KcCfL5LBuEWxQrjBASYXdUbwcm9d9pKseGK4r2Tg47e23+t6WghxGGw"' buildConfigField 'boolean', 'obfsvpn_use_kcp', 'true' // default to UDP usage buildConfigField 'boolean', 'prefer_udp', 'true' //Build Config Fields for automatic apk update checks // static update url pointing to the latest stable release apk def apkURL = '"https://dl.bitmask.net/RiseupVPN/android/RiseupVPN-Android-latest.apk"' buildConfigField "String", "update_apk_url", apkURL // the the pgp signature file of the apk def signatureURL = '"https://dl.bitmask.net/RiseupVPN/android/RiseupVPN-Android-latest.apk.sig"' buildConfigField "String", "signature_url", signatureURL // the version file should contain a single line with the versionCode of the latest release buildConfigField "String", "version_file_url", '"https://dl.bitmask.net/client/android/versioncode.txt"' //'"https://dl.bitmask.net/RiseupVPN/android/versioncode.txt"' //Build Config Fields for default donation details //This is the donation URL and should be set to the relevant donation page. buildConfigField 'String', 'donation_url', '"https://riseup.net/vpn/donate"' //The field to enable donations in the app. buildConfigField 'boolean', 'enable_donation', 'true' //The field to enable donation reminder popup in the app if enable_donation is set to 'false' this will be disabled. buildConfigField 'boolean', 'enable_donation_reminder', 'true' //The duration in days to trigger the donation reminder buildConfigField 'int', 'donation_reminder_duration', '7' // ActionBar design // if center_actionbar_title is set to true, the actionbar title and subtitle will be centered, otherwise default buildConfigField 'boolean', 'actionbar_center_title', 'true' buildConfigField 'boolean', 'actionbar_capitalize_title', 'false' // Build apks for each architecture, in addition to one 'fat' apk containing libraries for all all architectures // enable this if you're publishing in gplay ext { splitApk = true } //************************************************************************** //************************************************************************** } fatweb { dimension "abi" ext { abiVersionCode = 0 abiFilter = "web" } } fat { dimension "abi" ext { abiVersionCode = 0 abiFilter = "" } } x86 { dimension "abi" ndk { abiFilters "x86" } ext { abiVersionCode = 1 abiFilter = "x86" } } armv7 { dimension "abi" ndk { abiFilters "armeabi-v7a" } ext { abiVersionCode = 2 abiFilter = "armeabi-v7a" } } x86_64 { dimension "abi" ndk { abiFilters "x86_64" } ext { abiVersionCode = 3 abiFilter = "x86_64" } } arm64 { dimension "abi" ndk { abiFilters "arm64-v8a" } ext { abiVersionCode = 4 abiFilter = "arm64-v8a" } } } buildTypes { buildTypes.all { ext.appSuffix = "" } release { //runProguard true //ndk.debugSymbolLevel = "full" //minifyEnabled = true //shrinkResources true buildConfigField "Boolean", "DEBUG_MODE", "false" } beta { initWith release applicationIdSuffix ".beta" appSuffix = " Beta" buildConfigField "Boolean", "DEBUG_MODE", "true" // tor-android doesn't know this build-type, fallback to release in that case matchingFallbacks = ['release'] enableUnitTestCoverage false enableAndroidTestCoverage false } debug { buildConfigField "Boolean", "DEBUG_MODE", "true" enableUnitTestCoverage false enableAndroidTestCoverage false } } sourceSets { main { assets.srcDirs = ['assets', 'ovpnlibs/assets', '../ics-openvpn/main/build/ovpnassets', ] jniLibs.srcDirs = ['../ics-openvpn/main/build/intermediates/cmake/skeletonOvpn2Release/obj/'] jni.srcDirs = [] //disable automatic ndk-build } debug { assets.srcDirs = ['src/debug/assets'] } test { resources.srcDirs += ['src/test/resources'] } fatweb { java.srcDirs += ['src/fatweb/java'] } fat { java.srcDirs += ['src/notFatweb/java'] } x86 { java.srcDirs += ['src/notFatweb/java'] } x86_64 { java.srcDirs += ['src/notFatweb/java'] } armv7 { java.srcDirs += ['src/notFatweb/java'] } arm64 { java.srcDirs += ['src/notFatweb/java'] } } /** * BUILD VARAIANTS: * ================= * Development builds: * -------------------- * customProductionFatDebug -> branded development build, includes all ABIs * normalProductionFatDebug -> Bitmask development build, includes all ABIS * customProductionFatwebDebug -> branded development build, includes all ABIs, for distribution through a download page * normalProductionFatWebDebug -> Bitmask development build, includes all ABIS, for distribution through a download page * * Branded Releases: * ----------------- * customProductionFatBeta -> branded build, includes all ABI's, Beta release * customProductionFatRelease -> branded build, includes all ABI's, stable release (-> F-Droid, GPlay if not splitApk is set to true) * customProductionFatwebRelease -> branded build, includes all ABI's, stable release (-> F-Droid, GPlay if not splitApk is set to true), for distribution through a download page * * Bitmask Beta releases: * ---------------------- * normalProductionArm64Beta -> Bitmask build, only for ABI arm64, for GPlay Beta channel with split apks (1 of 4) * normalProductionArmv7Beta -> Bitmask build, only for ABI armeabi-v7a, for GPlay Beta channel with split apks (2 of 4) * normalProductionX86Beta -> Bitmask build, only for ABI x86, for GPlay Beta channel with split apks (3 of 4) * normalProductionX86_64Beta -> Bitmask build, only for ABI x86 64 bit, for GPlay Beta channel with split apks (4 of 4) * normalProductionFatBeta -> Bitmask build, including all ABIS, for izzysoft's F-Droid repo and beta link on download page * * Bitmask Stable releases: * ------------------------ * normalProductionArm64Release -> Bitmask build, only for ABI arm64, for GPlay releases with split apks (1 of 4) * normalProductionArmv7Release -> Bitmask build, only for ABI armeabi-v7a, for GPlay releases with split apks (2 of 4) * normalProductionX86Release -> Bitmask build, only for ABI x86, for GPlay releases with split apks (3 of 4) * normalProductionX86_64Release -> Bitmask build, only for ABI x86 64 bit, for GPlay releases with split apks (4 of 4) * normalProductionFatRelease -> Bitmask build, including all ABIS, for official F-Droid repo * normalProductionFatWebRelease -> Bitmask build, including all ABIS, for distribution through a download page */ variantFilter { variant -> def names = variant.flavors*.name def buildTypeName = variant.buildType.name // flavorDimensions "branding" -> 0, "implementation" -> 1, "abi" -> 2 def supportsSplitApk = variant.flavors[0].splitApk def branding = variant.flavors[0].name // To check for a certain build type, use variant.buildType.name == "" if (((buildTypeName.contains("debug") && !(names.contains("fatweb") || names.contains("fat"))) || (names.contains("fatweb") && buildTypeName.contains("beta")) || (!supportsSplitApk && !names.contains("fat"))) ) { // Gradle ignores any variants that satisfy the conditions above. setIgnore(true) } } lint { abortOnError false } } dependencies { testImplementation libs.junit //outdated mockito-core version due to powermock dependency testImplementation libs.mockito.core testImplementation(libs.powermock.api.mockito2) // { exclude group: 'junit' exclude group: 'org.mockito' } testImplementation libs.robolectric testImplementation libs.conscrypt.openjdk.uber testImplementation libs.powermock.module.junit4 testImplementation libs.powermock.core testImplementation libs.powermock.module.junit4.rule testImplementation libs.junit.dataprovider androidTestImplementation libs.mockito.core.v390 androidTestImplementation libs.androidx.espresso.core androidTestImplementation libs.androidx.espresso.contrib androidTestImplementation libs.androidx.espresso.intents androidTestImplementation libs.androidx.uiautomator androidTestImplementation libs.screengrab testImplementation libs.screengrab testImplementation libs.json androidTestImplementation libs.androidx.junit.v121 debugImplementation libs.leakcanary.android.core debugImplementation libs.androidx.fragment.testing debugImplementation libs.androidx.core implementation libs.gson implementation libs.okhttp implementation libs.okhttp.dnsoverhttps implementation libs.conscrypt.android implementation libs.bcprov.jdk15on implementation libs.androidx.security.crypto implementation libs.androidx.legacy.support.core.utils implementation libs.androidx.annotation implementation libs.androidx.legacy.support.v4 implementation libs.core implementation libs.androidx.appcompat.v170 implementation libs.material implementation libs.androidx.fragment implementation libs.androidx.constraintlayout implementation libs.androidx.multidex implementation libs.androidx.localbroadcast implementation libs.androidx.lifecycle.process implementation libs.circleimageview // swagger dependencies implementation libs.swagger.annotations implementation libs.gson.fire implementation libs.threetenbp // Start:: QR Code Scanner normalImplementation libs.zxing.android.embedded // End:: QR Code Scanner //implementation 'info.guardianproject:tor-android:0.4.5.7' //implementation 'info.guardianproject:jtorctl:0.4.5.7' implementation project(path: ':tor-android:tor-android-binary') fatwebImplementation project(path: ':lib-bitmask-core-web') fatImplementation project(path: ':lib-bitmask-core') x86Implementation project(path: ':lib-bitmask-core-x86') x86_64Implementation project(path: ':lib-bitmask-core-x86_64') armv7Implementation project(path: ':lib-bitmask-core-armv7') arm64Implementation project(path: ':lib-bitmask-core-arm64') } android.applicationVariants.all { variant -> // configure app name and apk file name for different build variants def flavors = variant.productFlavors // flavorDimensions "branding" -> 0, "implementation" -> 1, "abi" -> 2 def branding = flavors[0] def trimmedAppName = branding.appName.replaceAll(' ', '') def abiDimension = flavors[2] def abiFilter = abiDimension.abiFilter if (abiFilter.length() > 0) { abiFilter = "_"+abiFilter } def buildType = variant.buildType def tag = getTag() if (tag.length() > 0) { tag = "_"+tag } variant.resValue "string", "app_name", "\"${branding.appName}${buildType.appSuffix}\"" variant.outputs.all { output -> output.outputFileName = "${trimmedAppName}${abiFilter}_${buildType.name}${tag}.apk" } // reconfigure version codes for split builds variant.outputs.each { output -> // if not a fat build if (abiDimension.abiVersionCode > 0) { output.versionCodeOverride = android.defaultConfig.versionCode + abiDimension.abiVersionCode } } // remove unrelated abi specific assets variant.mergeAssetsProvider.get().doLast { // if not a fat build if (abiDimension.abiVersionCode > 0) { def filesToDelete = fileTree(dir: variant.mergeAssets.outputDir, excludes: ["*pie_openvpn.${abiDimension.abiFilter}", 'urls/', '*.url', '*.json', '*.pem', 'fronts']) delete(filesToDelete) } } } subprojects { afterEvaluate {project -> if (project.hasProperty("android")) { android { compileSdkVersion 35 } } } } task updateIcsOpenVpn( type: Exec ) { commandLine 'git', 'submodule', 'sync' commandLine 'git', 'submodule', 'update', '--init', '--recursive' } def getTag() { String commit = "git log --pretty=format:'%h' -n 1".execute().text.trim().replaceAll("'", "") return ("git describe --tags --exact-match "+ commit).execute().text.trim() } task cleanNative( type: Delete ) { def shouldClean = getCurrentFlavorForBetaOrRelease() == "production" println "cleanNative: " + shouldClean if (shouldClean) { def dirName = "obj" file( dirName ).list().each{ f -> delete "${dirName}/${f}" } } } task updateSdkLicences ( type: Exec ) { println "say yes to licenses" commandLine 'sh', 'yes', '|', 'sdkmanager', '--licenses' } def getCurrentFlavorForBetaOrRelease() { Gradle gradle = getGradle() String tskReqStr = gradle.getStartParameter().getTaskRequests().toString() Pattern pattern; if( tskReqStr.contains( "assemble" ) ) pattern = Pattern.compile("assemble(\\w+)(Beta|Release)") else pattern = Pattern.compile("generate(\\w+)(Beta|Release)") Matcher matcher = pattern.matcher( tskReqStr ) if( matcher.find() ) return matcher.group(1).toLowerCase() else { return ""; } }