import com.github.spotbugs.SpotBugsTask import java.nio.file.Files apply plugin: "com.android.application" apply plugin: "com.github.spotbugs" apply plugin: "pmd" apply plugin: 'com.google.protobuf' def ABORT_ON_CHECK_FAILURE = true tasks.withType(Test) { systemProperty "MiFirmwareDir", System.getProperty("MiFirmwareDir", null) systemProperty "logback.configurationFile", System.getProperty("user.dir", null) + "/app/src/main/assets/logback.xml" systemProperty "GB_LOGFILES_DIR", Files.createTempDirectory("gblog").toString() } def getVersionCode = { -> try { def stdout = new ByteArrayOutputStream() exec { commandLine 'git', 'rev-list', 'HEAD', '--count' standardOutput = stdout } return Integer.valueOf(stdout.toString().trim()) } catch (ignored) { return null } } def buildGitChangelog = { def stdout = new ByteArrayOutputStream() exec { commandLine 'git', 'log', '--pretty=format:%h %s' standardOutput = stdout } def commitVersionCode = getVersionCode() def includedCommits = 0 def changelogNode = new Node(null, 'changelog') stdout.toString().trim().eachLine { line -> if (includedCommits > 100) { return true; } def (commitHash, commitMessage) = line.split(" ", 2) if (commitMessage.contains("Translated using Weblate")) { return true; } def releaseNode = new Node(changelogNode, 'release', [version: commitHash, versioncode: commitVersionCode--]) def _ = new Node(releaseNode, 'change', [:], commitMessage) includedCommits++ } def changelogFile = new File("${project.rootDir}/app/build/generated/res/changelog/xml/changelog_git.xml") changelogFile.getParentFile().mkdirs() changelogFile.write(groovy.xml.XmlUtil.serialize(changelogNode)) } def getGitHashShort = { -> try { def stdout = new ByteArrayOutputStream() exec { commandLine 'git', 'rev-parse', '--short', 'HEAD' standardOutput = stdout } return stdout.toString().trim() } catch (ignored){ return null } } android { compileOptions { // for Android 5+ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } compileSdkVersion 33 buildToolsVersion "33.0.0" defaultConfig { applicationId "nodomain.freeyourgadget.gadgetbridge" minSdkVersion 21 targetSdkVersion 33 // Note: always bump BOTH versionCode and versionName! versionName "0.77.0" versionCode 227 vectorDrawables.useSupportLibrary = true buildConfigField "String", "GIT_HASH_SHORT", "\"${getGitHashShort()}\"" buildConfigField "boolean", "INTERNET_ACCESS", "false" resValue "string", "pebble_content_provider", "com.getpebble.android.provider" resValue "string", "app_name", "@string/application_name_generic" resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_generic" resValue "string", "about_activity_title", "@string/about_activity_title_generic" resValue "string", "about_description", "@string/about_description_generic" resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_generic" } signingConfigs { nightly { if (System.getProperty("nightly_store_file") != null) { storeFile file(System.getProperty("nightly_store_file")) storePassword System.getProperty("nightly_store_password") keyAlias System.getProperty("nightly_key_alias") keyPassword System.getProperty("nightly_key_password") } } } flavorDimensions "device_type" productFlavors { main { // Ensure that when starting from scratch, 'main' is selected, not 'banglejs' getIsDefault().set(true) // the default build product flavor dimension "device_type" //applicationIdSuffix "" //versionNameSuffix "" } banglejs { dimension "device_type" applicationId "com.espruino.gadgetbridge" applicationIdSuffix ".banglejs" versionNameSuffix "-banglejs" buildConfigField "boolean", "INTERNET_ACCESS", "true" // Disable pebble provider to allow Bangle.js Gadgetbridge to coexist with Gadgetbridge resValue "string", "pebble_content_provider", "com.getpebble.android.nopebble.bangle.provider" resValue "string", "app_name", "@string/application_name_banglejs_main" resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_banglejs_main" resValue "string", "about_activity_title", "@string/about_activity_title_banglejs_main" resValue "string", "about_description", "@string/about_description_banglejs_main" resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_banglejs_main" targetSdkVersion 33 // Note: app/src/banglejs/AndroidManifest.xml contains some extra permissions } } sourceSets { main { res.srcDirs += "build/generated/res/changelog" } } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" resValue "string", "pebble_content_provider", "com.getpebble.android.provider" } nightly { applicationIdSuffix ".nightly" versionNameSuffix "-${getGitHashShort}" minifyEnabled true proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" resValue "string", "pebble_content_provider", "com.getpebble.android.provider" resValue "string", "app_name", "@string/application_name_main_nightly" resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_main_nightly" resValue "string", "about_activity_title", "@string/about_activity_title_main_nightly" resValue "string", "about_description", "@string/about_description_main_nightly" resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nightly" debuggable true if (System.getProperty("nightly_store_file") != null) { signingConfig signingConfigs.nightly //this was conflicting with regular debug type (it should not), so it is only available for CI builds: productFlavors.main.resValue "string", "pebble_content_provider", "com.getpebble.android.provider" productFlavors.main.resValue "string", "app_name", "@string/application_name_main_nightly" productFlavors.main.resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_main_nightly" productFlavors.main.resValue "string", "about_activity_title", "@string/about_activity_title_main_nightly" productFlavors.main.resValue "string", "about_description", "@string/about_description_main_nightly" productFlavors.main.resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nightly" //keep the pebble provider for people who want this. In case of need we can create Banglejs Nopebble productFlavors.banglejs.resValue "string", "pebble_content_provider", "com.getpebble.android.provider" productFlavors.banglejs.resValue "string", "app_name", "@string/application_name_banglejs_nightly" productFlavors.banglejs.resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_banglejs_nightly" productFlavors.banglejs.resValue "string", "about_activity_title", "@string/about_activity_title_banglejs_nightly" productFlavors.banglejs.resValue "string", "about_description", "@string/about_description_banglejs_nightly" productFlavors.banglejs.resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_banglejs_nightly" } else { signingConfig signingConfigs.debug } } nopebble { applicationIdSuffix ".nightly_nopebble" versionNameSuffix "-${getGitHashShort}" minifyEnabled true if (System.getProperty("nightly_store_file") != null) { signingConfig signingConfigs.nightly } else { signingConfig signingConfigs.debug } proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" resValue "string", "pebble_content_provider", "com.getpebble.android.nopebble.provider" resValue "string", "app_name", "@string/application_name_main_nopebble" resValue "string", "title_activity_controlcenter", "@string/title_activity_controlcenter_main_nopebble" resValue "string", "about_activity_title", "@string/about_activity_title_main_nopebble" resValue "string", "about_description", "@string/about_description_main_nopebble" resValue "string", "gadgetbridge_running", "@string/gadgetbridge_running_main_nopebble" debuggable true } applicationVariants.all { variant -> variant.resValue "string", "applicationId", variant.applicationId buildGitChangelog() if (variant.buildType.name == 'nightly' || variant.buildType.name == 'nopebble') { variant.outputs.all { setVersionCodeOverride(getVersionCode()) //setVersionNameOverride(getGitHashShort()) setVersionNameOverride(variant.versionName) outputFileName = "${applicationId}_${variant.versionName}.apk" } } } } lintOptions { abortOnError ABORT_ON_CHECK_FAILURE lintConfig file("${project.rootDir}/app/src/main/lint.xml") // If true, generate an HTML report (with issue explanations, sourcecode, etc) htmlReport true // Optional path to report (default will be lint-results.html in the builddir) htmlOutput file("$project.buildDir/reports/lint/lint.html") // Ignore checks present in the snapshot baseline file("lint-baseline.xml") } testOptions { unitTests { returnDefaultValues = true includeAndroidResources = true } } } pmd { toolVersion = "5.5.5" } dependencies { // testImplementation "ch.qos.logback:logback-classic:1.1.3" // testImplementation "ch.qos.logback:logback-core:1.1.3" implementation 'com.android.support.constraint:constraint-layout:2.0.4' testImplementation "junit:junit:4.13.2" testImplementation "org.mockito:mockito-core:1.10.19" testImplementation "org.robolectric:robolectric:4.8.2" testImplementation "org.hamcrest:hamcrest-library:1.3" testImplementation "com.google.code.gson:gson:2.8.9" implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "androidx.appcompat:appcompat:1.6.1" implementation "androidx.preference:preference:1.2.1" implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.recyclerview:recyclerview:1.3.1" implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.palette:palette:1.0.0" implementation "androidx.activity:activity:1.7.2" implementation "androidx.fragment:fragment:1.6.1" implementation "com.google.android.material:material:1.9.0" implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation "com.google.code.gson:gson:2.8.9" implementation "no.nordicsemi.android:dfu:1.12.0" implementation("com.github.tony19:logback-android:2.0.0") { exclude group: "com.google.android", module: "android" } implementation "org.slf4j:slf4j-api:1.7.36" implementation "com.github.PhilJay:MPAndroidChart:v3.1.0" implementation "com.github.pfichtner:durationformatter:0.1.1" implementation "de.cketti.library.changelog:ckchangelog:1.2.2" implementation "net.e175.klaus:solarpositioning:0.0.9" implementation "co.nstant.in:cbor:0.9" // use pristine greendao instead of our custom version, since our custom jitpack-packaged // version contains way too much and our custom patches are in the generator only. implementation "org.greenrobot:greendao:2.2.1" implementation "org.apache.commons:commons-lang3:3.7" implementation "org.cyanogenmod:platform.sdk:6.0" implementation 'com.jaredrummler:colorpicker:1.0.2' // implementation project(":DaoCore") implementation 'com.github.wax911:android-emojify:0.1.7' implementation 'com.google.protobuf:protobuf-javalite:3.21.7' implementation 'com.android.volley:volley:1.2.1' // Bouncy Castle is included directly in GB, to avoid pulling the entire dependency // It's included in the org.bouncycastle.shaded package, to fix conflicts with // roboelectric //implementation 'org.bouncycastle:bcpkix-jdk18on:1.76' //implementation 'org.bouncycastle:bcprov-jdk18on:1.76' // NON-FOSS dependencies // implementation('androidx.core:core-google-shortcuts:1.0.1') { // exclude group:'com.google.android.gms' // exclude group:'com.google.firebase' // } // JSR-310 timezones backport for Android, since we're still API 21 implementation 'com.jakewharton.threetenabp:threetenabp:1.4.0' testImplementation 'org.threeten:threetenbp:1.6.0' // Android SDK bundles org.json, but we need an actual implementation to replace the stubs in tests testImplementation 'org.json:json:20180813' // Fix Duplicate class build error implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0")) } preBuild.dependsOn(":GBDaoGenerator:genSources") gradle.beforeProject { preBuild.dependsOn(":GBDaoGenerator:genSources") } check.dependsOn "spotbugsMain", "pmd", "lint" task pmd(type: Pmd) { ruleSetFiles = files("${project.rootDir}/config/pmd/pmd-ruleset.xml") ignoreFailures = !ABORT_ON_CHECK_FAILURE ruleSets = [ "java-android", "java-basic", "java-braces", "java-clone", "java-codesize", "java-controversial", "java-coupling", "java-design", "java-empty", "java-finalizers", "java-imports", "java-junit", "java-optimizations", "java-strictexception", "java-strings", "java-sunsecure", "java-typeresolution", "java-unnecessary", "java-unusedcode" ] source "src" include "**/*.java" exclude "**/gen/**" reports { xml.enabled = false html.enabled = true xml { destination file("$project.buildDir/reports/pmd/pmd.xml") } html { destination file("$project.buildDir/reports/pmd/pmd.html") } } } sourceSets { main { main.java.srcDirs += "${protobuf.generatedFilesBaseDir}" main.java.srcDirs += "build/generated/source/buildConfig" } } spotbugs { toolVersion = "3.1.12" ignoreFailures = !ABORT_ON_CHECK_FAILURE effort = "default" reportLevel = "medium" } tasks.withType(SpotBugsTask) { source = fileTree('src/main/java') classes = files("${project.rootDir}/app/build/intermediates/javac/debug/classes") excludeFilter = new File("${project.rootDir}/config/findbugs/findbugs-filter.xml") reports { xml.enabled = false html.enabled = true xml { destination file ("$project.buildDir/reports/spotbugs/spotbugs-output.xml") } html { destination file ("$project.buildDir/reports/spotbugs/spotbugs-output.html") } } } task cleanGenerated(type: Delete) { delete fileTree('src/main/java/nodomain/freeyourgadget/gadgetbridge/entities') { include '**/*.java' exclude '**/Abstract*.java' } } tasks.clean.dependsOn(tasks.cleanGenerated) protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.21.7' } generateProtoTasks { all().each { task -> task.builtins { java { option 'lite' } } } } }