Project Setup

Continuous Integration

Tests should run on Drone as part of the build process.

Steps to run on Drone:

build:
    image: registry.vokal.io/android
    volumes:
        - /home/ubuntu/android_build/android-sdk:/usr/local/android-sdk
        - /home/ubuntu/.android:/root/.android
    commands:
        - ./gradlew build deviceCheck
        - curl -sSL https://secure.vokal.io/new | sh
publish:
    script:
        image: vokal/drone-android-publish
        apiToken: $$hockeyApiToken
        path: app/build/outputs/apk/<name of debug>.apk

Artifactory Setup

artifactory_user=[insert user provided]
artifactory_passord=[insert password provided]
...
    jcenter()
    maven {
        url 'http://artifactory.vokal.io/artifactory/<repository>'
        credentials {
            username = "${artifactory_user}"
            password = "${artifactory_password}"
        }
    }

Ktlint

We automate our code style review using Ktlint. We ensure our code meets the standard style guidelines
by running it specially before our final commit prior to making a Git pull request. Ktlint notifies if our code passes or fails and where we need to amend for changes. Once we update our code and it passes then we can commit and make a pull request.

How to install Ktlint using Kotlin Gradle plugin.

repositories {
    jcenter()
}

val ktlint by configurations.creating

dependencies {
    ktlint("com.pinterest:ktlint:$ktLintVersion")
}

tasks.create<JavaExec>("ktlint") {
    group = "verification"
    val outputDir = "${project.buildDir}/reports/ktlint/"
    val outputFile = "${outputDir}ktlint-checkstyle-report.xml"
    val inputFiles = project.fileTree(
        mapOf(
            "dir" to "src",
            "include" to "**/*.kt"
        )
    )

    inputs.files(inputFiles)
    outputs.files(outputFile)
    description = "Check Kotlin code style."
    main = "com.pinterest.ktlint.Main"
    classpath = configurations.getByName("ktlint")
    args = listOf(
        "--reporter=plain",
        "--reporter=checkstyle,output=${outputFile}",
        "src/**/*.kt",
        "--verbose"
    )
    // to generate report in checkstyle format prepend following args:
    // "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
    // see https://github.com/pinterest/ktlint#usage for more
}
tasks.getByName("check").dependsOn("ktlint")

How to install Ktlint using Gradle

repositories {
    jcenter()
}

configurations {
    ktlint
}

dependencies {
    ktlint "com.pinterest:ktlint:$ktLintVersion"
}

task ktlint(type: JavaExec, group: "verification") {
    def outputDir = "${project.buildDir}/reports/ktlint/"
    def outputFile = "${outputDir}ktlint-checkstyle-report.xml"
    def inputFiles = project.fileTree(dir: "src", include: "**/*.kt")

    inputs.files(inputFiles)
    outputs.files(outputDir)
    description = "Check Kotlin code style."
    main = "com.pinterest.ktlint.Main"
    classpath = configurations.ktlint
    args = [
        "--reporter=plain",
        "--reporter=checkstyle,output=${outputFile}",
        "src/**/*.kt",
        "--verbose"
    ]
    // to generate report in checkstyle format prepend following args:
    // "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
    // see https://github.com/pinterest/ktlint#usage for more
}
check.dependsOn ktlint

Also attach .editorconfig at the root of your project

[*.{kt,kts}]
# possible values: number (e.g. 2), "unset" (makes ktlint ignore indentation completely)
indent_size=4
# possible values: number (e.g. 2), "unset"
continuation_indent_size=4
# true (recommended) / false
insert_final_newline=true
# possible values: number (e.g. 120) (package name, imports & comments are ignored), "off"
# it's automatically set to 100 on `ktlint --android ...` (per Android Kotlin Style Guide)
max_line_length=100

Gradle

Our Gradle setup has been written in Groovy and new projects will be written in Kotlin baserd on Kotlin DSL

Version Code Automation

Each time an Android project is committed in Git, its versionCode can be updated based on generateVersionCode()

Kotlin:

android {
    defaultConfig {
        versionCode = generateVersionCode()
    }
}
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.Reader

fun generateVersionCode(): Int {
    val result = "rev-list HEAD --count".git()

    if (result.isEmpty()) {
        throw RuntimeException("Could not generate versioncode. See if you have git installed.")
    }

    return result.toInt()
}

fun String.git(): String {
    var result = "git $this".execute()

    if (result.isEmpty()) {
        // windows
        result = "PowerShell -Command git $this".execute()
    }

    return result
}

fun String.execute(): String {
    return try {
        val process = Runtime.getRuntime().exec(this)
        val inputStream = process.inputStream
        val inputStreamReader: Reader = InputStreamReader(inputStream, "utf-8")
        val bufferedReader = BufferedReader(inputStreamReader)
        bufferedReader.readLine().trim()
    } catch (e: Exception) {
        ""
    }
}

Groovy:

android {
    defaultConfig {
        versionCode generateVersionCode()
    }
}
static def generateVersionCode() {
    // unix
    def result = "git rev-list HEAD --count".execute().text.trim()
    if(result.empty) {
        // windows
        result = "PowerShell -Command git rev-list HEAD --count".execute().text.trim()
    }
    if(result.empty) {
        throw new RuntimeException("Could not generate versioncode. See if you have git installed")
    }
    return result.toInteger()
}