├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ ├── strings.xml │ │ │ │ ├── themes.xml │ │ │ │ └── colors.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 │ │ │ ├── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ └── drawable │ │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ ├── ui │ │ │ │ └── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Shape.kt │ │ │ │ │ ├── Type.kt │ │ │ │ │ └── Theme.kt │ │ │ │ ├── MainApplication.kt │ │ │ │ ├── di │ │ │ │ └── AppModule.kt │ │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro ├── build.gradle.kts └── api │ └── app.api ├── .java-version ├── lib ├── crypto │ ├── .gitignore │ ├── build.gradle.kts │ ├── api │ │ └── crypto.api │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── crypto │ │ ├── Base64Util.kt │ │ └── HashHelper.kt ├── logger │ ├── .gitignore │ ├── build.gradle.kts │ ├── api │ │ └── logger.api │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── logger │ │ └── Logger.kt └── console-logger │ ├── .gitignore │ ├── build.gradle.kts │ ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── console_logger │ │ │ ├── ConsoleLogger.kt │ │ │ ├── NQueensSolver.kt │ │ │ └── NQueensSolver.java │ └── test │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── console_logger │ │ └── NQueensSolverTest.java │ └── api │ └── console-logger.api ├── android-lib ├── base │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── base │ │ │ │ └── KoinComposeActivity.kt │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── ExampleUnitTest.kt │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ ├── api │ │ └── base.api │ └── proguard-rules.pro ├── logcat-logger │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── logcat_logger │ │ │ │ └── LogcatLogger.kt │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── logcat_logger │ │ │ │ └── ExampleUnitTest.kt │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── logcat_logger │ │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ ├── api │ │ └── logcat-logger.api │ └── proguard-rules.pro └── navigation │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── navigation │ │ │ └── PaymentIntentProvider.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── navigation │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── navigation │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ ├── api │ └── navigation.api │ └── proguard-rules.pro ├── feature ├── feature-toys │ ├── .gitignore │ ├── consumer-rules.pro │ ├── build.gradle.kts │ ├── src │ │ ├── test │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── feature_toys │ │ │ │ └── ExampleUnitTest.kt │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── toysapp │ │ │ │ └── feature_toys │ │ │ │ └── ToysActivity.kt │ │ └── androidTest │ │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── feature_toys │ │ │ └── ExampleInstrumentedTest.kt │ ├── proguard-rules.pro │ └── api │ │ └── feature-toys.api └── feature-payment │ ├── .gitignore │ ├── consumer-rules.pro │ ├── src │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── feature_payment │ │ │ ├── PaymentIntentProviderImpl.kt │ │ │ └── PaymentActivity.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── toysapp │ │ │ └── feature_payment │ │ │ └── ScopeFunctionTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── toysapp │ │ └── feature_payment │ │ └── ExampleInstrumentedTest.kt │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── api │ └── feature-payment.api ├── stability_config.conf ├── .idea ├── .gitignore ├── kotlinc.xml ├── vcs.xml ├── migrations.xml ├── deploymentTargetSelector.xml ├── misc.xml ├── deploymentTargetDropDown.xml ├── checkstyle-idea.xml ├── androidTestResultsUserPreferences.xml ├── compiler.xml ├── runConfigurations.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml └── other.xml ├── presentation.pdf ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── .gitignore ├── gradle.properties ├── README.md ├── settings.gradle.kts ├── gradlew.bat ├── gradlew └── module-graph.svg /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /.java-version: -------------------------------------------------------------------------------- 1 | 21.0.6 2 | -------------------------------------------------------------------------------- /lib/crypto/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib/logger/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-lib/base/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-lib/base/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature/feature-toys/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib/console-logger/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-lib/logcat-logger/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-lib/navigation/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /android-lib/navigation/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature/feature-payment/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /feature/feature-payment/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature/feature-toys/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stability_config.conf: -------------------------------------------------------------------------------- 1 | com.example.toysapp.crypto.* 2 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/presentation.pdf -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Toys App 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib/logger/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.jvm.library") 3 | } 4 | 5 | dependencies { 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/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/aldoKelvianto/ToysApp/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/aldoKelvianto/ToysApp/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/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /lib/logger/api/logger.api: -------------------------------------------------------------------------------- 1 | public abstract interface class com/example/toysapp/logger/Logger { 2 | public abstract fun log (Ljava/lang/String;)V 3 | } 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aldoKelvianto/ToysApp/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /lib/logger/src/main/java/com/example/toysapp/logger/Logger.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.logger 2 | 3 | interface Logger { 4 | 5 | fun log(message: String) 6 | } 7 | -------------------------------------------------------------------------------- /android-lib/base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android-lib/navigation/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android-lib/navigation/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.library") 3 | } 4 | 5 | android { 6 | namespace = "com.example.toysapp.navigation" 7 | } 8 | 9 | dependencies { 10 | } 11 | -------------------------------------------------------------------------------- /android-lib/base/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.library") 3 | } 4 | 5 | android { 6 | namespace = "com.example.base" 7 | } 8 | 9 | dependencies { 10 | implementation(libs.koin.android) 11 | } 12 | -------------------------------------------------------------------------------- /lib/crypto/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.jvm.library") 3 | } 4 | 5 | dependencies { 6 | implementation(projects.lib.logger) 7 | implementation(libs.apache.commons.codec) 8 | implementation(libs.koin.core) 9 | } 10 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.library") 3 | } 4 | 5 | android { 6 | namespace = "com.example.toysapp.logcat_logger" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.lib.logger) 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) -------------------------------------------------------------------------------- /feature/feature-payment/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Sep 25 13:25:58 WIB 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /lib/console-logger/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.jvm.library") 3 | } 4 | 5 | tasks.test { 6 | useJUnitPlatform() 7 | } 8 | 9 | dependencies { 10 | implementation(projects.lib.logger) 11 | testImplementation(libs.junit5) 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /.idea/migrations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /android-lib/navigation/src/main/java/com/example/toysapp/navigation/PaymentIntentProvider.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.navigation 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | 6 | interface PaymentIntentProvider { 7 | 8 | fun getPaymentIntent(context: Context, paymentGateway: String): Intent 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /lib/console-logger/src/main/java/com/example/toysapp/console_logger/ConsoleLogger.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.console_logger 2 | 3 | import com.example.toysapp.logger.Logger 4 | 5 | class ConsoleLogger : Logger { 6 | 7 | override fun log(message: String) { 8 | println("Log from ConsoleLogger: $message") 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.nonTransitiveRClass=true 2 | android.useAndroidX=true 3 | kotlin.code.style=official 4 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 5 | org.gradle.parallel=true 6 | org.gradle.caching=true 7 | org.gradle.configureondemand=true 8 | android.defaults.buildfeatures.buildconfig=true 9 | android.nonFinalResIds=false 10 | 11 | -------------------------------------------------------------------------------- /.idea/deploymentTargetSelector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/ui/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.ui.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.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 | ) -------------------------------------------------------------------------------- /feature/feature-toys/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.feature.compose") 3 | } 4 | 5 | android { 6 | namespace = "com.example.toysapp.feature_toys" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.androidLib.navigation) 11 | implementation(projects.androidLib.base) 12 | implementation(projects.lib.logger) 13 | implementation(libs.koin.android) 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/src/main/java/com/example/toysapp/logcat_logger/LogcatLogger.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.logcat_logger 2 | 3 | import android.util.Log 4 | import com.example.toysapp.logger.Logger 5 | 6 | class LogcatLogger: Logger { 7 | 8 | override fun log(message: String) { 9 | Log.d(TAG, "Log from LogcatLogger: $message") 10 | } 11 | 12 | companion object { 13 | private const val TAG = "LogcatLogger" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/toysapp/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /feature/feature-payment/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.feature.compose") 3 | } 4 | 5 | android { 6 | namespace = "com.example.toysapp.feature_payment" 7 | } 8 | 9 | dependencies { 10 | implementation(projects.androidLib.navigation) 11 | implementation(projects.androidLib.base) 12 | implementation(projects.lib.logger) 13 | implementation(libs.koin.android.compose) 14 | implementation(projects.lib.crypto) 15 | } 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | -------------------------------------------------------------------------------- /android-lib/base/src/test/java/com/example/toysapp/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/crypto/api/crypto.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/crypto/Base64Util { 2 | public fun (Lcom/example/toysapp/logger/Logger;)V 3 | public final fun encodeToBase64 (Ljava/lang/String;)Ljava/lang/String; 4 | } 5 | 6 | public final class com/example/toysapp/crypto/HashHelper : org/koin/core/component/KoinComponent { 7 | public fun ()V 8 | public fun getKoin ()Lorg/koin/core/Koin; 9 | public final fun md5Hash (Ljava/lang/String;)Ljava/lang/String; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /android-lib/navigation/src/test/java/com/example/toysapp/navigation/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.navigation 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /feature/feature-toys/src/test/java/com/example/toysapp/feature_toys/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_toys 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/src/test/java/com/example/toysapp/logcat_logger/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.logcat_logger 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android-lib/navigation/api/navigation.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/navigation/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public abstract interface class com/example/toysapp/navigation/PaymentIntentProvider { 9 | public abstract fun getPaymentIntent (Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent; 10 | } 11 | 12 | -------------------------------------------------------------------------------- /lib/crypto/src/main/java/com/example/toysapp/crypto/Base64Util.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.crypto 2 | 3 | import com.example.toysapp.logger.Logger 4 | import org.apache.commons.codec.binary.Base64 5 | 6 | class Base64Util( 7 | private val logger: Logger 8 | ) { 9 | 10 | fun encodeToBase64(text: String): String { 11 | logger.log("EncoderUtil - encodeToBase64") 12 | 13 | val base64 = Base64() 14 | val encodedBytes = base64.encode(text.toByteArray()) 15 | return String(encodedBytes) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /lib/crypto/src/main/java/com/example/toysapp/crypto/HashHelper.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.crypto 2 | 3 | import com.example.toysapp.logger.Logger 4 | import org.apache.commons.codec.digest.Md5Crypt 5 | import org.koin.core.component.KoinComponent 6 | import org.koin.core.component.inject 7 | 8 | class HashHelper : KoinComponent { 9 | 10 | private val logger: Logger by inject() 11 | 12 | fun md5Hash(text: String): String { 13 | logger.log("HashHelper - md5Hash") 14 | return Md5Crypt.md5Crypt(text.toByteArray()) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | ToysApp is an Android app written in Kotlin. This app is used as a companion project in "The Cost of 4 | Modularization" presentation. This project demonstrates the following: 5 | 6 | 1. How to use Gradle Version Catalog 7 | 2. How to use convention plugin 8 | 3. How to navigate between activities in a multimodule project 9 | 4. How to use Koin and Koin scope in a multimodule project 10 | 11 | # Publication 12 | 13 | ## Android Worldwide 14 | You can access the presentation file in this repository: [presentation.pdf](presentation.pdf) 15 | -------------------------------------------------------------------------------- /feature/feature-toys/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import android.app.Application 4 | import com.example.toysapp.di.appModule 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.android.ext.koin.androidLogger 7 | import org.koin.core.context.startKoin 8 | 9 | class MainApplication: Application() { 10 | 11 | override fun onCreate() { 12 | super.onCreate() 13 | startKoin { 14 | androidLogger() 15 | androidContext(this@MainApplication) 16 | modules(appModule) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /feature/feature-payment/src/main/java/com/example/toysapp/feature_payment/PaymentIntentProviderImpl.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_payment 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import com.example.toysapp.navigation.PaymentIntentProvider 6 | 7 | class PaymentIntentProviderImpl : PaymentIntentProvider { 8 | 9 | override fun getPaymentIntent(context: Context, paymentGateway: String): Intent { 10 | return Intent(context, PaymentActivity::class.java).apply { 11 | putExtra(PaymentActivity.KEY_PAYMENT_GATEWAY, paymentGateway) 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /android-lib/base/src/main/java/com/example/toysapp/base/KoinComposeActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.base 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import org.koin.android.scope.AndroidScopeComponent 6 | import org.koin.androidx.scope.createActivityRetainedScope 7 | import org.koin.core.scope.Scope 8 | 9 | abstract class KoinComposeActivity : ComponentActivity(), AndroidScopeComponent { 10 | 11 | override var scope: Scope? = null 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | createActivityRetainedScope() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /feature/feature-payment/src/test/java/com/example/toysapp/feature_payment/ScopeFunctionTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_payment 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | class ScopeFunctionTest { 7 | 8 | @Test 9 | fun `apply vs with`() { 10 | val list: MutableList = mutableListOf().apply { 11 | add(1) 12 | add(2) 13 | add(3) 14 | } 15 | assertEquals(3, list.size) 16 | val number: Int = with(mutableListOf()) { 17 | add(1) 18 | add(2) 19 | get(0) 20 | } 21 | assertEquals(1, number) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android-lib/base/api/base.api: -------------------------------------------------------------------------------- 1 | public final class com/example/base/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public abstract class com/example/toysapp/base/KoinComposeActivity : androidx/activity/ComponentActivity, org/koin/android/scope/AndroidScopeComponent { 9 | public fun ()V 10 | public fun getScope ()Lorg/koin/core/scope/Scope; 11 | protected fun onCreate (Landroid/os/Bundle;)V 12 | public fun requireScope ()Lorg/koin/core/scope/Scope; 13 | public fun setScope (Lorg/koin/core/scope/Scope;)V 14 | } 15 | 16 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/api/logcat-logger.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/logcat_logger/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public final class com/example/toysapp/logcat_logger/LogcatLogger : com/example/toysapp/logger/Logger { 9 | public static final field Companion Lcom/example/toysapp/logcat_logger/LogcatLogger$Companion; 10 | public fun ()V 11 | public fun log (Ljava/lang/String;)V 12 | } 13 | 14 | public final class com/example/toysapp/logcat_logger/LogcatLogger$Companion { 15 | } 16 | 17 | -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10.17.0 5 | JavaOnly 6 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/androidTestResultsUserPreferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/toysapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.toysapp", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /android-lib/base/src/androidTest/java/com/example/toysapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.platform.app.InstrumentationRegistry 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 20 | assertEquals("com.example.base.test", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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. 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 -------------------------------------------------------------------------------- /android-lib/base/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 -------------------------------------------------------------------------------- /android-lib/navigation/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 -------------------------------------------------------------------------------- /feature/feature-payment/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 -------------------------------------------------------------------------------- /feature/feature-toys/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 -------------------------------------------------------------------------------- /android-lib/logcat-logger/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 -------------------------------------------------------------------------------- /android-lib/navigation/src/androidTest/java/com/example/toysapp/navigation/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.navigation 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.toysapp.navigation.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /feature/feature-toys/src/androidTest/java/com/example/toysapp/feature_toys/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_toys 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.toysapp.feature_toys.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /android-lib/logcat-logger/src/androidTest/java/com/example/toysapp/logcat_logger/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.logcat_logger 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.toysapp.logcat_logger.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/console-logger/api/console-logger.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/console_logger/ConsoleLogger : com/example/toysapp/logger/Logger { 2 | public fun ()V 3 | public fun log (Ljava/lang/String;)V 4 | } 5 | 6 | public class com/example/toysapp/console_logger/NQueensSolver { 7 | public fun ()V 8 | public static fun main ([Ljava/lang/String;)V 9 | public fun solveNQueens (I)Ljava/util/List; 10 | } 11 | 12 | public final class com/example/toysapp/consolelogger/NQueensSolver { 13 | public static final field Companion Lcom/example/toysapp/consolelogger/NQueensSolver$Companion; 14 | public fun ()V 15 | public static final fun main ([Ljava/lang/String;)V 16 | public final fun solveNQueens (I)Ljava/util/List; 17 | } 18 | 19 | public final class com/example/toysapp/consolelogger/NQueensSolver$Companion { 20 | public final fun main ([Ljava/lang/String;)V 21 | } 22 | 23 | -------------------------------------------------------------------------------- /feature/feature-payment/src/androidTest/java/com/example/toysapp/feature_payment/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_payment 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.example.toysapp.feature_payment.test", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.ui.theme 2 | 3 | import androidx.compose.material.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 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.di 2 | 3 | import com.example.toysapp.console_logger.ConsoleLogger 4 | import com.example.toysapp.crypto.Base64Util 5 | import com.example.toysapp.crypto.HashHelper 6 | import com.example.toysapp.feature_payment.PaymentIntentProviderImpl 7 | import com.example.toysapp.feature_toys.ToysActivity 8 | import com.example.toysapp.logcat_logger.LogcatLogger 9 | import com.example.toysapp.logger.Logger 10 | import com.example.toysapp.navigation.PaymentIntentProvider 11 | import org.koin.core.qualifier.named 12 | import org.koin.dsl.module 13 | 14 | val appModule = module { 15 | scope { 16 | scoped { 17 | LogcatLogger() 18 | } 19 | scoped { 20 | PaymentIntentProviderImpl() 21 | } 22 | } 23 | single { 24 | ConsoleLogger() 25 | } 26 | single(named("logcat")) { 27 | LogcatLogger() 28 | } 29 | single { 30 | HashHelper() 31 | } 32 | single { 33 | Base64Util(get(named("logcat"))) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.example.toysapp.convention.android.application.compose") 3 | } 4 | 5 | android { 6 | namespace = "com.example.toysapp" 7 | 8 | defaultConfig { 9 | applicationId = "com.example.toysapp" 10 | versionCode = 1 11 | versionName = "1.0" 12 | } 13 | } 14 | 15 | moduleGraphAssert { 16 | maxHeight = 3 17 | allowed = arrayOf( 18 | ":.* -> :lib:.*", 19 | ":.* -> :android-lib:.*", 20 | ":feature:.* -> :lib:.*", 21 | ":feature:.* -> :android-lib:.*", 22 | ":app -> :feature:.*" 23 | ) 24 | restricted = arrayOf( 25 | ":feature:.* -X> :feature:.*", 26 | ":android-lib:.* -X> :feature:.*", 27 | ":lib:.* -X> :feature:.*" 28 | ) 29 | } 30 | 31 | dependencies { 32 | implementation(libs.koin.android) 33 | implementation(projects.feature.featurePayment) 34 | implementation(projects.feature.featureToys) 35 | implementation(projects.lib.consoleLogger) 36 | implementation(projects.lib.crypto) 37 | implementation(projects.lib.logger) 38 | implementation(projects.androidLib.logcatLogger) 39 | implementation(projects.androidLib.navigation) 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 16 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.ui.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun ToysAppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { 32 | val colors = if (darkTheme) { 33 | DarkColorPalette 34 | } else { 35 | LightColorPalette 36 | } 37 | 38 | MaterialTheme( 39 | colors = colors, 40 | typography = Typography, 41 | shapes = Shapes, 42 | content = content 43 | ) 44 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | includeBuild("build-logic") 3 | repositories { 4 | gradlePluginPortal() 5 | google() 6 | mavenCentral() 7 | } 8 | } 9 | 10 | plugins { 11 | // From: https://docs.gradle.org/current/userguide/version_catalogs.html 12 | // You cannot use a plugin declared in a version catalog in your settings file or settings plugin. 13 | // alias(libs.plugins.build.health) 14 | id("com.autonomousapps.build-health") version "2.8.2" 15 | 16 | // From: https://github.com/autonomousapps/dependency-analysis-gradle-plugin/wiki/Adding-to-your-project 17 | id("org.jetbrains.kotlin.jvm") version "2.1.10" apply false 18 | id("com.android.application") version "8.8.1" apply false 19 | id("org.jetbrains.kotlin.android") version "2.1.10" apply false 20 | } 21 | 22 | dependencyResolutionManagement { 23 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 24 | repositories { 25 | google() 26 | mavenCentral() 27 | } 28 | } 29 | 30 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") 31 | 32 | rootProject.name = "ToysApp" 33 | include(":app") 34 | include(":feature:feature-toys") 35 | include(":feature:feature-payment") 36 | include(":android-lib:logcat-logger") 37 | include(":android-lib:navigation") 38 | include(":android-lib:base") 39 | include(":lib:logger") 40 | include(":lib:console-logger") 41 | include(":lib:crypto") 42 | -------------------------------------------------------------------------------- /feature/feature-toys/api/feature-toys.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/feature_toys/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public final class com/example/toysapp/feature_toys/ComposableSingletons$ToysActivityKt { 9 | public static final field INSTANCE Lcom/example/toysapp/feature_toys/ComposableSingletons$ToysActivityKt; 10 | public static field lambda-1 Lkotlin/jvm/functions/Function3; 11 | public fun ()V 12 | public final fun getLambda-1$feature_toys_release ()Lkotlin/jvm/functions/Function3; 13 | } 14 | 15 | public final class com/example/toysapp/feature_toys/ToysActivity : com/example/toysapp/base/KoinComposeActivity { 16 | public static final field $stable I 17 | public static final field Companion Lcom/example/toysapp/feature_toys/ToysActivity$Companion; 18 | public static final field KEY_NAME Ljava/lang/String; 19 | public fun ()V 20 | } 21 | 22 | public final class com/example/toysapp/feature_toys/ToysActivity$Companion { 23 | } 24 | 25 | public final class com/example/toysapp/feature_toys/ToysActivityKt { 26 | public static final fun Content (Ljava/lang/String;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;II)V 27 | public static final fun ContentPreview (Landroidx/compose/runtime/Composer;I)V 28 | } 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/toysapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.compose.foundation.layout.* 8 | import androidx.compose.material.Button 9 | import androidx.compose.material.Text 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.tooling.preview.Preview 14 | import androidx.compose.ui.unit.dp 15 | import com.example.toysapp.feature_payment.PaymentActivity 16 | import com.example.toysapp.feature_toys.ToysActivity 17 | 18 | class MainActivity : ComponentActivity() { 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setContent { 22 | Content(::launchToyActivity, ::launchPaymentActivity) 23 | } 24 | } 25 | 26 | private fun launchToyActivity() { 27 | val intent = Intent(this, ToysActivity::class.java) 28 | startActivity(intent) 29 | } 30 | 31 | private fun launchPaymentActivity() { 32 | val intent = Intent(this, PaymentActivity::class.java) 33 | startActivity(intent) 34 | } 35 | } 36 | 37 | @Composable 38 | fun Content( 39 | onToyButtonClick: () -> Unit = {}, 40 | onPaymentButtonClick: () -> Unit = {} 41 | ) { 42 | Column( 43 | modifier = Modifier 44 | .fillMaxSize() 45 | .padding(8.dp), 46 | verticalArrangement = Arrangement.Center, 47 | horizontalAlignment = Alignment.CenterHorizontally, 48 | ) { 49 | Text("This is Main Activity") 50 | Spacer(modifier = Modifier.height(16.dp)) 51 | Button(onClick = onToyButtonClick) { 52 | Text("Go to Toy Activity") 53 | } 54 | Spacer(modifier = Modifier.height(16.dp)) 55 | Button(onClick = onPaymentButtonClick) { 56 | Text("Go to Payment Activity") 57 | } 58 | } 59 | } 60 | 61 | @Preview(showBackground = true) 62 | @Composable 63 | fun ContentPreview() { 64 | Content() 65 | } 66 | -------------------------------------------------------------------------------- /feature/feature-payment/api/feature-payment.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/feature_payment/BuildConfig { 2 | public static final field BUILD_TYPE Ljava/lang/String; 3 | public static final field DEBUG Z 4 | public static final field LIBRARY_PACKAGE_NAME Ljava/lang/String; 5 | public fun ()V 6 | } 7 | 8 | public final class com/example/toysapp/feature_payment/ComposableSingletons$PaymentActivityKt { 9 | public static final field INSTANCE Lcom/example/toysapp/feature_payment/ComposableSingletons$PaymentActivityKt; 10 | public static field lambda-1 Lkotlin/jvm/functions/Function3; 11 | public static field lambda-2 Lkotlin/jvm/functions/Function3; 12 | public static field lambda-3 Lkotlin/jvm/functions/Function3; 13 | public fun ()V 14 | public final fun getLambda-1$feature_payment_release ()Lkotlin/jvm/functions/Function3; 15 | public final fun getLambda-2$feature_payment_release ()Lkotlin/jvm/functions/Function3; 16 | public final fun getLambda-3$feature_payment_release ()Lkotlin/jvm/functions/Function3; 17 | } 18 | 19 | public final class com/example/toysapp/feature_payment/PaymentActivity : androidx/activity/ComponentActivity { 20 | public static final field $stable I 21 | public static final field Companion Lcom/example/toysapp/feature_payment/PaymentActivity$Companion; 22 | public static final field KEY_PAYMENT_GATEWAY Ljava/lang/String; 23 | public fun ()V 24 | } 25 | 26 | public final class com/example/toysapp/feature_payment/PaymentActivity$Companion { 27 | } 28 | 29 | public final class com/example/toysapp/feature_payment/PaymentActivityKt { 30 | public static final fun Content (Ljava/lang/String;Lkotlin/jvm/functions/Function0;Lcom/example/toysapp/crypto/HashHelper;Lcom/example/toysapp/crypto/Base64Util;Landroidx/compose/runtime/Composer;II)V 31 | public static final fun ContentPreview (Landroidx/compose/runtime/Composer;I)V 32 | } 33 | 34 | public final class com/example/toysapp/feature_payment/PaymentIntentProviderImpl : com/example/toysapp/navigation/PaymentIntentProvider { 35 | public static final field $stable I 36 | public fun ()V 37 | public fun getPaymentIntent (Landroid/content/Context;Ljava/lang/String;)Landroid/content/Intent; 38 | } 39 | 40 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 44 | 45 | -------------------------------------------------------------------------------- /lib/console-logger/src/main/java/com/example/toysapp/console_logger/NQueensSolver.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.consolelogger 2 | 3 | class NQueensSolver { 4 | fun solveNQueens(n: Int): List> { 5 | val solutions = mutableListOf>() 6 | val board = Array(n) { CharArray(n) { '.' } } 7 | 8 | backtrack(board, 0, solutions) 9 | return solutions 10 | } 11 | 12 | private fun backtrack(board: Array, row: Int, solutions: MutableList>) { 13 | if (row == board.size) { 14 | solutions.add(constructSolution(board)) 15 | return 16 | } 17 | 18 | for (col in board.indices) { 19 | if (isValid(board, row, col)) { 20 | board[row][col] = 'Q' 21 | backtrack(board, row + 1, solutions) 22 | board[row][col] = '.' 23 | } 24 | } 25 | } 26 | 27 | private fun isValid(board: Array, row: Int, col: Int): Boolean { 28 | for (i in 0 until row) { 29 | if (board[i][col] == 'Q') { 30 | return false 31 | } 32 | } 33 | 34 | for (i in row - 1 downTo 0) { 35 | if (col - (row - i) >= 0 && board[i][col - (row - i)] == 'Q') { 36 | return false 37 | } 38 | if (col + (row - i) < board.size && board[i][col + (row - i)] == 'Q') { 39 | return false 40 | } 41 | } 42 | 43 | return true 44 | } 45 | 46 | private fun constructSolution(board: Array): List = board.map { String(it) } 47 | 48 | companion object { 49 | @JvmStatic 50 | fun main(args: Array) { 51 | val solver = NQueensSolver() 52 | val solutions = solver.solveNQueens(4) 53 | 54 | println("Solutions for 4-Queens problem:") 55 | for ((index, solution) in solutions.withIndex()) { 56 | println("Solution ${index + 1}:") 57 | for (row in solution) { 58 | println(row) 59 | } 60 | println() 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /feature/feature-toys/src/main/java/com/example/toysapp/feature_toys/ToysActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_toys 2 | 3 | import android.os.Bundle 4 | import androidx.activity.compose.setContent 5 | import androidx.compose.foundation.layout.* 6 | import androidx.compose.material.Button 7 | import androidx.compose.material.Text 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Alignment 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.tooling.preview.Preview 12 | import androidx.compose.ui.unit.dp 13 | import com.example.toysapp.base.KoinComposeActivity 14 | import com.example.toysapp.logger.Logger 15 | import com.example.toysapp.navigation.PaymentIntentProvider 16 | import org.koin.android.ext.android.inject 17 | 18 | class ToysActivity : KoinComposeActivity() { 19 | 20 | private val paymentIntentProvider: PaymentIntentProvider by inject() 21 | 22 | private val logger: Logger by inject() 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | logger.log("ToysActivity - onCreate") 27 | val name = intent.extras?.getString(KEY_NAME) ?: "Empty Name" 28 | setContent { 29 | Content(name, ::launchPaymentActivity) 30 | } 31 | } 32 | 33 | private fun launchPaymentActivity() { 34 | val intent = paymentIntentProvider.getPaymentIntent(this, "Stripe") 35 | startActivity(intent) 36 | } 37 | 38 | companion object { 39 | const val KEY_NAME = "key_name" 40 | } 41 | } 42 | 43 | @Composable 44 | fun Content( 45 | name: String = "Empty name", 46 | onPaymentButtonClick: () -> Unit = {} 47 | ) { 48 | Column( 49 | modifier = Modifier 50 | .fillMaxSize() 51 | .padding(8.dp), 52 | verticalArrangement = Arrangement.Center, 53 | horizontalAlignment = Alignment.CenterHorizontally, 54 | ) { 55 | Text("This is Toys Activity. Name: $name") 56 | Spacer(modifier = Modifier.height(16.dp)) 57 | Button(onClick = onPaymentButtonClick) { 58 | Text("Go to Payment Activity") 59 | } 60 | } 61 | } 62 | 63 | @Preview(showBackground = true) 64 | @Composable 65 | fun ContentPreview() { 66 | Content() 67 | } 68 | -------------------------------------------------------------------------------- /app/api/app.api: -------------------------------------------------------------------------------- 1 | public final class com/example/toysapp/BuildConfig { 2 | public static final field APPLICATION_ID Ljava/lang/String; 3 | public static final field BUILD_TYPE Ljava/lang/String; 4 | public static final field DEBUG Z 5 | public static final field VERSION_CODE I 6 | public static final field VERSION_NAME Ljava/lang/String; 7 | public fun ()V 8 | } 9 | 10 | public final class com/example/toysapp/ComposableSingletons$MainActivityKt { 11 | public static final field INSTANCE Lcom/example/toysapp/ComposableSingletons$MainActivityKt; 12 | public static field lambda-1 Lkotlin/jvm/functions/Function3; 13 | public static field lambda-2 Lkotlin/jvm/functions/Function3; 14 | public fun ()V 15 | public final fun getLambda-1$app_release ()Lkotlin/jvm/functions/Function3; 16 | public final fun getLambda-2$app_release ()Lkotlin/jvm/functions/Function3; 17 | } 18 | 19 | public final class com/example/toysapp/MainActivity : androidx/activity/ComponentActivity { 20 | public static final field $stable I 21 | public fun ()V 22 | } 23 | 24 | public final class com/example/toysapp/MainActivityKt { 25 | public static final fun Content (Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function0;Landroidx/compose/runtime/Composer;II)V 26 | public static final fun ContentPreview (Landroidx/compose/runtime/Composer;I)V 27 | } 28 | 29 | public final class com/example/toysapp/MainApplication : android/app/Application { 30 | public static final field $stable I 31 | public fun ()V 32 | public fun onCreate ()V 33 | } 34 | 35 | public final class com/example/toysapp/di/AppModuleKt { 36 | public static final fun getAppModule ()Lorg/koin/core/module/Module; 37 | } 38 | 39 | public final class com/example/toysapp/ui/theme/ColorKt { 40 | public static final fun getPurple200 ()J 41 | public static final fun getPurple500 ()J 42 | public static final fun getPurple700 ()J 43 | public static final fun getTeal200 ()J 44 | } 45 | 46 | public final class com/example/toysapp/ui/theme/ShapeKt { 47 | public static final fun getShapes ()Landroidx/compose/material/Shapes; 48 | } 49 | 50 | public final class com/example/toysapp/ui/theme/ThemeKt { 51 | public static final fun ToysAppTheme (ZLkotlin/jvm/functions/Function2;Landroidx/compose/runtime/Composer;II)V 52 | } 53 | 54 | public final class com/example/toysapp/ui/theme/TypeKt { 55 | public static final fun getTypography ()Landroidx/compose/material/Typography; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | androidGradlePlugin = "8.8.1" 3 | androidxComposeBom = "2025.02.00" 4 | androidMinSdk = "21" 5 | androidCompileSdk = "35" 6 | androidTargetSdk = "35" 7 | composeUiVersion = "1.2.1" 8 | buildHealth = "2.8.2" 9 | koin = "3.2.2" 10 | kotlin = "2.1.10" 11 | 12 | [libraries] 13 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "androidxComposeBom" } 14 | koin-android = { group = "io.insert-koin", name = "koin-android", version.ref = "koin" } 15 | koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" } 16 | koin-android-compose = "io.insert-koin:koin-androidx-compose:3.2.1" 17 | compose-activity = "androidx.activity:activity-compose:1.10.0" 18 | compose-ui = { group = "androidx.compose.ui", name = "ui" } 19 | compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling-preview" } 20 | compose-material = { group = "androidx.compose.material", name = "material" } 21 | apache-commons-codec = "commons-codec:commons-codec:1.16.1" 22 | junit = "junit:junit:4.13.2" 23 | junit5 = "org.junit.jupiter:junit-jupiter-engine:5.10.3" 24 | androidJunit = "androidx.test.ext:junit:1.1.3" 25 | androidEspresso = "androidx.test.espresso:espresso-core:3.6.1" 26 | 27 | # Dependencies for build-logic 28 | gradle-plugin-android = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } 29 | gradle-plugin-kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } 30 | gradle-plugin-compose-compiler = { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" } 31 | 32 | [bundles] 33 | compose = ["compose-activity", "compose-ui", "compose-ui-tooling", "compose-material"] 34 | 35 | [plugins] 36 | android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" } 37 | android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" } 38 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } 39 | kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } 40 | build-health = { id = "com.autonomousapps.build-health", version.ref = "buildHealth" } 41 | binary-compatibility-validator = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.2" 42 | module-graph-assertion = "com.jraska.module.graph.assertion:2.3.0" 43 | compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } 44 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 41 | -------------------------------------------------------------------------------- /lib/console-logger/src/main/java/com/example/toysapp/console_logger/NQueensSolver.java: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.console_logger; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class NQueensSolver { 7 | public List> solveNQueens(int n) { 8 | List> solutions = new ArrayList<>(); 9 | char[][] board = new char[n][n]; 10 | 11 | // Initialize the board with empty spaces 12 | for (int i = 0; i < n; i++) { 13 | for (int j = 0; j < n; j++) { 14 | board[i][j] = '.'; 15 | } 16 | } 17 | 18 | backtrack(board, 0, solutions); 19 | return solutions; 20 | } 21 | 22 | private void backtrack(char[][] board, int row, List> solutions) { 23 | if (row == board.length) { 24 | solutions.add(constructSolution(board)); 25 | return; 26 | } 27 | 28 | for (int col = 0; col < board.length; col++) { 29 | if (isValid(board, row, col)) { 30 | board[row][col] = 'Q'; 31 | backtrack(board, row + 1, solutions); 32 | board[row][col] = '.'; 33 | } 34 | } 35 | } 36 | 37 | private boolean isValid(char[][] board, int row, int col) { 38 | // Check column 39 | for (int i = 0; i < row; i++) { 40 | if (board[i][col] == 'Q') { 41 | return false; 42 | } 43 | } 44 | 45 | // Check upper-left diagonal 46 | for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { 47 | if (board[i][j] == 'Q') { 48 | return false; 49 | } 50 | } 51 | 52 | // Check upper-right diagonal 53 | for (int i = row - 1, j = col + 1; i >= 0 && j < board.length; i--, j++) { 54 | if (board[i][j] == 'Q') { 55 | return false; 56 | } 57 | } 58 | 59 | return true; 60 | } 61 | 62 | private List constructSolution(char[][] board) { 63 | List solution = new ArrayList<>(); 64 | for (char[] row : board) { 65 | solution.add(new String(row)); 66 | } 67 | return solution; 68 | } 69 | 70 | public static void main(String[] args) { 71 | NQueensSolver solver = new NQueensSolver(); 72 | List> solutions = solver.solveNQueens(4); 73 | 74 | System.out.println("Solutions for 4-Queens problem:"); 75 | for (int i = 0; i < solutions.size(); i++) { 76 | System.out.println("Solution " + (i + 1) + ":"); 77 | for (String row : solutions.get(i)) { 78 | System.out.println(row); 79 | } 80 | System.out.println(); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /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 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /lib/console-logger/src/test/java/com/example/toysapp/console_logger/NQueensSolverTest.java: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.console_logger; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import static org.junit.jupiter.api.Assertions.*; 5 | 6 | import java.util.List; 7 | 8 | public class NQueensSolverTest { 9 | 10 | private NQueensSolver solver = new NQueensSolver(); 11 | 12 | @Test 13 | public void testSolveNQueensFor1x1Board() { 14 | List> solutions = solver.solveNQueens(1); 15 | assertEquals(1, solutions.size()); 16 | assertEquals("Q", solutions.get(0).get(0)); 17 | } 18 | 19 | @Test 20 | public void testSolveNQueensFor2x2Board() { 21 | List> solutions = solver.solveNQueens(2); 22 | assertEquals(0, solutions.size()); 23 | } 24 | 25 | @Test 26 | public void testSolveNQueensFor3x3Board() { 27 | List> solutions = solver.solveNQueens(3); 28 | assertEquals(0, solutions.size()); 29 | } 30 | 31 | @Test 32 | public void testSolveNQueensFor4x4Board() { 33 | List> solutions = solver.solveNQueens(4); 34 | assertEquals(2, solutions.size()); 35 | 36 | List expectedSolution1 = List.of( 37 | ".Q..", 38 | "...Q", 39 | "Q...", 40 | "..Q." 41 | ); 42 | 43 | List expectedSolution2 = List.of( 44 | "..Q.", 45 | "Q...", 46 | "...Q", 47 | ".Q.." 48 | ); 49 | 50 | assertTrue(solutions.contains(expectedSolution1)); 51 | assertTrue(solutions.contains(expectedSolution2)); 52 | } 53 | 54 | @Test 55 | public void testSolveNQueensFor8x8Board() { 56 | List> solutions = solver.solveNQueens(8); 57 | assertEquals(92, solutions.size()); 58 | } 59 | 60 | @Test 61 | public void testNoTwoQueensThreatenEachOther() { 62 | List> solutions = solver.solveNQueens(5); 63 | for (List solution : solutions) { 64 | assertTrue(isValidSolution(solution)); 65 | } 66 | } 67 | 68 | private boolean isValidSolution(List board) { 69 | int n = board.size(); 70 | for (int i = 0; i < n; i++) { 71 | for (int j = 0; j < n; j++) { 72 | if (board.get(i).charAt(j) == 'Q') { 73 | // Check row and column 74 | for (int k = 0; k < n; k++) { 75 | if (k != j && board.get(i).charAt(k) == 'Q') return false; 76 | if (k != i && board.get(k).charAt(j) == 'Q') return false; 77 | } 78 | // Check diagonals 79 | for (int k = 1; k < n; k++) { 80 | if (i+k < n && j+k < n && board.get(i+k).charAt(j+k) == 'Q') return false; 81 | if (i-k >= 0 && j-k >= 0 && board.get(i-k).charAt(j-k) == 'Q') return false; 82 | if (i+k < n && j-k >= 0 && board.get(i+k).charAt(j-k) == 'Q') return false; 83 | if (i-k >= 0 && j+k < n && board.get(i-k).charAt(j+k) == 'Q') return false; 84 | } 85 | } 86 | } 87 | } 88 | return true; 89 | } 90 | } -------------------------------------------------------------------------------- /feature/feature-payment/src/main/java/com/example/toysapp/feature_payment/PaymentActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.toysapp.feature_payment 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import androidx.activity.ComponentActivity 6 | import androidx.activity.compose.setContent 7 | import androidx.compose.foundation.layout.* 8 | import androidx.compose.material.Button 9 | import androidx.compose.material.Text 10 | import androidx.compose.runtime.* 11 | import androidx.compose.ui.Alignment 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.tooling.preview.Preview 14 | import androidx.compose.ui.unit.dp 15 | import com.example.toysapp.crypto.Base64Util 16 | import com.example.toysapp.crypto.HashHelper 17 | import com.example.toysapp.logger.Logger 18 | import org.koin.android.ext.android.inject 19 | import org.koin.androidx.compose.get 20 | 21 | class PaymentActivity : ComponentActivity() { 22 | 23 | private val logger: Logger by inject() 24 | 25 | override fun onCreate(savedInstanceState: Bundle?) { 26 | super.onCreate(savedInstanceState) 27 | logger.log("PaymentActivity - onCreate") 28 | 29 | val provider = intent.getStringExtra(KEY_PAYMENT_GATEWAY) ?: "Empty Provider" 30 | setContent { 31 | Content(provider, ::launchToyActivity) 32 | } 33 | } 34 | 35 | private fun launchToyActivity() { 36 | val action = "com.example.toysapp.launch.toy.activity" 37 | // packageName is the application id, so the value is 38 | // com.example.toysapp , not com.example.toysapp.feature_payment 39 | val intent = Intent(action).apply { 40 | setPackage(this@PaymentActivity.packageName) 41 | putExtra("key_name", "Rubber Duck") 42 | } 43 | startActivity(intent) 44 | } 45 | 46 | companion object { 47 | const val KEY_PAYMENT_GATEWAY = "key_payment_gateway" 48 | } 49 | } 50 | 51 | @Composable 52 | fun Content( 53 | provider: String = "Default Payment Gateway", 54 | onToyButtonClick: () -> Unit = {}, 55 | hashHelper: HashHelper = get(), 56 | base64Util: Base64Util = get() 57 | ) { 58 | Column( 59 | modifier = Modifier 60 | .fillMaxSize() 61 | .padding(8.dp), 62 | verticalArrangement = Arrangement.Center, 63 | horizontalAlignment = Alignment.CenterHorizontally, 64 | ) { 65 | Text(text = "This is Payment Activity. Payment Gateway: $provider") 66 | Spacer(modifier = Modifier.height(16.dp)) 67 | Button(onClick = onToyButtonClick) { 68 | Text("Go to Toy Activity") 69 | } 70 | Spacer(modifier = Modifier.height(32.dp)) 71 | 72 | var hashText by remember { 73 | mutableStateOf("This is hash result") 74 | } 75 | Text(text = hashText) 76 | Spacer(modifier = Modifier.height(16.dp)) 77 | Button(onClick = { 78 | hashText = hashHelper.md5Hash(provider) 79 | }) { 80 | Text("Hash Provider") 81 | } 82 | Spacer(modifier = Modifier.height(32.dp)) 83 | 84 | var base64Text by remember { 85 | mutableStateOf("This is base64 encode result") 86 | } 87 | Text(text = base64Text) 88 | Spacer(modifier = Modifier.height(16.dp)) 89 | Button(onClick = { 90 | base64Text = base64Util.encodeToBase64(provider) 91 | }) { 92 | Text("Base64 Encode Provider") 93 | } 94 | } 95 | } 96 | 97 | @Preview(showBackground = true) 98 | @Composable 99 | fun ContentPreview() { 100 | Content() 101 | } 102 | 103 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /.idea/other.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 265 | 266 | -------------------------------------------------------------------------------- /module-graph.svg: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | G 7 | 9 | 10 | 11 | :app 12 | 13 | :app 15 | 16 | 17 | 18 | 19 | :feature:feature-payment 20 | 21 | :feature:feature-payment 23 | 24 | 25 | 26 | 27 | :app->:feature:feature-payment 28 | 30 | 32 | 33 | 34 | 35 | :feature:feature-toys 36 | 37 | :feature:feature-toys 39 | 40 | 41 | 42 | 43 | :app->:feature:feature-toys 44 | 46 | 48 | 49 | 50 | 51 | :lib:console-logger 52 | 53 | :lib:console-logger 55 | 56 | 57 | 58 | 59 | :app->:lib:console-logger 60 | 62 | 64 | 65 | 66 | 67 | :lib:crypto 68 | 69 | :lib:crypto 71 | 72 | 73 | 74 | 75 | :app->:lib:crypto 76 | 78 | 80 | 81 | 82 | 83 | :lib:logger 84 | 85 | :lib:logger 87 | 88 | 89 | 90 | 91 | :app->:lib:logger 92 | 94 | 96 | 97 | 98 | 99 | :android-lib:logcat-logger 100 | 101 | :android-lib:logcat-logger 103 | 104 | 105 | 106 | 107 | :app->:android-lib:logcat-logger 108 | 110 | 112 | 113 | 114 | 115 | :android-lib:navigation 116 | 117 | :android-lib:navigation 119 | 120 | 121 | 122 | 123 | :app->:android-lib:navigation 124 | 126 | 128 | 129 | 130 | 131 | :feature:feature-payment->:lib:crypto 132 | 134 | 136 | 137 | 138 | 139 | :feature:feature-payment->:lib:logger 140 | 142 | 144 | 145 | 146 | 147 | :feature:feature-payment->:android-lib:navigation 148 | 150 | 152 | 153 | 154 | 155 | :android-lib:base 156 | 157 | :android-lib:base 159 | 160 | 161 | 162 | 163 | :feature:feature-payment->:android-lib:base 164 | 166 | 168 | 169 | 170 | 171 | :feature:feature-toys->:lib:logger 172 | 174 | 176 | 177 | 178 | 179 | :feature:feature-toys->:android-lib:navigation 180 | 182 | 184 | 185 | 186 | 187 | :feature:feature-toys->:android-lib:base 188 | 190 | 192 | 193 | 194 | 195 | :lib:console-logger->:lib:logger 196 | 198 | 200 | 201 | 202 | 203 | :lib:crypto->:lib:logger 204 | 206 | 208 | 209 | 210 | 211 | :android-lib:logcat-logger->:lib:logger 212 | 214 | 216 | 217 | 218 | 219 | --------------------------------------------------------------------------------