├── library-compose ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── ncorti │ │ │ └── kotlin │ │ │ └── template │ │ │ └── app │ │ │ ├── ComposeActivity.kt │ │ │ └── ui │ │ │ └── components │ │ │ └── Factorial.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── ncorti │ │ └── kotlin │ │ └── template │ │ └── app │ │ └── FactorialTest.kt ├── proguard-rules.pro └── build.gradle.kts ├── library-android ├── consumer-rules.pro ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── ncorti │ │ │ └── kotlin │ │ │ └── template │ │ │ └── library │ │ │ └── android │ │ │ └── ToastUtil.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── ncorti │ │ └── kotlin │ │ └── template │ │ └── library │ │ └── android │ │ └── ToastUtilTest.kt ├── proguard-rules.pro └── build.gradle.kts ├── .idea ├── icon.png ├── icon_dark.png └── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── renovate.json ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── app ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── ncorti │ │ │ └── kotlin │ │ │ └── template │ │ │ └── app │ │ │ └── MainActivity.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── ncorti │ │ └── kotlin │ │ └── template │ │ └── app │ │ └── MainActivityTest.kt ├── proguard-rules.pro └── build.gradle.kts ├── library-kotlin ├── build.gradle.kts └── src │ ├── main │ └── java │ │ └── com │ │ └── ncorti │ │ └── kotlin │ │ └── template │ │ └── library │ │ └── FactorialCalculator.kt │ └── test │ └── java │ └── com │ └── ncorti │ └── kotlin │ └── template │ └── library │ └── FactorialCalculatorTest.kt ├── settings.gradle.kts ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── template-cleanup │ └── README.md ├── workflows │ ├── pre-merge.yaml │ ├── cleanup.yaml │ ├── publish-release.yaml │ └── publish-snapshot.yaml └── PULL_REQUEST_TEMPLATE ├── LICENSE ├── TROUBLESHOOTING.md ├── gradle.properties ├── gradlew.bat ├── .gitignore ├── README.md ├── gradlew └── config └── detekt └── detekt.yml /library-compose/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /library-android/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /library-compose/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/.idea/icon.png -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base", 4 | ":automergeMinor" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.idea/icon_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/.idea/icon_dark.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dext7r/kotlin-android-template/main/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 16dp 5 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /library-compose/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /library-android/src/main/java/com/ncorti/kotlin/template/library/android/ToastUtil.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.library.android 2 | 3 | import android.content.Context 4 | import android.widget.Toast 5 | 6 | object ToastUtil { 7 | 8 | fun showToast(context: Context, message: String): Toast = 9 | Toast.makeText(context, message, Toast.LENGTH_SHORT).also { 10 | it.show() 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /library-kotlin/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("java-library") 3 | kotlin("jvm") 4 | id("maven-publish") 5 | publish 6 | } 7 | 8 | dependencies { 9 | testImplementation(libs.junit) 10 | } 11 | 12 | java { 13 | sourceCompatibility = JavaVersion.VERSION_17 14 | targetCompatibility = JavaVersion.VERSION_17 15 | withSourcesJar() 16 | withJavadocJar() 17 | } 18 | 19 | kotlin { 20 | jvmToolchain(17) 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | rootProject.name = ("kotlin-android-template") 17 | 18 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 19 | 20 | include( 21 | "app", 22 | "library-android", 23 | "library-compose", 24 | "library-kotlin" 25 | ) 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | --- 5 | 6 | ## ⚠️ Is your feature request related to a problem? Please describe 7 | 8 | 9 | ## 💡 Describe the solution you'd like 10 | 11 | 12 | ## 🤚 Do you want to develop this feature yourself? 13 | 14 | - [ ] Yes 15 | - [ ] No 16 | -------------------------------------------------------------------------------- /library-kotlin/src/main/java/com/ncorti/kotlin/template/library/FactorialCalculator.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.library 2 | 3 | object FactorialCalculator { 4 | private const val MAX_FACTORIAL_64BIT = 20 5 | 6 | tailrec fun computeFactorial(input: Long, temp: Long = 1L): Long = 7 | when { 8 | input < 0 -> error("Factorial is not defined for negative numbers") 9 | input > MAX_FACTORIAL_64BIT -> error("Only a factorial up to 20 can fit in a 64-bit Long") 10 | input == 0L -> temp 11 | else -> computeFactorial(input - 1, temp * input) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.github/template-cleanup/README.md: -------------------------------------------------------------------------------- 1 | # %NAME% 2 | 3 | ![Build](https://github.com/%REPOSITORY%/workflows/Pre%20Merge%20Checks/badge.svg) 4 | 5 | This is your new Kotlin Android Project! Happy hacking! 6 | 7 | ## Template ToDo list 👣 8 | 9 | - [x] Create a new template project. 10 | - [ ] Choose a [LICENSE](https://github.com/%REPOSITORY%/community/license/new?branch=main). 11 | - [ ] Set your `ORG_GRADLE_PROJECT_NEXUS_USERNAME`, `ORG_GRADLE_PROJECT_NEXUS_PASSWORD`, `ORG_GRADLE_PROJECT_SIGNING_KEY` and `ORG_GRADLE_PROJECT_SIGNING_PWD` secrets in [Settings](https://github.com/%REPOSITORY%/settings/secrets/actions). 12 | - [ ] Code some cool apps and libraries 🚀. 13 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin Android Template 3 | This is just a template 4 | You can compute a factorial using the library-kotlin module. The result will be also shown in a notification using the library-android module. 5 | Insert a number to compute his factorial 6 | Compute 7 | The Result is: %s 8 | Please Enter a Number 9 | In Compose 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/pre-merge.yaml: -------------------------------------------------------------------------------- 1 | name: Pre Merge Checks 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - "*" 10 | 11 | jobs: 12 | gradle: 13 | runs-on: ubuntu-latest 14 | if: ${{ !contains(github.event.head_commit.message, 'ci skip') }} 15 | steps: 16 | - name: Checkout Repo 17 | uses: actions/checkout@v4 18 | 19 | - name: Setup Java 20 | uses: actions/setup-java@v4 21 | with: 22 | distribution: "zulu" 23 | java-version: "17" 24 | 25 | - name: Setup Gradle 26 | uses: gradle/actions/setup-gradle@v4 27 | 28 | - name: Run Gradle 29 | run: ./gradlew build publishToMavenLocal --continue 30 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | --- 5 | 6 | ## 🐛 Describe the bug 7 | 8 | 9 | ## ⚠️ Current behavior 10 | 11 | 12 | ## ✅ Expected behavior 13 | 14 | 15 | ## 💣 Steps to reproduce 16 | 17 | 18 | ## 📷 Screenshots 19 | 20 | 21 | ## 📱 Tech info 22 | - Device: 23 | - OS: 24 | - Library/App version: 25 | -------------------------------------------------------------------------------- /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 22 | -------------------------------------------------------------------------------- /library-compose/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. 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 -------------------------------------------------------------------------------- /library-android/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.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 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /library-android/src/androidTest/java/com/ncorti/kotlin/template/library/android/ToastUtilTest.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.library.android 2 | 3 | import android.widget.Toast 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | import androidx.test.platform.app.InstrumentationRegistry 6 | import org.junit.Assert.assertEquals 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | 10 | /** 11 | * Instrumented test, which will execute on an Android device. 12 | * 13 | * See [testing documentation](http://d.android.com/tools/testing). 14 | */ 15 | @RunWith(AndroidJUnit4::class) 16 | class ToastUtilTest { 17 | 18 | @Test 19 | fun showCorrectToast() { 20 | val context = InstrumentationRegistry.getInstrumentation().targetContext 21 | 22 | val toast = ToastUtil.showToast(context, "test message") 23 | 24 | assertEquals(Toast.LENGTH_SHORT, toast.duration) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /library-compose/src/androidTest/java/com/ncorti/kotlin/template/app/FactorialTest.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.app 2 | 3 | import androidx.compose.ui.test.assert 4 | import androidx.compose.ui.test.assertIsDisplayed 5 | import androidx.compose.ui.test.hasText 6 | import androidx.compose.ui.test.junit4.createComposeRule 7 | import androidx.compose.ui.test.onNodeWithTag 8 | import androidx.compose.ui.test.onNodeWithText 9 | import androidx.compose.ui.test.performClick 10 | import androidx.compose.ui.test.performTextInput 11 | import com.ncorti.kotlin.template.app.ui.components.Factorial 12 | import org.junit.Rule 13 | import org.junit.Test 14 | 15 | class FactorialTest { 16 | @get:Rule 17 | val composeTestRule = createComposeRule() 18 | 19 | @Test 20 | fun useAppContext() { 21 | composeTestRule.setContent { 22 | Factorial() 23 | } 24 | 25 | composeTestRule.onNodeWithTag("Input").performClick().performTextInput("5") 26 | composeTestRule.onNodeWithText("COMPUTE").performClick() 27 | composeTestRule.onNodeWithTag("FactorialResult") 28 | .assertIsDisplayed() 29 | .assert(hasText("120")) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /library-kotlin/src/test/java/com/ncorti/kotlin/template/library/FactorialCalculatorTest.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.library 2 | 3 | import com.ncorti.kotlin.template.library.FactorialCalculator.computeFactorial 4 | import org.junit.Assert.assertEquals 5 | import org.junit.Assert.assertThrows 6 | import org.junit.Test 7 | import java.lang.Exception 8 | import java.lang.IllegalStateException 9 | 10 | /** 11 | * Example local unit test, which will execute on the development machine (host). 12 | * 13 | * See [testing documentation](http://d.android.com/tools/testing). 14 | */ 15 | class FactorialCalculatorTest { 16 | 17 | @Test 18 | fun computeFactorial_withNegative_raiseException() { 19 | assertThrows(Exception::class.java) { 20 | computeFactorial(-1) 21 | } 22 | } 23 | 24 | @Test 25 | fun computeFactorial_forZero() { 26 | assertEquals(1, computeFactorial(0)) 27 | } 28 | 29 | @Test 30 | fun computeFactorial_forFive() { 31 | assertEquals(120, computeFactorial(5)) 32 | } 33 | 34 | @Test(expected = IllegalStateException::class) 35 | fun computeFactorial_TooLarge() { 36 | computeFactorial(21) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /TROUBLESHOOTING.md: -------------------------------------------------------------------------------- 1 | # agp incompatibility with IntelliJ IDEA 2 | 3 | Opening this template in IDEA might result in an error like this: 4 | 5 | ``` 6 | The project is using an incompatible version (AGP 8.0.0) of the Android Gradle plugin. Latest supported version is AGP 7.4.0 7 | ``` 8 | 9 | This is caused by IDEA support for AGP lagging behind releases of AGP and is tracked by bugs like https://youtrack.jetbrains.com/issue/IDEA-317997. 10 | 11 | To get this working in the mean time, you can downgrade the version of `agp` in `libs.versions.toml` to a supported version. 12 | 13 | # Use correct JVM version 14 | 15 | This template requires you to have the correct JDK version in your path. Check which version you have by running `java --version` in a terminal. This should match the version specified in the Gradle build files e.g.: 16 | 17 | ``` 18 | tasks.withType().configureEach { 19 | compilerOptions { 20 | jvmTarget.set(JvmTarget.JVM_17) 21 | } 22 | } 23 | ``` 24 | 25 | If you use a JVM which is too new, you may see an error like this: 26 | 27 | ``` 28 | FAILURE: Build failed with an exception. 29 | * What went wrong: 30 | Execution failed for task ':library-compose:detekt'. 31 | > Invalid value (20) passed to --jvm-target, must be one of [1.6, 1.8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] 32 | ``` -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ncorti/kotlin/template/app/MainActivityTest.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.app 2 | 3 | import androidx.test.espresso.Espresso.onView 4 | import androidx.test.espresso.action.ViewActions.click 5 | import androidx.test.espresso.action.ViewActions.closeSoftKeyboard 6 | import androidx.test.espresso.action.ViewActions.typeText 7 | import androidx.test.espresso.assertion.ViewAssertions.matches 8 | import androidx.test.espresso.matcher.ViewMatchers.isDisplayed 9 | import androidx.test.espresso.matcher.ViewMatchers.withId 10 | import androidx.test.espresso.matcher.ViewMatchers.withText 11 | import androidx.test.ext.junit.rules.activityScenarioRule 12 | import androidx.test.ext.junit.runners.AndroidJUnit4 13 | import org.junit.Rule 14 | import org.junit.Test 15 | import org.junit.runner.RunWith 16 | 17 | @RunWith(AndroidJUnit4::class) 18 | class MainActivityTest { 19 | 20 | @get:Rule 21 | val rule = activityScenarioRule() 22 | 23 | @Test 24 | fun typeANumber_resultIsDisplayed() { 25 | onView(withId(R.id.edit_text_factorial)).perform(typeText("1"), closeSoftKeyboard()) 26 | onView(withId(R.id.button_compute)).perform(click()) 27 | 28 | onView(withId(R.id.text_result)).check(matches(isDisplayed())) 29 | onView(withId(R.id.text_result)).check(matches(withText("1"))) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 🚀 Description 4 | 5 | 6 | ## 📄 Motivation and Context 7 | 8 | 9 | 10 | ## 🧪 How Has This Been Tested? 11 | 12 | 13 | 14 | 15 | ## 📷 Screenshots (if appropriate) 16 | 17 | 18 | ## 📦 Types of changes 19 | 20 | - [ ] Bug fix (non-breaking change which fixes an issue) 21 | - [ ] New feature (non-breaking change which adds functionality) 22 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 23 | 24 | ## ✅ Checklist 25 | 26 | 27 | - [ ] My code follows the code style of this project. 28 | - [ ] My change requires a change to the documentation. 29 | - [ ] I have updated the documentation accordingly. -------------------------------------------------------------------------------- /.github/workflows/cleanup.yaml: -------------------------------------------------------------------------------- 1 | # GitHub Actions Workflow responsible for cleaning up the template repository from 2 | # the template-specific files and configurations. This workflow is supposed to be triggered automatically 3 | # when a new template-based repository has been created. 4 | 5 | name: Template Cleanup 6 | on: 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | template-cleanup: 13 | name: Template Cleanup 14 | runs-on: ubuntu-latest 15 | if: github.event.repository.name != 'kotlin-android-template' 16 | steps: 17 | 18 | # Check out current repository 19 | - name: Fetch Sources 20 | uses: actions/checkout@v4 21 | # Setup Java 22 | - uses: actions/setup-java@v4 23 | with: 24 | distribution: 'zulu' 25 | java-version: '17' 26 | # Setup Gradle 27 | - name: Setup Gradle 28 | uses: gradle/actions/setup-gradle@v4 29 | # Cleanup project 30 | - name: Cleanup 31 | run: ./gradlew templateCleanup 32 | # Commit modified files 33 | - name: Commit files 34 | run: | 35 | git config --local user.email "action@github.com" 36 | git config --local user.name "GitHub Action" 37 | git add . 38 | git commit -m "Template cleanup" 39 | # Push changes 40 | - name: Push changes 41 | uses: ad-m/github-push-action@v0.8.0 42 | with: 43 | branch: main 44 | github_token: ${{ secrets.GITHUB_TOKEN }} 45 | -------------------------------------------------------------------------------- /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=-Xmx1536m 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 22 | 23 | # Edit these properties to configure publishing for your project 24 | GROUP=com.ncorti.kotlin.template 25 | VERSION=1.0.0 26 | 27 | # Edit these properties to configure the app version name and code 28 | APP_VERSION_NAME=1.0.0 29 | APP_VERSION_CODE=1 30 | APP_ID=com.ncorti.kotlin.template.app -------------------------------------------------------------------------------- /.github/workflows/publish-release.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | on: 3 | push: 4 | tags: 5 | - '*' 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish: 10 | # remove this check when your secrets are setup and you're ready to publish 11 | if: ${{ github.repository == 'cortinico/kotlin-android-template'}} 12 | runs-on: [ubuntu-latest] 13 | env: 14 | GRADLE_OPTS: -Dorg.gradle.parallel=false 15 | 16 | steps: 17 | 18 | - name: Checkout Repo 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup Java 22 | uses: actions/setup-java@v4 23 | with: 24 | distribution: 'zulu' 25 | java-version: '17' 26 | 27 | - name: Setup Gradle 28 | uses: gradle/actions/setup-gradle@v4 29 | 30 | - name: Publish to Maven Local 31 | run: ./gradlew publishToMavenLocal 32 | env: 33 | ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} 34 | ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} 35 | 36 | - name: Upload Build Artifacts 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: 'release-artifacts' 40 | path: '~/.m2/repository/' 41 | 42 | - name: Publish to the Snapshot Repository 43 | run: ./gradlew publishToSonatype closeAndReleaseStagingRepositories 44 | env: 45 | ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} 46 | ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} 47 | ORG_GRADLE_PROJECT_NEXUS_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_USERNAME }} 48 | ORG_GRADLE_PROJECT_NEXUS_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_PASSWORD }} -------------------------------------------------------------------------------- /app/src/main/java/com/ncorti/kotlin/template/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.app 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.view.View 6 | import androidx.appcompat.app.AppCompatActivity 7 | import com.ncorti.kotlin.template.app.databinding.ActivityMainBinding 8 | import com.ncorti.kotlin.template.library.FactorialCalculator 9 | import com.ncorti.kotlin.template.library.android.ToastUtil 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | private lateinit var binding: ActivityMainBinding 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | binding = ActivityMainBinding.inflate(layoutInflater) 18 | setContentView(binding.root) 19 | 20 | binding.buttonCompute.setOnClickListener { 21 | val message = if (binding.editTextFactorial.text.isNotEmpty()) { 22 | val input = binding.editTextFactorial.text.toString().toLong() 23 | val result = try { 24 | FactorialCalculator.computeFactorial(input).toString() 25 | } catch (ex: IllegalStateException) { 26 | "Error: ${ex.message}" 27 | } 28 | 29 | binding.textResult.text = result 30 | binding.textResult.visibility = View.VISIBLE 31 | getString(R.string.notification_title, result) 32 | } else { 33 | getString(R.string.please_enter_a_number) 34 | } 35 | ToastUtil.showToast(this, message) 36 | } 37 | 38 | binding.buttonAppcompose.setOnClickListener { 39 | val intent = Intent(it.context, ComposeActivity::class.java) 40 | startActivity(intent) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yaml: -------------------------------------------------------------------------------- 1 | name: Publish Snapshot 2 | on: 3 | push: 4 | branches: 5 | - main 6 | workflow_dispatch: 7 | 8 | jobs: 9 | publish: 10 | # remove this check when your secrets are setup and you're ready to publish 11 | if: ${{ github.repository == 'cortinico/kotlin-android-template'}} 12 | runs-on: [ubuntu-latest] 13 | env: 14 | GRADLE_OPTS: -Dorg.gradle.parallel=false 15 | 16 | steps: 17 | 18 | - name: Checkout Repo 19 | uses: actions/checkout@v4 20 | 21 | - name: Setup Java 22 | uses: actions/setup-java@v4 23 | with: 24 | distribution: 'zulu' 25 | java-version: '17' 26 | 27 | - name: Setup Gradle 28 | uses: gradle/actions/setup-gradle@v4 29 | 30 | - name: Publish to Maven Local 31 | run: ./gradlew publishToMavenLocal 32 | env: 33 | ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} 34 | ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} 35 | ORG_GRADLE_PROJECT_USE_SNAPSHOT: true 36 | 37 | - name: Upload Build Artifacts 38 | uses: actions/upload-artifact@v4 39 | with: 40 | name: 'snapshot-artifacts' 41 | path: '~/.m2/repository/' 42 | 43 | - name: Publish to the Snapshot Repository 44 | run: ./gradlew publishToSonatype 45 | env: 46 | ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }} 47 | ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }} 48 | ORG_GRADLE_PROJECT_NEXUS_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_USERNAME }} 49 | ORG_GRADLE_PROJECT_NEXUS_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_NEXUS_PASSWORD }} 50 | ORG_GRADLE_PROJECT_USE_SNAPSHOT: true -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /library-android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.withType 2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 4 | 5 | plugins { 6 | id("com.android.library") 7 | kotlin("android") 8 | id("maven-publish") 9 | publish 10 | } 11 | 12 | android { 13 | compileSdk = libs.versions.compile.sdk.version.get().toInt() 14 | 15 | defaultConfig { 16 | minSdk = libs.versions.min.sdk.version.get().toInt() 17 | namespace = "com.ncorti.kotlin.template.library.android" 18 | 19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 20 | consumerProguardFiles("consumer-rules.pro") 21 | } 22 | 23 | compileOptions { 24 | sourceCompatibility = JavaVersion.VERSION_17 25 | targetCompatibility = JavaVersion.VERSION_17 26 | } 27 | 28 | buildTypes { 29 | getByName("release") { 30 | isMinifyEnabled = false 31 | proguardFiles( 32 | getDefaultProguardFile("proguard-android-optimize.txt"), 33 | "proguard-rules.pro" 34 | ) 35 | } 36 | } 37 | 38 | publishing { 39 | singleVariant("release") { 40 | withSourcesJar() 41 | withJavadocJar() 42 | } 43 | } 44 | 45 | lint { 46 | warningsAsErrors = true 47 | abortOnError = true 48 | disable.add("GradleDependency") 49 | } 50 | } 51 | 52 | tasks.withType().configureEach { 53 | compilerOptions { 54 | jvmTarget.set(JvmTarget.JVM_17) 55 | } 56 | } 57 | 58 | dependencies { 59 | implementation(libs.androidx.appcompat) 60 | implementation(libs.androidx.core.ktx) 61 | 62 | testImplementation(libs.junit) 63 | 64 | androidTestImplementation(libs.androidx.test.runner) 65 | androidTestImplementation(libs.androidx.test.ext.junit) 66 | } 67 | -------------------------------------------------------------------------------- /library-compose/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.gradle.kotlin.dsl.withType 2 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 3 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 4 | 5 | plugins { 6 | id("com.android.library") 7 | kotlin("android") 8 | alias(libs.plugins.compose.compiler) 9 | 10 | } 11 | 12 | android { 13 | compileSdk = libs.versions.compile.sdk.version.get().toInt() 14 | 15 | defaultConfig { 16 | minSdk = libs.versions.min.sdk.version.get().toInt() 17 | namespace = "com.ncorti.kotlin.template.library.compose" 18 | 19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 20 | 21 | vectorDrawables { 22 | useSupportLibrary = true 23 | } 24 | } 25 | 26 | compileOptions { 27 | sourceCompatibility = JavaVersion.VERSION_17 28 | targetCompatibility = JavaVersion.VERSION_17 29 | } 30 | 31 | buildFeatures { 32 | compose = true 33 | viewBinding = true 34 | buildConfig = false 35 | } 36 | 37 | lint { 38 | warningsAsErrors = true 39 | abortOnError = true 40 | disable.add("GradleDependency") 41 | } 42 | } 43 | 44 | tasks.withType().configureEach { 45 | compilerOptions { 46 | jvmTarget.set(JvmTarget.JVM_17) 47 | } 48 | } 49 | 50 | dependencies { 51 | implementation(projects.libraryKotlin) 52 | implementation(libs.androidx.appcompat) 53 | implementation(libs.androidx.core.ktx) 54 | 55 | implementation(platform(libs.compose.bom)) 56 | implementation(libs.androidx.activity.compose) 57 | implementation(libs.compose.ui) 58 | implementation(libs.compose.ui.tooling) 59 | implementation(libs.compose.foundation) 60 | implementation(libs.compose.material) 61 | 62 | testImplementation(libs.junit) 63 | 64 | debugImplementation(libs.compose.ui.test.manifest) 65 | androidTestImplementation(libs.compose.ui.test.junit4) 66 | androidTestImplementation(libs.androidx.test.runner) 67 | androidTestImplementation(libs.androidx.test.ext.junit) 68 | } 69 | -------------------------------------------------------------------------------- /library-compose/src/main/java/com/ncorti/kotlin/template/app/ComposeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.ncorti.kotlin.template.app 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.animation.ExperimentalAnimationApi 7 | import androidx.compose.foundation.layout.Box 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.layout.wrapContentSize 11 | import androidx.compose.material.MaterialTheme 12 | import androidx.compose.material.Scaffold 13 | import androidx.compose.material.Text 14 | import androidx.compose.material.TopAppBar 15 | import androidx.compose.runtime.Composable 16 | import androidx.compose.ui.Alignment 17 | import androidx.compose.ui.Modifier 18 | import androidx.compose.ui.tooling.preview.Preview 19 | import androidx.compose.ui.unit.dp 20 | import com.ncorti.kotlin.template.app.ui.components.Factorial 21 | 22 | class ComposeActivity : ComponentActivity() { 23 | @ExperimentalAnimationApi 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | setContent { 27 | AppMain() 28 | } 29 | } 30 | } 31 | 32 | @ExperimentalAnimationApi 33 | @Preview 34 | @Composable 35 | fun AppMain() { 36 | MaterialTheme { 37 | Scaffold( 38 | topBar = { 39 | TopAppBar( 40 | title = { Text(text = "Kotlin Android Template") }, 41 | backgroundColor = MaterialTheme.colors.primary 42 | ) 43 | }, 44 | backgroundColor = MaterialTheme.colors.background 45 | ) { 46 | Box( 47 | modifier = Modifier 48 | .padding(it) 49 | .fillMaxSize() 50 | .wrapContentSize(align = Alignment.Center) 51 | .padding(horizontal = 8.dp) 52 | ) { 53 | Factorial() 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | agp = "8.13.0" 3 | androidx_activity_compose = "1.11.0" 4 | androidx_test = "1.7.0" 5 | androidx_test_ext = "1.3.0" 6 | appcompat = "1.7.1" 7 | compile_sdk_version = "36" 8 | compose-bom = "2025.09.00" 9 | compose_compilerextension = "1.5.5" 10 | constraint_layout = "2.2.1" 11 | core_ktx = "1.17.0" 12 | detekt = "1.23.8" 13 | espresso_core = "3.7.0" 14 | junit = "4.13.2" 15 | kotlin = "2.2.20" 16 | min_sdk_version = "23" 17 | nexus_publish = "2.0.0" 18 | target_sdk_version = "36" 19 | 20 | [libraries] 21 | junit = { module = "junit:junit", version.ref = "junit" } 22 | androidx_activity_compose = { module = "androidx.activity:activity-compose", version.ref = "androidx_activity_compose" } 23 | androidx_appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } 24 | androidx_constraint_layout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraint_layout" } 25 | androidx_core_ktx = { module = "androidx.core:core-ktx", version.ref = "core_ktx" } 26 | androidx_test_rules = { module = "androidx.test:rules", version.ref = "androidx_test" } 27 | androidx_test_runner = { module = "androidx.test:runner", version.ref = "androidx_test" } 28 | androidx_test_ext_junit = { module = "androidx.test.ext:junit", version.ref = "androidx_test_ext" } 29 | androidx_test_ext_junit_ktx = { module = "androidx.test.ext:junit-ktx", version.ref = "androidx_test_ext" } 30 | compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" } 31 | compose_material = { module = "androidx.compose.material:material" } 32 | compose_foundation = { module = "androidx.compose.foundation:foundation" } 33 | compose_ui = { module = "androidx.compose.ui:ui" } 34 | compose_ui_tooling = { module = "androidx.compose.ui:ui-tooling" } 35 | compose_ui_test_junit4 = { module = "androidx.compose.ui:ui-test-junit4" } 36 | compose_ui_test_manifest = { module = "androidx.compose.ui:ui-test-manifest" } 37 | detekt_formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } 38 | espresso_core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso_core" } 39 | agp = { module = "com.android.tools.build:gradle", version.ref = "agp" } 40 | kgp = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } 41 | 42 | [plugins] 43 | detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } 44 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 45 | nexus-publish = { id = "io.github.gradle-nexus.publish-plugin", version.ref = "nexus_publish" } -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.kotlin.gradle.dsl.JvmTarget 2 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 3 | 4 | plugins { 5 | id("com.android.application") 6 | kotlin("android") 7 | } 8 | 9 | val APP_VERSION_NAME : String by project 10 | val APP_VERSION_CODE : String by project 11 | val APP_ID : String by project 12 | 13 | android { 14 | compileSdk = libs.versions.compile.sdk.version.get().toInt() 15 | 16 | defaultConfig { 17 | minSdk = libs.versions.min.sdk.version.get().toInt() 18 | namespace = APP_ID 19 | 20 | applicationId = APP_ID 21 | versionCode = APP_VERSION_CODE.toInt() 22 | versionName = APP_VERSION_NAME 23 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 24 | } 25 | buildFeatures { 26 | viewBinding = true 27 | } 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_17 30 | targetCompatibility = JavaVersion.VERSION_17 31 | } 32 | buildTypes { 33 | getByName("release") { 34 | isMinifyEnabled = false 35 | proguardFiles( 36 | getDefaultProguardFile("proguard-android-optimize.txt"), 37 | "proguard-rules.pro" 38 | ) 39 | } 40 | } 41 | 42 | lint { 43 | warningsAsErrors = true 44 | abortOnError = true 45 | disable.add("GradleDependency") 46 | } 47 | 48 | // Use this block to configure different flavors 49 | // flavorDimensions("version") 50 | // productFlavors { 51 | // create("full") { 52 | // dimension = "version" 53 | // applicationIdSuffix = ".full" 54 | // } 55 | // create("demo") { 56 | // dimension = "version" 57 | // applicationIdSuffix = ".demo" 58 | // } 59 | // } 60 | } 61 | 62 | tasks.withType().configureEach { 63 | compilerOptions { 64 | jvmTarget.set(JvmTarget.JVM_17) 65 | } 66 | } 67 | 68 | dependencies { 69 | implementation(projects.libraryAndroid) 70 | implementation(projects.libraryCompose) 71 | implementation(projects.libraryKotlin) 72 | 73 | implementation(libs.androidx.appcompat) 74 | implementation(libs.androidx.constraint.layout) 75 | implementation(libs.androidx.core.ktx) 76 | 77 | testImplementation(libs.junit) 78 | 79 | androidTestImplementation(libs.androidx.test.ext.junit) 80 | androidTestImplementation(libs.androidx.test.ext.junit.ktx) 81 | androidTestImplementation(libs.androidx.test.rules) 82 | androidTestImplementation(libs.espresso.core) 83 | } 84 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 29 | 30 | 42 | 43 | 54 | 55 |