├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ └── ic_launcher_round.webp │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── values-night │ │ │ └── themes.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ └── template │ │ │ ├── theme │ │ │ ├── Color.kt │ │ │ ├── Shape.kt │ │ │ ├── Type.kt │ │ │ └── Theme.kt │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle.kts ├── git-hooks ├── pre-push.sh └── pre-commit.sh ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── Gemfile ├── .gitignore ├── .editorconfig ├── .github ├── pull_request_template.md └── workflows │ ├── android_ui_tests.yml │ ├── android_build.yml │ └── template_change_test.yml ├── settings.gradle.kts ├── documentation ├── GitHubActions.md ├── GitHooks.md └── StaticAnalysis.md ├── Dangerfile ├── gradle.properties ├── gradlew.bat ├── README.md ├── gradlew └── config └── detekt └── detekt.yml /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /git-hooks/pre-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo "Running static analysis." 4 | 5 | ./gradlew lintKotlin 6 | ./gradlew detektAll -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | gem "danger" -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aslansari/android-app-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | build/ 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{kt,kts}] 4 | max_line_length = 140 5 | indent_size = 4 6 | insert_final_newline = true 7 | ij_kotlin_allow_trailing_comma = true 8 | ij_kotlin_allow_trailing_comma_on_call_site = true 9 | ktlint_disabled_rules = filename 10 | -------------------------------------------------------------------------------- /app/src/main/java/template/theme/Color.kt: -------------------------------------------------------------------------------- 1 | @file:Suppress("MagicNumber") 2 | 3 | package template.theme 4 | 5 | import androidx.compose.ui.graphics.Color 6 | 7 | val Purple200 = Color(0xFFBB86FC) 8 | val Purple500 = Color(0xFF6200EE) 9 | val Teal200 = Color(0xFF03DAC5) 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 10 21:53:17 EDT 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/template/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package template.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material3.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp), 11 | ) 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary 2 | 3 | 4 | 5 | ## How It Was Tested 6 | 7 | 8 | 9 | ## Screenshot/Gif 10 | 11 | 12 | 13 |
14 | 15 | Screenshot Name 16 | 17 | 18 | 19 |
-------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | repositories { 4 | google() 5 | mavenCentral() 6 | gradlePluginPortal() 7 | maven(url = "https://plugins.gradle.org/m2/") 8 | } 9 | } 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | } 17 | rootProject.name = "template" 18 | include(":app") 19 | -------------------------------------------------------------------------------- /app/src/main/java/template/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package template.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyMedium = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | ), 16 | ) 17 | -------------------------------------------------------------------------------- /git-hooks/pre-commit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ######## KTLINT-GRADLE HOOK START ######## 4 | 5 | CHANGED_FILES="$(git --no-pager diff --name-status --no-color --cached | awk '$1 != "D" && $2 ~ /\.kts|\.kt/ { print $2}')" 6 | 7 | if [ -z "$CHANGED_FILES" ]; then 8 | echo "No Kotlin staged files." 9 | exit 0 10 | fi; 11 | 12 | echo "Running ktlint over these files:" 13 | echo "$CHANGED_FILES" 14 | 15 | ./gradlew --quiet formatKotlin -PinternalKtlintGitFilter="$CHANGED_FILES" 16 | 17 | echo "Completed ktlint run." 18 | 19 | echo "$CHANGED_FILES" | while read -r file; do 20 | if [ -f $file ]; then 21 | git add $file 22 | fi 23 | done 24 | 25 | echo "Completed ktlint hook." 26 | ######## KTLINT-GRADLE HOOK END ######## -------------------------------------------------------------------------------- /documentation/GitHubActions.md: -------------------------------------------------------------------------------- 1 | # GitHub Actions 2 | 3 | This project has [GitHub Actions](https://github.com/features/actions) workflows to validate our code for us automatically. The project currently uses two workflows. 4 | 5 | ## Android Build 6 | 7 | The [Android Build](/.github/workflows/android_build.yml) workflow automates the core checks for the repository: compile, unit tests, lint checks. This is set to run on every push. 8 | 9 | ## Android UI Tests 10 | 11 | The [Android UI Tests](/.github/workflows/android_ui_tests.yml) is a separate workflow that is set to only run on pull request. This is because UI tests are slow and take up a lot of resources, so we only want to validate them when we're ready to merge changes into our base branch. 12 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle.kts. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /.github/workflows/android_ui_tests.yml: -------------------------------------------------------------------------------- 1 | name: Android UI Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | 9 | jobs: 10 | android-test: 11 | runs-on: macos-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Set Up JDK 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'zulu' 20 | java-version: 17 21 | 22 | - name: Setup Gradle 23 | uses: gradle/gradle-build-action@v2 24 | with: 25 | # Only write to the cache for builds on the 'development' branch 26 | cache-read-only: ${{ github.ref != 'refs/heads/development' }} 27 | 28 | - name: Run Tests 29 | uses: reactivecircus/android-emulator-runner@v2 30 | with: 31 | api-level: 29 32 | script: ./gradlew app:connectedCheck -------------------------------------------------------------------------------- /.github/workflows/android_build.yml: -------------------------------------------------------------------------------- 1 | name: Android Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | - main 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v3 15 | 16 | - name: Set Up JDK 17 | uses: actions/setup-java@v3 18 | with: 19 | distribution: 'zulu' 20 | java-version: 17 21 | 22 | - name: Setup Gradle 23 | uses: gradle/gradle-build-action@v2 24 | with: 25 | # Only write to the cache for builds on the 'development' branch 26 | cache-read-only: ${{ github.ref != 'refs/heads/development' }} 27 | 28 | - name: Build Project 29 | run: ./gradlew assemble 30 | 31 | - name: Run Tests 32 | run: ./gradlew test 33 | 34 | - name: Lint Checks 35 | run: ./gradlew detektAll lintKotlin lint -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /documentation/GitHooks.md: -------------------------------------------------------------------------------- 1 | # Git Hooks 2 | 3 | This project has some Git hooks included inside the [git-hooks](/git-hooks) folder. These hooks can be installed automatically via the Gradle commands `./gradlew copyGitHooks` and `./gradlew installGitHooks`. You can find these commands in [this Gradle file](/buildscripts/githooks.gradle), but it's also good to know that the hooks are installed automatically just by running a `clean` task. Thanks to [Sebastiano's blog post](https://blog.sebastiano.dev/ooga-chaka-git-hooks-to-enforce-code-quality/) for that inspiration. 4 | 5 | ## Pre-Commit 6 | 7 | There is a [pre-commit](/git-hooks/pre-commit.sh) hook that will automatically run Ktlint formatting over any modified Kotlin files. This way you can just commit your code and trust that formatting happens behind the scenes, without having to consciously worry about it. 8 | 9 | ## Pre-Push 10 | 11 | There is a [pre-push](/git-hooks/pre-push.sh) hook that will run static analysis checks before any code is pushed up to the remote repository. This way, if you have any code smells, you can be alerted right away without waiting for the GitHub Actions to fail. -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | message "Thanks @#{github.pr_author}!" 2 | 3 | if github.pr_body.length == 0 4 | fail "Please provide a summary in the Pull Request description." 5 | end 6 | 7 | if git.lines_of_code > 500 8 | warn "Please consider breaking up this pull request." 9 | end 10 | 11 | if github.pr_labels.empty? 12 | warn "Please add labels to this PR." 13 | end 14 | 15 | if git.deletions > git.insertions 16 | message "🎉 Code Cleanup!" 17 | end 18 | 19 | # Notify of outdated dependencies 20 | dependencyUpdatesHeader = "The following dependencies have later milestone versions:" 21 | dependencyReportsFile = "build/dependencyUpdates/report.txt" 22 | 23 | # Due to the formatting of this output file, we first check if there are any dependencies 24 | # by looking for a `->` arrow, then we check for the relevant headers. We do this to handle a case 25 | # where there are no app dependencies but there are Gradle dependencies. 26 | hasDependencyUpdatesHeader = File.readlines(dependencyReportsFile).grep(/#{dependencyUpdatesHeader}/).any? 27 | 28 | if hasDependencyUpdatesHeader 29 | file = File.open(dependencyReportsFile, "rb").read 30 | index = file.index(dependencyUpdatesHeader) 31 | message file.slice(index..-1) 32 | end -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx6g -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 |