diff --git a/.gitignore b/.gitignore index 4b2cb01..eb1b6a4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,8 @@ local.properties *.iml app/src/main/res/*/*strings.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties diff --git a/.gitmodules b/.gitmodules index aa5ba1d..2346927 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,9 @@ -[submodule "Template-common"] - path = Template-common - url = git@github.com:TouchInstinct/Template-common.git [submodule "RoboSwag"] path = RoboSwag - url = git@github.com:TouchInstinct/RoboSwag.git + url = https://git.svc.touchin.ru/TouchInstinct/RoboSwag.git [submodule "BuildScripts"] path = BuildScripts - url = git@github.com:TouchInstinct/BuildScripts.git + url = https://git.svc.touchin.ru/TouchInstinct/BuildScripts.git +[submodule "common-template"] + path = common-template + url = https://git.svc.touchin.ru/TouchInstinct/common-template.git diff --git a/app/.gitignore b/app/.gitignore index 796b96d..b615a9d 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1 +1,3 @@ /build + +app/src/main/res/*/*strings.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a40f352..c62fc50 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,74 +1,95 @@ plugins { - id(Plugins.ANDROID_APP_PLUGIN_WITH_DEFAULT_CONFIG) - id(Plugins.FIREBASE_CRASH) - id(Plugins.GOOGLE_SERVICES) - id(Plugins.LICENCE_PLUGIN) + id(libs.plugins.android.app.get().pluginId) + alias(libs.plugins.firebase.crashlytics) + alias(libs.plugins.firebase.perf) + id(libs.plugins.google.oss.licenses.plugin.get().pluginId) } val customEndpoint: String? = Environment.ENDPOINT.getenv()?.takeIf(String::isNotBlank) android { + namespace = "ru.touchin.template" + configureSigningConfig(this@Build_gradle::file) with(defaultConfig) { - applicationId = Environment.APP_ID.getenv() ?: AndroidConfig.TEST_APP_ID - signingConfig = signingConfigs.getByName(SigningConfig.CONFIG_NAME) + addResourceConfigurations("ru") } - firebaseCrashlytics { - mappingFileUploadEnabled = true - } + addBuildType(type = BuildType.Develop, project = rootProject) + addBuildType(type = BuildType.Debug, project = rootProject) + addBuildType(type = BuildType.Customer, project = rootProject) + addBuildType(type = BuildType.Release, project = rootProject) - addBuildType(BuildType.Debug, buildScriptDir = buildScriptDir) - addBuildType(BuildType.Release, buildScriptDir = buildScriptDir) + addMobileServicesFlavor() - flavorDimensions( - ApiFlavour.DIMENSION_NAME, - SSLPinningFlavour.DIMENSION_NAME, - TestPanelFlavour.DIMENSION_NAME - ) - - addFlavour(flavour = ApiFlavour.CustomerStage, customEndpoint = customEndpoint) - addFlavour(flavour = ApiFlavour.CustomerProd, customEndpoint = customEndpoint) - - addFlavour(SSLPinningFlavour.OFF) - addFlavour(SSLPinningFlavour.ON) - - addEmptyFlavour(TestPanelFlavour.OFF) - addEmptyFlavour(TestPanelFlavour.ON) - - ignoreCustomerProdFlavourIfReleaseIsDebuggable() -} - -androidExtensions { - features = setOf("parcelize") + ext["languageMap"] = mapOf("ru" to "${rootProject.projectDir}/${AndroidConfig.COMMON_FOLDER}/strings/default_common_strings.json") } dependencies { - androidX() - featureModules() - mvi() - materialDesign() - dagger() - retrofit() - moshi() - navigation() - leakCanary() - sharedPrefs() - chucker() - implementation(Library.FIREBASE_ANAL) - implementation(Library.FIREBASE_CRASH) - implementation(Library.FIREBASE_PERF) - implementation(Library.ANDROIDX_SECURE) - coreNetwork() - coreStrings() - implementationModule(Module.Core.UI) - implementationModule(Module.Core.UTILS) - implementationModule(Module.Core.DATA) - implementationModule(Module.RoboSwag.UTILS) + // AndroidX + implementation(libs.bundles.androidX) + + // KotlinX + implementation(libs.coroutines) + + // UI + implementation(libs.bundles.ui) + + // Lifecycle + implementation(libs.bundles.lifecycle) + kapt(libs.androidx.lifecycle.compiler) + + // Dagger + implementation(libs.bundles.dagger) + kapt(libs.dagger.compiler) + kapt(libs.dagger.assisted.inject.processor) + + // Glide + implementation(libs.glide) + implementation(libs.glide.okhttp3) + kapt(libs.glide.compiler) + + // Retrofit2, OkHttp3 + implementation(libs.retrofit) + implementation(libs.retrofit.converter.moshi) + implementation(libs.okhttp) + implementation(libs.okhttp.logging.interceptor) + + // Moshi + implementation(libs.moshi) + implementation(libs.moshi.kotlin) + kapt(libs.moshi.codegen) + + // Room + implementation(libs.room) + implementation(libs.room.ktx) + kapt(libs.room.compiler) + + // LeakCanary + implementation(libs.leakcanary) + + // Chucker + debugImplementation(libs.chucker.debug) + releaseImplementation(libs.chucker.release) + + // GMS + implementation(platform(libs.firebase.bom)) + implementation(libs.bundles.firebase) + implementation(libs.google.oss.licenses) + + // Security + implementation(libs.androidx.security.crypto) + + // Biometric + implementation(libs.androidx.biometric) + + // Groupie + implementation(libs.groupie) + implementation(libs.groupie.viewbinding) } -apply(from = "$buildScriptDir/gradle/scripts/applicationFileNaming.gradle") +apply(from = "${rootProject.ext["buildScriptsDir"]}/gradle/scripts/stringGenerator.gradle") val Project.buildScriptDir: String get() = rootProject.ext["buildScriptsDir"] as String diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index 89951fd..0000000 --- a/app/google-services.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "project_info": { - "project_number": "1084813714260", - "firebase_url": "https://testproject-ac7fe.firebaseio.com", - "project_id": "testproject-ac7fe", - "storage_bucket": "testproject-ac7fe.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:1084813714260:android:b6d7bb18a0acfe96255ec1", - "android_client_info": { - "package_name": "com.touchin.template" - } - }, - "oauth_client": [ - { - "client_id": "1084813714260-ijq13dkdc1h5i5j87t45tiibl8eg2v9e.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyBVsh_CN-RCfU3LkHuvhLdqVS-ZUJbOljE" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "1084813714260-ijq13dkdc1h5i5j87t45tiibl8eg2v9e.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - } - ], - "configuration_version": "1" -} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fd2e16f..498225a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,6 @@ + xmlns:tools="http://schemas.android.com/tools"> @@ -9,7 +8,7 @@ android:name="ru.touchin.template.App" android:allowBackup="false" android:icon="@mipmap/ic_launcher" - android:label="@string/common_global_app_name" + android:label="@string/common_app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="false" tools:ignore="GoogleAppIndexingWarning"> @@ -18,8 +17,8 @@ android:name="ru.touchin.template.SingleActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme" - android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize" + android:exported="true"> diff --git a/app/src/main/java/ru/touchin/template/App.kt b/app/src/main/java/ru/touchin/template/App.kt index dc4afba..c6ffdc1 100644 --- a/app/src/main/java/ru/touchin/template/App.kt +++ b/app/src/main/java/ru/touchin/template/App.kt @@ -1,25 +1,5 @@ package ru.touchin.template -import me.vponomarenko.injectionmanager.IHasComponent -import me.vponomarenko.injectionmanager.x.XInjectionManager -import ru.touchin.roboswag.navigation_base.TouchinApp -import ru.touchin.template.di.ApplicationComponent -import ru.touchin.template.di.DaggerApplicationComponent +import android.app.Application -class App : TouchinApp(), IHasComponent { - - override fun onCreate() { - super.onCreate() - initDagger() - } - - fun initDagger() { - XInjectionManager.init(this) - XInjectionManager.bindComponent(this) - } - - override fun getComponent(): ApplicationComponent = DaggerApplicationComponent - .factory() - .create(this) - -} +class App : Application() diff --git a/app/src/main/java/ru/touchin/template/SingleActivity.kt b/app/src/main/java/ru/touchin/template/SingleActivity.kt index 5e7a377..e71c1db 100644 --- a/app/src/main/java/ru/touchin/template/SingleActivity.kt +++ b/app/src/main/java/ru/touchin/template/SingleActivity.kt @@ -1,62 +1,5 @@ package ru.touchin.template -import android.os.Bundle -import androidx.activity.OnBackPressedCallback -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.ktx.Firebase -import me.vponomarenko.injectionmanager.x.XInjectionManager -import ru.terrakok.cicerone.NavigatorHolder -import ru.terrakok.cicerone.android.support.SupportAppNavigator -import ru.touchin.roboswag.navigation_base.activities.BaseActivity -import ru.touchin.roboswag.navigation_cicerone.CiceroneTuner -import ru.touchin.template.di.ApplicationComponent -import ru.touchin.template.navigation.MainNavigation -import ru.touchin.template.navigation.StartUpCoordinator -import javax.inject.Inject +import androidx.appcompat.app.AppCompatActivity -// TDOD: change package name everywhere -// TODO: change google play config -class SingleActivity : BaseActivity() { - - @Inject - @MainNavigation - lateinit var navigatorHolder: NavigatorHolder - - @Inject - lateinit var coordinator: StartUpCoordinator - - private lateinit var firebaseAnalytics: FirebaseAnalytics - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - firebaseAnalytics = Firebase.analytics - - setContentView(R.layout.activity_main) - - injectDependencies() - - lifecycle.addObserver( - CiceroneTuner( - navigatorHolder = navigatorHolder, - navigator = SupportAppNavigator(this, R.id.fragment_container) - ) - ) - - coordinator.start() - - onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - coordinator.closeCurrentScreen() - } - }) - } - - private fun injectDependencies() { - XInjectionManager - .findComponent() - .inject(this) - } - -} +class SingleActivity : AppCompatActivity() diff --git a/app/src/main/java/ru/touchin/template/di/ApplicationComponent.kt b/app/src/main/java/ru/touchin/template/di/ApplicationComponent.kt deleted file mode 100644 index a1475a4..0000000 --- a/app/src/main/java/ru/touchin/template/di/ApplicationComponent.kt +++ /dev/null @@ -1,37 +0,0 @@ -package ru.touchin.template.di - -import android.content.Context -import dagger.BindsInstance -import dagger.Component -import ru.terrakok.cicerone.Router -import ru.touchin.template.feature_login.LoginDeps -import ru.touchin.template.network.di.NetworkModule -import ru.touchin.template.App -import ru.touchin.template.SingleActivity -import ru.touchin.template.core_prefs.PreferencesModule -import ru.touchin.template.navigation.MainNavigation -import javax.inject.Singleton - -@Singleton -@Component(modules = [ - ApplicationModule::class, - PreferencesModule::class, - MainNavigationModule::class, - NetworkModule::class, - CoordinatorsImpl::class -]) -interface ApplicationComponent : LoginDeps { - - @MainNavigation - fun router(): Router - - fun inject(application: App) - - fun inject(activity: SingleActivity) - - @Component.Factory - interface Factory { - fun create(@BindsInstance context: Context): ApplicationComponent - } - -} diff --git a/app/src/main/java/ru/touchin/template/di/ApplicationModule.kt b/app/src/main/java/ru/touchin/template/di/ApplicationModule.kt deleted file mode 100644 index 90758d1..0000000 --- a/app/src/main/java/ru/touchin/template/di/ApplicationModule.kt +++ /dev/null @@ -1,28 +0,0 @@ -package ru.touchin.template.di - -import android.content.Context -import com.chuckerteam.chucker.api.ChuckerInterceptor -import dagger.Module -import dagger.Provides -import okhttp3.Interceptor -import ru.touchin.template.network.di.ApiUrl -import ru.touchin.template.network.di.ChuckInterceptor -import ru.touchin.template.network.di.WithSslPinning -import ru.touchin.template.BuildConfig - -@Module -class ApplicationModule { - - @Provides - @ApiUrl - fun provideApiUrl() = BuildConfig.API_URL - - @Provides - @WithSslPinning - fun providePluggerForSsl() = BuildConfig.WithSSLPinning - - @Provides - @ChuckInterceptor - fun provideChucker(context: Context): Interceptor = ChuckerInterceptor(context) - -} diff --git a/app/src/main/java/ru/touchin/template/di/CoordinatorsImpl.kt b/app/src/main/java/ru/touchin/template/di/CoordinatorsImpl.kt deleted file mode 100644 index d43191f..0000000 --- a/app/src/main/java/ru/touchin/template/di/CoordinatorsImpl.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.touchin.template.di - -import dagger.Binds -import dagger.Module -import ru.touchin.template.feature_login.navigation.LoginCoordinator -import ru.touchin.template.navigation.login.LoginCoordinatorImpl - -@Module -abstract class CoordinatorsImpl { - - @Binds - abstract fun loginCoordinator(impl: LoginCoordinatorImpl): LoginCoordinator -} diff --git a/app/src/main/java/ru/touchin/template/di/MainNavigationModule.kt b/app/src/main/java/ru/touchin/template/di/MainNavigationModule.kt deleted file mode 100644 index d7c055d..0000000 --- a/app/src/main/java/ru/touchin/template/di/MainNavigationModule.kt +++ /dev/null @@ -1,27 +0,0 @@ -package ru.touchin.template.di - -import dagger.Module -import dagger.Provides -import ru.terrakok.cicerone.Cicerone -import ru.terrakok.cicerone.NavigatorHolder -import ru.terrakok.cicerone.Router -import ru.touchin.template.navigation.MainNavigation -import javax.inject.Singleton - -@Module -class MainNavigationModule { - - @Provides - @Singleton - @MainNavigation - fun provideCicerone(): Cicerone = Cicerone.create() - - @Provides - @MainNavigation - fun provideNavigatorHolder(@MainNavigation cicerone: Cicerone): NavigatorHolder = cicerone.navigatorHolder - - @Provides - @MainNavigation - fun provideRouter(@MainNavigation cicerone: Cicerone): Router = cicerone.router - -} diff --git a/app/src/main/java/ru/touchin/template/navigation/MainNavigation.kt b/app/src/main/java/ru/touchin/template/navigation/MainNavigation.kt deleted file mode 100644 index e63ac31..0000000 --- a/app/src/main/java/ru/touchin/template/navigation/MainNavigation.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.touchin.template.navigation - -import javax.inject.Qualifier - -@Qualifier -annotation class MainNavigation diff --git a/app/src/main/java/ru/touchin/template/navigation/Screens.kt b/app/src/main/java/ru/touchin/template/navigation/Screens.kt deleted file mode 100644 index 3ed2df7..0000000 --- a/app/src/main/java/ru/touchin/template/navigation/Screens.kt +++ /dev/null @@ -1,13 +0,0 @@ -package ru.touchin.template.navigation - -import androidx.fragment.app.Fragment -import ru.terrakok.cicerone.android.support.SupportAppScreen -import ru.touchin.template.feature_login.presentation.LoginFragment - -object Screens { - - class Login : SupportAppScreen() { - override fun getFragment(): Fragment = LoginFragment() - - } -} diff --git a/app/src/main/java/ru/touchin/template/navigation/StartUpCoordinator.kt b/app/src/main/java/ru/touchin/template/navigation/StartUpCoordinator.kt deleted file mode 100644 index b926336..0000000 --- a/app/src/main/java/ru/touchin/template/navigation/StartUpCoordinator.kt +++ /dev/null @@ -1,18 +0,0 @@ -package ru.touchin.template.navigation - -import ru.terrakok.cicerone.Router -import javax.inject.Inject - -class StartUpCoordinator @Inject constructor( - @MainNavigation private val router: Router -) { - - fun start() { - router.newRootScreen(Screens.Login()) - } - - fun closeCurrentScreen() { - router.exit() - } - -} diff --git a/app/src/main/java/ru/touchin/template/navigation/login/LoginCoordinatorImpl.kt b/app/src/main/java/ru/touchin/template/navigation/login/LoginCoordinatorImpl.kt deleted file mode 100644 index f7ed3a7..0000000 --- a/app/src/main/java/ru/touchin/template/navigation/login/LoginCoordinatorImpl.kt +++ /dev/null @@ -1,16 +0,0 @@ -package ru.touchin.template.navigation.login - -import ru.terrakok.cicerone.Router -import ru.touchin.template.feature_login.navigation.LoginCoordinator -import ru.touchin.template.navigation.MainNavigation -import javax.inject.Inject - -class LoginCoordinatorImpl @Inject constructor( - @MainNavigation private val router: Router -) : LoginCoordinator { - - override fun openMainScreen() { - router.exit() - } - -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml deleted file mode 100644 index 8b13ff6..0000000 --- a/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - Template - diff --git a/build.gradle.kts b/build.gradle.kts index e3b60e9..2c88a96 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,43 +1,16 @@ buildscript { repositories { - mavenCentral() google() - jcenter() + mavenCentral() maven("https://plugins.gradle.org/m2/") } + dependencies { - classpath("com.android.tools.build:gradle:${Version.ANDROID_PLUGIN}") - classpath(kotlin("gradle-plugin", version = Version.KOTLIN)) - classpath("com.google.gms:google-services:${Version.GOOGLE_SERVICES_PLUGIN}") - classpath("com.google.firebase:firebase-crashlytics-gradle:${Version.FIREBASE_CRASH_PLUGIN}") - classpath("com.vanniktech:gradle-dependency-graph-generator-plugin:0.5.0") - classpath("com.google.android.gms:oss-licenses-plugin:0.10.2") + classpath(libs.android.gradle.plugin) + classpath(libs.kotlin.gradle.plugin) + classpath(libs.google.oss.licenses.plugin) } } -plugins { - id(Plugins.DEPENDENCY_GRAPH).version("0.5.0") - id("static-analysis-android") -} - -allprojects { - repositories { - google() - jcenter() - maven("http://dl.bintray.com/touchin/touchin-tools") - maven("https://jitpack.io") - } -} - -subprojects { - apply(plugin = Plugins.DETEKT) -} - val buildScriptsDir = "${rootProject.projectDir}/BuildScripts" ext["buildScriptsDir"] = buildScriptsDir - -apply(plugin = Plugins.DEPENDENCY_GRAPH) - -staticAnalysis { - buildScriptDir = buildScriptsDir -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index bda1b11..9c2bd80 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,14 +1,11 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { `kotlin-dsl` `kotlin-dsl-precompiled-script-plugins` - kotlin("jvm") version embeddedKotlinVersion } // The kotlin-dsl plugin requires a repository to be declared repositories { - jcenter() + mavenCentral() google() maven { url = uri("https://plugins.gradle.org/m2/") @@ -16,18 +13,7 @@ repositories { } dependencies { - // android gradle plugin, required by custom plugin - implementation("com.android.tools.build:gradle:4.0.0") - - // kotlin plugin, required by custom plugin - implementation(kotlin("gradle-plugin", embeddedKotlinVersion)) - - gradleKotlinDsl() - implementation(kotlin("stdlib-jdk8")) + implementation(libs.android.gradle.plugin) + implementation(libs.kotlin.gradle.plugin) } - -val compileKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions { - jvmTarget = "1.8" -} diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 0000000..7743895 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,10 @@ +dependencyResolutionManagement { + repositories { + mavenCentral() + } + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/AndroidConfig.kt b/buildSrc/src/main/kotlin/AndroidConfig.kt index e44b5af..2a53f04 100644 --- a/buildSrc/src/main/kotlin/AndroidConfig.kt +++ b/buildSrc/src/main/kotlin/AndroidConfig.kt @@ -1,29 +1,14 @@ import com.android.build.gradle.BaseExtension object AndroidConfig { - const val COMPILE_SDK_VERSION = 29 - const val MIN_SDK_VERSION = 23 - const val TARGET_SDK_VERSION = 29 - const val BUILD_TOOLS_VERSION = "29.0.2" - - val VERSION_CODE: Int = Environment.BUILD_NUMBER.getenv()?.toIntOrNull() ?: 10000 - const val VERSION_NAME = "1.0.0" // TODO: change test package name - const val TEST_APP_ID = "com.touchin.template" + const val TEST_APP_ID = "ru.touchin.template" // TODO: change common file folder - const val COMMON_FOLDER = "Template-common" - - const val RELEASE_DEBUGGABLE = false - -} - -fun BaseExtension.ignoreCustomerProdFlavourIfReleaseIsDebuggable() { - variantFilter { - ignore = name.contains(ApiFlavour.CustomerProd.name, ignoreCase = true) && AndroidConfig.RELEASE_DEBUGGABLE - } + const val COMMON_FOLDER = "common-template" } + diff --git a/buildSrc/src/main/kotlin/BuildType.kt b/buildSrc/src/main/kotlin/BuildType.kt new file mode 100644 index 0000000..49251c1 --- /dev/null +++ b/buildSrc/src/main/kotlin/BuildType.kt @@ -0,0 +1,135 @@ +import com.android.build.gradle.BaseExtension +import org.gradle.api.Project +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.kotlin.dsl.extra +import versioncatalog.versionName + +fun BaseExtension.addFlavors(dimensionName: String, vararg flavorNames: String) { + if (flavorNames.isEmpty()) return + + flavorDimensions(dimensionName) + + flavorNames.forEach { flavor -> + productFlavors { + create(flavor) { + dimension = dimensionName + } + } + } +} + +fun BaseExtension.addMobileServicesFlavor() { + addFlavors(dimensionName = "mobileServices", flavorNames = arrayOf("huawei", "google")) +} + +fun BaseExtension.addBuildType( + type: BuildType, + project: Project, +) { + buildTypes { + maybeCreate(type.name) + getByName(type.name) { + + isDebuggable = !type.optimizeAndObfuscate + isMinifyEnabled = type.optimizeAndObfuscate + isShrinkResources = type.optimizeAndObfuscate + setMatchingFallbacks(type.matchingFallbacks) + + if (listOf(BuildType.Develop, BuildType.Debug).contains(type)) { + applicationIdSuffix = ".${type.name}" + } + + if (type.optimizeAndObfuscate) { + setProguardFiles( + listOf( + project.file("proguard").listFiles(), + getDefaultProguardFile("proguard-android-optimize.txt") + ).filterNotNull() + ) + } + + extra.set("enableCrashlytics", type.enableCrashlytics) + + buildConfigField("Boolean", "ENABLE_SSL_PINNING", type.enableSslPinning.toString()) + buildConfigField("Boolean", "ENABLE_LOGS", type.enabledLogs.toString()) + buildConfigField("Boolean", "ENABLE_DEBUG_PANEL", type.enabledDebugPanel.toString()) + } + } +} + +fun BaseExtension.addLibBuildType( + type: BuildType, + serverType: String? = null, + enableConfig: Boolean = false, + versionCatalog: VersionCatalog +) { + buildTypes { + maybeCreate(type.name) + getByName(type.name) { + isDebuggable = !type.optimizeAndObfuscate + isMinifyEnabled = type.optimizeAndObfuscate + setMatchingFallbacks(type.matchingFallbacks) + buildConfigField("String", "VERSION_NAME", "\"${versionCatalog.versionName}\"") + if (enableConfig) { + val server = serverType ?: type.serverType + buildConfigField("ru.template.data.network.ServerUrl", "DEFAULT_SERVER", type.defaultServer) + buildConfigField("String", "DEFAULT_SERVER_TYPE", "\"$server\"") + buildConfigField("Boolean", "ENABLE_SSL_PINNING", type.enableSslPinning.toString()) + buildConfigField("Boolean", "ENABLE_LOGS", type.enabledLogs.toString()) + } + } + } +} + +sealed class BuildType( + val name: String, + val optimizeAndObfuscate: Boolean, + val enableSslPinning: Boolean, + val enabledLogs: Boolean, + val enabledDebugPanel: Boolean, + val enableCrashlytics: Boolean = true, + val defaultServer: String = "ru.template.data.network.ServerUrl.CUSTOMER_TEST", + val serverType: String, + val matchingFallbacks: String = "debug", +) { + object Develop : BuildType( + name = "develop", + optimizeAndObfuscate = false, + enableSslPinning = false, + enabledLogs = true, + enabledDebugPanel = true, + enableCrashlytics = false, + serverType = "Test", + ) + + object Debug : BuildType( + name = "debug", + optimizeAndObfuscate = false, + enableSslPinning = false, + enabledLogs = true, + enabledDebugPanel = true, + serverType = "Test", + ) + + object Customer : BuildType( + name = "customer", + optimizeAndObfuscate = true, + enableSslPinning = true, + enabledLogs = false, + enabledDebugPanel = false, + defaultServer = "ru.template.data.network.ServerUrl.CUSTOMER_PROD", + serverType = "Prod", + matchingFallbacks = "release" + ) + + object Release : BuildType( + name = "release", + optimizeAndObfuscate = true, + enableSslPinning = true, + enabledLogs = false, + enabledDebugPanel = false, + defaultServer = "ru.template.data.network.ServerUrl.CUSTOMER_PROD", + serverType = "Prod", + matchingFallbacks = "release" + ) +} diff --git a/buildSrc/src/main/kotlin/DependencyHandler.kt b/buildSrc/src/main/kotlin/DependencyHandler.kt deleted file mode 100644 index 14b51d0..0000000 --- a/buildSrc/src/main/kotlin/DependencyHandler.kt +++ /dev/null @@ -1,145 +0,0 @@ -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.dsl.DependencyHandler - -fun DependencyHandler.fragment() { - implementation(Library.ANDROIDX_FRAGMENT) - implementation(Library.ANDROIDX_FRAGMENT_KTX) - implementationModule(Module.RoboSwag.NAVIGATION_BASE) -} - -fun DependencyHandler.materialDesign() { - implementation(Library.ANDROID_MATERIAL) - implementation(Library.SWIPE_TO_REFRESH) -} - -fun DependencyHandler.permissionDispatcher() { - implementation(Library.PERMISSION_DISPATCHER) - kapt(Library.PERMISSION_DISPATCHER_ANNOTATION_PROCESSOR) -} - -fun DependencyHandler.constraintLayout() { - implementation(Library.ANDROIDX_CONSTRAINT) -} - -fun DependencyHandler.androidX() { - implementation(Library.ANDROIDX_CORE) - implementation(Library.ANDROIDX_APPCOMPAT) - implementationModule(Module.RoboSwag.KOTLIN_EXTENSIONS) -} - -fun DependencyHandler.recyclerView() { - implementation(Library.ANDROIDX_RECYCLER) - implementationModule(Module.RoboSwag.RECYCLER_VIEW_ADAPTERS) -} - -fun DependencyHandler.kotlinStd() { - implementation(Library.KOTLIN_STDLIB) -} - -fun DependencyHandler.navigation() { - implementation(Library.CICERONE) - implementationModule(Module.RoboSwag.NAVIGATION_CICERONE) -} - -fun DependencyHandler.featureModules() { - Module.Feature.ALL.forEach(this::implementationModule) -} - -fun DependencyHandler.mvi() { - implementationModule(Module.RoboSwag.MVI_ARCH) - fragment() - lifecycle() -} - -fun DependencyHandler.coreNetwork() { - implementationModule(Module.Core.NETWORK) -} - -fun DependencyHandler.coreStrings() { - implementationModule(Module.Core.STRINGS) -} - -fun DependencyHandler.retrofit() { - implementation(Library.RETROFIT) - implementation(Library.OKHTTP_LOGGING_INTERCEPTOR) - implementation(Library.OKHTTP) - implementation(Library.MOSHI_RETROFIT) -} - -fun DependencyHandler.dagger(withAssistedInject: Boolean = true) { - implementation(Library.DAGGER) - kapt(Library.DAGGER_COMPILER) - implementation(Library.DAGGER_COMPONENT_MANAGER) - - if (withAssistedInject) { - compileOnly(Library.DAGGER_INJECT_ASSISTED_ANNOTATIONS) - kapt(Library.DAGGER_INJECT_ASSISTED_PROCESSOR) - } -} - -fun DependencyHandler.glide() { - implementation(Library.GLIDE) - implementation(Library.GLIDE_OKHTTP_INTEGRATION) - kapt(Library.GLIDE_COMPILER) -} - -fun DependencyHandler.moshi() { - implementation(Library.MOSHI) - kapt(Library.MOSHI_CODEGEN) -} - -fun DependencyHandler.lifecycle() { - implementation(Library.ANDROID_LIFECYCLE_EXTENSIONS) - implementation(Library.ANDROID_LIFECYCLE_VIEW_MODEL_EXTENSIONS) - implementation(Library.ANDROID_LIFECYCLE_LIVE_DATA_EXTENSIONS) - implementationModule(Module.RoboSwag.LIFECYCLE) -} - -fun DependencyHandler.coroutines() { - implementation(Library.COROUTINES_CORE) - implementation(Library.COROUTINES_ANDROID) -} - -fun DependencyHandler.leakCanary() { - add("withTestPanelImplementation", Library.LEAK_CANARY) -} - -fun DependencyHandler.sharedPrefs() { - implementationModule(Module.RoboSwag.STORABLE) - implementationModule(Module.Core.PREFS) -} - -fun DependencyHandler.chucker() { - add("withTestPanelImplementation", Library.CHUCKER) - add("withoutTestPanelImplementation", Library.CHUCKER_NO_OP) -} - -fun DependencyHandler.implementationModule(moduleName: String) { - implementation(project(":$moduleName")) -} - -private fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? = - add("implementation", dependencyNotation) - -private fun DependencyHandler.kapt(dependencyNotation: Any): Dependency? = - add("kapt", dependencyNotation) - -private fun DependencyHandler.compileOnly(dependencyNotation: Any): Dependency? = - add("compileOnly", dependencyNotation) - -private fun DependencyHandler.project( - path: String, - configuration: String? = null -): ProjectDependency { - val notation = if (configuration != null) { - mapOf("path" to path, "configuration" to configuration) - } else { - mapOf("path" to path) - } - - return uncheckedCast(project(notation)) -} - -@Suppress("unchecked_cast", "nothing_to_inline", "detekt.UnsafeCast") -private inline fun uncheckedCast(obj: Any?): T = obj as T diff --git a/buildSrc/src/main/kotlin/Environment.kt b/buildSrc/src/main/kotlin/Environment.kt index 8239642..f90f836 100644 --- a/buildSrc/src/main/kotlin/Environment.kt +++ b/buildSrc/src/main/kotlin/Environment.kt @@ -1,13 +1,12 @@ object Environment { const val APP_ID = "BUNDLE_ID" - const val STORE_PASSWORD = "STORE_PASSWORD" - const val KEY_ALIAS = "KEY_ALIAS" + const val ALIAS = "ALIAS" const val KEY_PASSWORD = "KEY_PASSWORD" - const val ENDPOINT = "CUSTOM_ENDPOINT" - const val BUILD_NUMBER = "BUILD_NUMBER" + const val SERVER_ENVIRONMENT = "SERVER_ENVIRONMENT" + const val BUILD_TYPE = "BUILD_TYPE" } fun String.getenv(): String? = System.getenv(this) diff --git a/buildSrc/src/main/kotlin/Library.kt b/buildSrc/src/main/kotlin/Library.kt deleted file mode 100644 index 70d409f..0000000 --- a/buildSrc/src/main/kotlin/Library.kt +++ /dev/null @@ -1,60 +0,0 @@ -object Library { - const val KOTLIN_STDLIB = "org.jetbrains.kotlin:kotlin-stdlib:${Version.KOTLIN}" - - const val ANDROIDX_APPCOMPAT = "androidx.appcompat:appcompat:${Version.ANDROIDX_APPCOMPAT}" - const val ANDROIDX_CORE = "androidx.core:core-ktx:${Version.ANDROIDX_CORE}" - const val ANDROIDX_RECYCLER = "androidx.recyclerview:recyclerview:${Version.ANDROIDX}" - const val ANDROIDX_CONSTRAINT = "androidx.constraintlayout:constraintlayout:${Version.ANDROIDX_CONSTRAINT}" - - const val ANDROIDX_FRAGMENT = "androidx.fragment:fragment:${Version.ANDROIDX_FRAGMENT}" - const val ANDROIDX_FRAGMENT_KTX = "androidx.fragment:fragment-ktx:${Version.ANDROIDX_FRAGMENT}" - - const val ANDROID_MATERIAL = "com.google.android.material:material:${Version.ANDROID_MATERIAL}" - const val SWIPE_TO_REFRESH = "androidx.swiperefreshlayout:swiperefreshlayout:${Version.SWIPE_TO_REFRESH}" - - const val PERMISSION_DISPATCHER = "org.permissionsdispatcher:permissionsdispatcher:${Version.PERMISSION_DISPATCHER}" - const val PERMISSION_DISPATCHER_ANNOTATION_PROCESSOR = "org.permissionsdispatcher:permissionsdispatcher-processor:${Version.PERMISSION_DISPATCHER}" - - const val ANDROID_LIFECYCLE_EXTENSIONS = "androidx.lifecycle:lifecycle-extensions:${Version.ANDROID_LIFECYCLE}" - const val ANDROID_LIFECYCLE_VIEW_MODEL_EXTENSIONS = "androidx.lifecycle:lifecycle-viewmodel-ktx:${Version.ANDROID_LIFECYCLE}" - const val ANDROID_LIFECYCLE_LIVE_DATA_EXTENSIONS = "androidx.lifecycle:lifecycle-livedata-ktx:${Version.ANDROID_LIFECYCLE}" - - const val DAGGER = "com.google.dagger:dagger:${Version.DAGGER}" - const val DAGGER_COMPILER = "com.google.dagger:dagger-compiler:${Version.DAGGER}" - - const val DAGGER_INJECT_ASSISTED_ANNOTATIONS = "com.squareup.inject:assisted-inject-annotations-dagger2:${Version.DAGGER_INJECT_ASSISTED}" - const val DAGGER_INJECT_ASSISTED_PROCESSOR = "com.squareup.inject:assisted-inject-processor-dagger2:${Version.DAGGER_INJECT_ASSISTED}" - - const val DAGGER_COMPONENT_MANAGER = "com.github.valeryponomarenko.componentsmanager:androidx:${Version.DAGGER_COMPONENT_MANAGER}" - - const val GLIDE = "com.github.bumptech.glide:glide:${Version.GLIDE}" - const val GLIDE_COMPILER = "com.github.bumptech.glide:compiler:${Version.GLIDE}" - const val GLIDE_OKHTTP_INTEGRATION = "com.github.bumptech.glide:okhttp3-integration:${Version.GLIDE}" - - const val RETROFIT = "com.squareup.retrofit2:retrofit:${Version.RETROFIT}" - - const val OKHTTP_LOGGING_INTERCEPTOR = "com.squareup.okhttp3:logging-interceptor:${Version.OKHTTP}" - const val OKHTTP = "com.squareup.okhttp3:okhttp-urlconnection:${Version.OKHTTP}" - - const val MOSHI = "com.squareup.moshi:moshi:${Version.MOSHI}" - const val MOSHI_CODEGEN = "com.squareup.moshi:moshi-kotlin-codegen:${Version.MOSHI}" - const val MOSHI_RETROFIT = "com.squareup.retrofit2:converter-moshi:${Version.RETROFIT}" - - const val COROUTINES_CORE = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${Version.COROUTINES}" - const val COROUTINES_ANDROID = "org.jetbrains.kotlinx:kotlinx-coroutines-android:${Version.COROUTINES}" - - const val CICERONE = "ru.terrakok.cicerone:cicerone:${Version.CICERONE}" - - const val LEAK_CANARY = "com.squareup.leakcanary:leakcanary-android:${Version.LEAK_CANARY}" - const val CHUCKER = "com.github.chuckerteam.chucker:library:${Version.CHUCKER}" - const val CHUCKER_NO_OP = "com.github.chuckerteam.chucker:library-no-op:${Version.CHUCKER}" - - const val FIREBASE_ANAL = "com.google.firebase:firebase-analytics-ktx:${Version.FIREBASE_ANAL}" - const val FIREBASE_PERF = "com.google.firebase:firebase-perf:${Version.FIREBASE_PERF}" - const val FIREBASE_CRASH = "com.google.firebase:firebase-crashlytics:${Version.FIREBASE_CRASH}" - - const val ANDROIDX_SECURE = "androidx.security:security-crypto:${Version.ANDROIDX_SECURE}" - const val ANDROIDX_BIOMETRIC = "androidx.biometric:biometric:${Version.ANDROIDX_BIOMETRIC}" - - const val LICENCE_LIBRARY = "com.google.android.gms:play-services-oss-licenses:${Version.LICENCE_LIBRARY}" -} diff --git a/buildSrc/src/main/kotlin/Module.kt b/buildSrc/src/main/kotlin/Module.kt deleted file mode 100644 index cc856e0..0000000 --- a/buildSrc/src/main/kotlin/Module.kt +++ /dev/null @@ -1,35 +0,0 @@ -object Module { - - object RoboSwag { - const val UTILS = "utils" - const val LOGGING = "logging" - const val MVI_ARCH = "mvi-arch" - const val NAVIGATION_BASE = "navigation-base" - const val NAVIGATION_CICERONE = "navigation-cicerone" - const val STORABLE = "storable" - const val LIFECYCLE = "lifecycle" - const val VIEWS = "views" - const val RECYCLER_VIEW_ADAPTERS = "recyclerview-adapters" - const val RECYCLER_VIEW_DECORATORS = "recyclerview-decorators" - const val KOTLIN_EXTENSIONS = "kotlin-extensions" - } - - object Feature { - const val LOGIN = "feature_login" - - val ALL = listOf( - LOGIN - ) - } - - object Core { - const val NETWORK = "core_network" - const val PREFS = "core_prefs" - const val STRINGS = "core_strings" - const val UTILS = "core_utils" - const val UI = "core_ui" - const val DATA = "core_data" - const val DOMAIN = "core_domain" - } - -} diff --git a/buildSrc/src/main/kotlin/Plugins.kt b/buildSrc/src/main/kotlin/Plugins.kt deleted file mode 100644 index 10f75e6..0000000 --- a/buildSrc/src/main/kotlin/Plugins.kt +++ /dev/null @@ -1,20 +0,0 @@ -object Plugins { - const val ANDROID_APPLICATION = "com.android.application" - const val ANDROID_LIBRARY = "com.android.library" - - const val ANDROID_APP_PLUGIN_WITH_DEFAULT_CONFIG = "android_app" - const val ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG = "android_lib" - - const val KOTLIN_ANDROID = "kotlin-android" - const val KOTLIN_ANDROID_EXTENSIONS = "kotlin-android-extensions" - const val KOTLIN_KAPT = "kotlin-kapt" - const val LICENCE_PLUGIN = "com.google.android.gms.oss-licenses-plugin" - - const val GOOGLE_SERVICES = "com.google.gms.google-services" - const val FIREBASE_CRASH = "com.google.firebase.crashlytics" - - const val DEPENDENCY_GRAPH = "com.vanniktech.dependency.graph.generator" - - const val DETEKT = "io.gitlab.arturbosch.detekt" - const val CPD = "de.aaschmid.cpd" -} diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt deleted file mode 100644 index 22db47b..0000000 --- a/buildSrc/src/main/kotlin/Version.kt +++ /dev/null @@ -1,47 +0,0 @@ -object Version { - - const val ANDROID_PLUGIN = "4.0.0" - - const val KOTLIN = "1.3.72" - - const val ANDROIDX = "1.1.0" - const val ANDROIDX_CORE = "1.2.0" - const val ANDROIDX_APPCOMPAT = "1.0.2" - const val ANDROIDX_CONSTRAINT = "2.0.0-beta4" - const val ANDROIDX_FRAGMENT = "1.2.1" - const val ANDROIDX_SECURE = "1.0.0-rc02" - const val ANDROIDX_BIOMETRIC = "1.0.1" - - const val ANDROID_MATERIAL = "1.2.0-rc01" - const val SWIPE_TO_REFRESH = "1.0.0" - const val ANDROID_LIFECYCLE = "2.2.0" - - const val PERMISSION_DISPATCHER = "4.8.0" - - const val DAGGER = "2.27" - const val DAGGER_INJECT_ASSISTED = "0.5.2" - const val DAGGER_COMPONENT_MANAGER = "2.1.0" - - const val GLIDE = "4.10.0" - - const val RETROFIT = "2.8.1" - const val OKHTTP = "3.14.1" - - const val MOSHI = "1.9.2" - - const val COROUTINES = "1.3.7" - - const val CICERONE = "5.1.0" - - const val FIREBASE_ANAL = "17.4.3" - const val FIREBASE_CRASH = "17.1.0" - const val FIREBASE_PERF = "19.0.7" - const val GOOGLE_SERVICES_PLUGIN = "4.3.3" - const val FIREBASE_CRASH_PLUGIN = "2.2.0" - - const val LEAK_CANARY = "2.4" - const val CHUCKER = "3.2.0" - - const val LICENCE_LIBRARY = "17.0.0" - -} diff --git a/buildSrc/src/main/kotlin/flavours/BuildType.kt b/buildSrc/src/main/kotlin/flavours/BuildType.kt deleted file mode 100644 index c6193a5..0000000 --- a/buildSrc/src/main/kotlin/flavours/BuildType.kt +++ /dev/null @@ -1,41 +0,0 @@ -import com.android.build.gradle.BaseExtension - -fun BaseExtension.addBuildType( - type: BuildType, - buildScriptDir: String -) { - buildTypes { - getByName(type.name) { - isMinifyEnabled = type.optimizeAndObfuscate - isShrinkResources = type.optimizeAndObfuscate - if (type.optimizeAndObfuscate) { - val proguardFile = if (AndroidConfig.RELEASE_DEBUGGABLE) "noObfuscate.pro" else "obfuscate.pro" - setProguardFiles(listOfNotNull( - getDefaultProguardFile("proguard-android-optimize.txt"), - "$buildScriptDir/proguard/$proguardFile", - "proguard/projectConfig.pro" - )) - } - } - } -} - -sealed class BuildType( - val name: String, - val optimizeAndObfuscate: Boolean -) { - - object Debug : BuildType( - name = "debug", - optimizeAndObfuscate = false - ) - - object Release : BuildType( - name = "release", - optimizeAndObfuscate = true - ) - -} - - - diff --git a/buildSrc/src/main/kotlin/flavours/SSLPinningFlavour.kt b/buildSrc/src/main/kotlin/flavours/SSLPinningFlavour.kt index 99fd78f..f43cd68 100644 --- a/buildSrc/src/main/kotlin/flavours/SSLPinningFlavour.kt +++ b/buildSrc/src/main/kotlin/flavours/SSLPinningFlavour.kt @@ -11,7 +11,7 @@ sealed class SSLPinningFlavour( object OFF : SSLPinningFlavour( name = "withoutSSLPinning", - withSslPinning = true + withSslPinning = false ) object ON : SSLPinningFlavour( diff --git a/buildSrc/src/main/kotlin/flavours/SigningConfig.kt b/buildSrc/src/main/kotlin/flavours/SigningConfig.kt index 31374cb..e471374 100644 --- a/buildSrc/src/main/kotlin/flavours/SigningConfig.kt +++ b/buildSrc/src/main/kotlin/flavours/SigningConfig.kt @@ -15,7 +15,7 @@ fun BaseExtension.configureSigningConfig(getRelativeFile: (String) -> File) { create(SigningConfig.CONFIG_NAME) { storeFile = getRelativeFile(SigningConfig.PATH_TO_KEYSTORE_FILE) storePassword = Environment.STORE_PASSWORD.getenv() ?: SigningConfig.DEFAULT_STORE_PASSWORD - keyAlias = Environment.KEY_ALIAS.getenv() ?: SigningConfig.DEFAULT_KEY_ALIAS + keyAlias = Environment.ALIAS.getenv() ?: SigningConfig.DEFAULT_KEY_ALIAS keyPassword = Environment.KEY_PASSWORD.getenv() ?: SigningConfig.DEFAULT_KEY_PASSWORD } } diff --git a/buildSrc/src/main/kotlin/plugins/AndroidAppPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidAppPlugin.kt index 9d722c5..7c2d004 100644 --- a/buildSrc/src/main/kotlin/plugins/AndroidAppPlugin.kt +++ b/buildSrc/src/main/kotlin/plugins/AndroidAppPlugin.kt @@ -1,12 +1,12 @@ package plugins -import Plugins +import versioncatalog.androidApplicationPlugin import org.gradle.api.Project class AndroidAppPlugin : BaseAndroidPlugin() { override fun apply(target: Project) { - target.plugins.apply(Plugins.ANDROID_APPLICATION) + target.plugins.apply(target.libs.androidApplicationPlugin) super.apply(target) } diff --git a/buildSrc/src/main/kotlin/plugins/AndroidLibPlugin.kt b/buildSrc/src/main/kotlin/plugins/AndroidLibPlugin.kt index ca976ba..f3dbb4b 100644 --- a/buildSrc/src/main/kotlin/plugins/AndroidLibPlugin.kt +++ b/buildSrc/src/main/kotlin/plugins/AndroidLibPlugin.kt @@ -1,12 +1,12 @@ package plugins -import Plugins import org.gradle.api.Project +import versioncatalog.androidLibraryPlugin class AndroidLibPlugin : BaseAndroidPlugin() { override fun apply(target: Project) { - target.plugins.apply(Plugins.ANDROID_LIBRARY) + target.plugins.apply(target.libs.androidLibraryPlugin) super.apply(target) } diff --git a/buildSrc/src/main/kotlin/plugins/BaseAndroidPlugin.kt b/buildSrc/src/main/kotlin/plugins/BaseAndroidPlugin.kt index 8983be3..34f9e6d 100644 --- a/buildSrc/src/main/kotlin/plugins/BaseAndroidPlugin.kt +++ b/buildSrc/src/main/kotlin/plugins/BaseAndroidPlugin.kt @@ -1,18 +1,24 @@ package plugins -import AndroidConfig -import BuildType -import Plugins import com.android.build.gradle.BaseExtension -import kotlinStd +import versioncatalog.jvmBytecode import org.gradle.api.JavaVersion import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.VersionCatalog +import org.gradle.api.artifacts.VersionCatalogsExtension import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.getByType import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import versioncatalog.compileSdk +import versioncatalog.kotlinAndroidPlugin +import versioncatalog.kotlinKaptPlugin +import versioncatalog.minSdk +import versioncatalog.targetSdk +import versioncatalog.versionCode +import versioncatalog.versionName abstract class BaseAndroidPlugin : Plugin { override fun apply(target: Project) { @@ -22,51 +28,46 @@ abstract class BaseAndroidPlugin : Plugin { } private fun Project.configurePlugins() { - plugins.apply(Plugins.KOTLIN_ANDROID) - plugins.apply(Plugins.KOTLIN_ANDROID_EXTENSIONS) - plugins.apply(Plugins.KOTLIN_KAPT) + plugins.apply(libs.kotlinAndroidPlugin) + plugins.apply(libs.kotlinKaptPlugin) } private fun Project.configureAndroid() = extensions.getByType().run { - compileSdkVersion(AndroidConfig.COMPILE_SDK_VERSION) - buildToolsVersion = AndroidConfig.BUILD_TOOLS_VERSION + compileSdkVersion(libs.compileSdk.toInt()) defaultConfig { - minSdkVersion(AndroidConfig.MIN_SDK_VERSION) - targetSdkVersion(AndroidConfig.TARGET_SDK_VERSION) - versionCode = AndroidConfig.VERSION_CODE - versionName = AndroidConfig.VERSION_NAME + minSdk = libs.minSdk.toInt() + targetSdk = libs.targetSdk.toInt() + versionCode = libs.versionCode.toInt() + versionName = libs.versionName } compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - coreLibraryDesugaringEnabled = true + sourceCompatibility = JavaVersion.toVersion(libs.jvmBytecode) + targetCompatibility = JavaVersion.toVersion(libs.jvmBytecode) + isCoreLibraryDesugaringEnabled = true } - buildFeatures.viewBinding = true + buildFeatures.apply { + buildConfig = true + viewBinding = true + } tasks.withType(KotlinCompile::class.java) { kotlinOptions { - jvmTarget = "1.8" - } - } - - if (AndroidConfig.RELEASE_DEBUGGABLE) { - buildTypes { - getByName(BuildType.Release.name) { - isDebuggable = true - } + jvmTarget = libs.jvmBytecode } } } private fun Project.configureDependencies() = dependencies { implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) - kotlinStd() - add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:1.0.5") + add("coreLibraryDesugaring", "com.android.tools:desugar_jdk_libs:2.0.4") } private fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? = add("implementation", dependencyNotation) } + +internal val Project.libs: VersionCatalog + get() = extensions.getByType().named("libs") diff --git a/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogLibraries.kt b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogLibraries.kt new file mode 100644 index 0000000..aa630e5 --- /dev/null +++ b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogLibraries.kt @@ -0,0 +1,6 @@ +package versioncatalog + +import org.gradle.api.artifacts.VersionCatalog + +private fun VersionCatalog.getLibrary(library: String) = findLibrary(library).get() + diff --git a/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogPlugins.kt b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogPlugins.kt new file mode 100644 index 0000000..2c74f0b --- /dev/null +++ b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogPlugins.kt @@ -0,0 +1,15 @@ +package versioncatalog + +import org.gradle.api.artifacts.VersionCatalog + +val VersionCatalog.androidApplicationPlugin: String + get() = findPlugin("android-application").get().orNull?.pluginId.toString() + +val VersionCatalog.androidLibraryPlugin: String + get() = findPlugin("android-library").get().orNull?.pluginId.toString() + +val VersionCatalog.kotlinAndroidPlugin: String + get() = findPlugin("kotlin-android").get().orNull?.pluginId.toString() + +val VersionCatalog.kotlinKaptPlugin: String + get() = findPlugin("kotlin-kapt").get().orNull?.pluginId.toString() diff --git a/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogVersions.kt b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogVersions.kt new file mode 100644 index 0000000..f97febb --- /dev/null +++ b/buildSrc/src/main/kotlin/versioncatalog/VersionCatalogVersions.kt @@ -0,0 +1,21 @@ +package versioncatalog + +import org.gradle.api.artifacts.VersionCatalog + +val VersionCatalog.compileSdk: String + get() = findVersion("compileSdk").get().requiredVersion + +val VersionCatalog.minSdk: String + get() = findVersion("minSdk").get().requiredVersion + +val VersionCatalog.targetSdk: String + get() = findVersion("targetSdk").get().requiredVersion + +val VersionCatalog.jvmBytecode: String + get() = findVersion("jvmBytecode").get().requiredVersion + +val VersionCatalog.versionCode: String + get() = findVersion("versionCode").get().requiredVersion + +val VersionCatalog.versionName: String + get() = findVersion("versionName").get().requiredVersion diff --git a/core/core_data/build.gradle.kts b/core/core_data/build.gradle.kts deleted file mode 100644 index 6b554b4..0000000 --- a/core/core_data/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} diff --git a/core/core_data/src/main/AndroidManifest.xml b/core/core_data/src/main/AndroidManifest.xml deleted file mode 100644 index 3fca1ad..0000000 --- a/core/core_data/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_domain/build.gradle.kts b/core/core_domain/build.gradle.kts deleted file mode 100644 index 6b554b4..0000000 --- a/core/core_domain/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} diff --git a/core/core_domain/src/main/AndroidManifest.xml b/core/core_domain/src/main/AndroidManifest.xml deleted file mode 100644 index 573c1d4..0000000 --- a/core/core_domain/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_network/.gitignore b/core/core_network/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/core/core_network/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/core/core_network/build.gradle.kts b/core/core_network/build.gradle.kts deleted file mode 100644 index 7bebdca..0000000 --- a/core/core_network/build.gradle.kts +++ /dev/null @@ -1,20 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -// id("api-generator-android") -} - -// TODO: uncomment api generator -//apiGenerator { -// pathToApiSchemes = "${AndroidConfig.COMMON_FOLDER}/api" -// outputPackageName = AndroidConfig.TEST_APP_ID -// outputLanguage = apigen.OutputLanguage.KotlinAndroid( -// methodOutputType = apigen.MethodOutputType.Coroutine -// ) -//} - -dependencies { - retrofit() - dagger() - moshi() - coroutines() -} diff --git a/core/core_network/src/main/AndroidManifest.xml b/core/core_network/src/main/AndroidManifest.xml deleted file mode 100644 index 7adec17..0000000 --- a/core/core_network/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_network/src/main/java/ru/touchin/template/network/ResponseBodyExt.kt b/core/core_network/src/main/java/ru/touchin/template/network/ResponseBodyExt.kt deleted file mode 100644 index 3cbe4d6..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/ResponseBodyExt.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ru.touchin.template.network - -import okhttp3.ResponseBody -import java.nio.charset.Charset - -fun ResponseBody.cloneBody(): String? = source() - .also { it.request(Long.MAX_VALUE) } - .buffer - ?.clone() - ?.readString(Charset.forName("UTF-8")) diff --git a/core/core_network/src/main/java/ru/touchin/template/network/di/ApiUrl.kt b/core/core_network/src/main/java/ru/touchin/template/network/di/ApiUrl.kt deleted file mode 100644 index 819132a..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/di/ApiUrl.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.touchin.template.network.di - -import javax.inject.Qualifier - -@Qualifier -annotation class ApiUrl diff --git a/core/core_network/src/main/java/ru/touchin/template/network/di/ChuckInterceptor.kt b/core/core_network/src/main/java/ru/touchin/template/network/di/ChuckInterceptor.kt deleted file mode 100644 index 2589f62..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/di/ChuckInterceptor.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.touchin.template.network.di - -import javax.inject.Qualifier - -@Qualifier -annotation class ChuckInterceptor diff --git a/core/core_network/src/main/java/ru/touchin/template/network/di/NetworkModule.kt b/core/core_network/src/main/java/ru/touchin/template/network/di/NetworkModule.kt deleted file mode 100644 index 928e615..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/di/NetworkModule.kt +++ /dev/null @@ -1,69 +0,0 @@ -package ru.touchin.template.network.di - -import com.squareup.moshi.Moshi -import dagger.Module -import dagger.Provides -import okhttp3.CertificatePinner -import okhttp3.Interceptor -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Retrofit -import retrofit2.converter.moshi.MoshiConverterFactory -import ru.touchin.template.network.interceptor.ExceptionsInterceptor -import ru.touchin.template.core_network.BuildConfig -import java.util.concurrent.TimeUnit -import javax.inject.Singleton - -@Module -class NetworkModule { - - companion object { - private const val TIMEOUT = 30L - } - - @Singleton - @Provides - fun providePublicClient( - exceptionsInterceptor: ExceptionsInterceptor, - @ChuckInterceptor chuckerInterceptor: Interceptor, - @WithSslPinning withSslPinning: Boolean - ): OkHttpClient = - buildPublicClient(exceptionsInterceptor, chuckerInterceptor, withSslPinning) - - @Singleton - @Provides - fun provideMoshi() = buildMoshi() - - @Singleton - @Provides - fun provideRetrofit(client: OkHttpClient, moshi: Moshi, @ApiUrl apiUrl: String) = buildRetrofitInstance(client, moshi, apiUrl) - - private fun buildMoshi() = Moshi.Builder() - .build() - - private fun buildRetrofitInstance(client: OkHttpClient, moshi: Moshi, apiUrl: String): Retrofit = Retrofit.Builder() - .baseUrl(apiUrl) - .client(client) - .addConverterFactory(MoshiConverterFactory.create(moshi)) - .build() - - private fun buildPublicClient( - exceptionsInterceptor: ExceptionsInterceptor, - chuckerInterceptor: Interceptor, - withSslPinning: Boolean - ): OkHttpClient = OkHttpClient.Builder() - .apply { - connectTimeout(TIMEOUT, TimeUnit.SECONDS) - readTimeout(TIMEOUT, TimeUnit.SECONDS) - writeTimeout(TIMEOUT, TimeUnit.SECONDS) - addInterceptor(exceptionsInterceptor) - addInterceptor(chuckerInterceptor) - if (BuildConfig.DEBUG) { - addNetworkInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY }) - } - if (withSslPinning) { - certificatePinner(CertificatePinner.DEFAULT) - } - }.build() - -} diff --git a/core/core_network/src/main/java/ru/touchin/template/network/di/WithSslPinning.kt b/core/core_network/src/main/java/ru/touchin/template/network/di/WithSslPinning.kt deleted file mode 100644 index 3334f32..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/di/WithSslPinning.kt +++ /dev/null @@ -1,6 +0,0 @@ -package ru.touchin.template.network.di - -import javax.inject.Qualifier - -@Qualifier -annotation class WithSslPinning diff --git a/core/core_network/src/main/java/ru/touchin/template/network/interceptor/ExceptionsInterceptor.kt b/core/core_network/src/main/java/ru/touchin/template/network/interceptor/ExceptionsInterceptor.kt deleted file mode 100644 index 78c4991..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/interceptor/ExceptionsInterceptor.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.touchin.template.network.interceptor - -import okhttp3.Interceptor -import okhttp3.Response -import okhttp3.ResponseBody -import org.json.JSONException -import org.json.JSONObject -import ru.touchin.template.network.cloneBody -import ru.touchin.template.network.models.ServerException -import java.io.IOException -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class ExceptionsInterceptor @Inject constructor() : Interceptor { - - companion object { - private const val ERROR_MESSAGE_FIELD = "errorMessage" - } - - override fun intercept(chain: Interceptor.Chain): Response = chain - .proceed(chain.request()) - .also { getError(it, it.body())?.let { exception -> throw exception } } - - @Suppress("detekt.NestedBlockDepth") - private fun getError(response: Response, body: ResponseBody?): IOException? = body - ?.cloneBody() - ?.let { responseBody -> - try { - val jsonObject = JSONObject(responseBody) - val message = jsonObject.optString(ERROR_MESSAGE_FIELD) - when { - response.code() != 200 -> ServerException(response.code(), message) - else -> null - } - } catch (error: JSONException) { - null - } - } - -} diff --git a/core/core_network/src/main/java/ru/touchin/template/network/models/ServerException.kt b/core/core_network/src/main/java/ru/touchin/template/network/models/ServerException.kt deleted file mode 100644 index cdd3ab2..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/models/ServerException.kt +++ /dev/null @@ -1,18 +0,0 @@ -package ru.touchin.template.network.models - -import java.io.IOException - -open class ServerException(val code: Int, message: String? = null) : IOException(message) { - - companion object { - private val codeToErrorTypeMap = mapOf( - 1 to TemplateApiError.VALID_RESPONSE, - 2 to TemplateApiError.INVALID_PARAMETERS - ) - - fun getErrorTypeByCode(code: Int) = codeToErrorTypeMap[code] - } - - fun getErrorType(): TemplateApiError? = codeToErrorTypeMap[code] - -} diff --git a/core/core_network/src/main/java/ru/touchin/template/network/models/TemplateApiError.kt b/core/core_network/src/main/java/ru/touchin/template/network/models/TemplateApiError.kt deleted file mode 100644 index 461b590..0000000 --- a/core/core_network/src/main/java/ru/touchin/template/network/models/TemplateApiError.kt +++ /dev/null @@ -1,9 +0,0 @@ -package ru.touchin.template.network.models - -enum class TemplateApiError { - - INVALID_PARAMETERS, - - VALID_RESPONSE - -} diff --git a/core/core_network/src/main/res/values/strings.xml b/core/core_network/src/main/res/values/strings.xml deleted file mode 100644 index e3a7072..0000000 --- a/core/core_network/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - My Library - diff --git a/core/core_prefs/.gitignore b/core/core_prefs/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/core/core_prefs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/core/core_prefs/build.gradle.kts b/core/core_prefs/build.gradle.kts deleted file mode 100644 index ca6e8e2..0000000 --- a/core/core_prefs/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} - -dependencies { - implementation(Library.DAGGER) - implementationModule(Module.RoboSwag.STORABLE) -} diff --git a/core/core_prefs/src/main/AndroidManifest.xml b/core/core_prefs/src/main/AndroidManifest.xml deleted file mode 100644 index 48c2fe8..0000000 --- a/core/core_prefs/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_prefs/src/main/java/ru/touchin/template/core_prefs/PreferencesModule.kt b/core/core_prefs/src/main/java/ru/touchin/template/core_prefs/PreferencesModule.kt deleted file mode 100644 index ad458db..0000000 --- a/core/core_prefs/src/main/java/ru/touchin/template/core_prefs/PreferencesModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.touchin.template.core_prefs - -import android.content.Context -import android.content.SharedPreferences -import android.preference.PreferenceManager -import dagger.Module -import dagger.Provides -import ru.touchin.roboswag.components.utils.storables.PreferenceUtils -import ru.touchin.roboswag.core.observables.storable.NonNullStorable -import javax.inject.Singleton - -@Module -class PreferencesModule { - - @Provides - @Singleton - fun provideDefaultSharedPreferences(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) - - @Provides - @Singleton - fun provideTutorialStorable(sharedPreferences: SharedPreferences): NonNullStorable = PreferenceUtils - .booleanStorable("TUTORIAL_STORABLE", sharedPreferences, false) - -} diff --git a/core/core_strings/.gitignore b/core/core_strings/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/core/core_strings/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/core/core_strings/build.gradle.kts b/core/core_strings/build.gradle.kts deleted file mode 100644 index cf68a50..0000000 --- a/core/core_strings/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} -android { - ext["languageMap"] = mapOf("ru" to "${AndroidConfig.COMMON_FOLDER}/strings/default_common_strings_ru.json") - ext["rootPath"] = "core/core_strings" -} - -//gradle.projectsEvaluated { -// tasks.named("preBuild") { -// dependsOn("stringGenerator") -// } -//} -// -//apply(from = "${rootProject.ext["buildScriptsDir"]}/gradle/stringGenerator.gradle") diff --git a/core/core_strings/src/main/AndroidManifest.xml b/core/core_strings/src/main/AndroidManifest.xml deleted file mode 100644 index af59855..0000000 --- a/core/core_strings/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_strings/src/main/res/values/strings.xml b/core/core_strings/src/main/res/values/strings.xml deleted file mode 100644 index 69db99c..0000000 --- a/core/core_strings/src/main/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - Да - diff --git a/core/core_ui/build.gradle.kts b/core/core_ui/build.gradle.kts deleted file mode 100644 index 6b554b4..0000000 --- a/core/core_ui/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} diff --git a/core/core_ui/src/main/AndroidManifest.xml b/core/core_ui/src/main/AndroidManifest.xml deleted file mode 100644 index 3381bd9..0000000 --- a/core/core_ui/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/core/core_utils/.gitignore b/core/core_utils/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/core/core_utils/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/core/core_utils/build.gradle.kts b/core/core_utils/build.gradle.kts deleted file mode 100644 index 6b554b4..0000000 --- a/core/core_utils/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} diff --git a/core/core_utils/src/main/AndroidManifest.xml b/core/core_utils/src/main/AndroidManifest.xml deleted file mode 100644 index 4d6d64c..0000000 --- a/core/core_utils/src/main/AndroidManifest.xml +++ /dev/null @@ -1 +0,0 @@ - diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/data/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/data/build.gradle.kts b/data/build.gradle.kts new file mode 100644 index 0000000..4c55b8f --- /dev/null +++ b/data/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + id(libs.plugins.android.lib.get().pluginId) +} + +private val serverType = Environment.SERVER_ENVIRONMENT.getenv()?.takeIf(String::isNotBlank) +private val versionCatalog: VersionCatalog + get() = extensions.getByType().named("libs") + +android { + namespace = "ru.template.data" + + addLibBuildType(type = BuildType.Develop, enableConfig = true, versionCatalog = versionCatalog) + addLibBuildType(type = BuildType.Debug, enableConfig = true, versionCatalog = versionCatalog) + addLibBuildType(type = BuildType.Customer, enableConfig = true, versionCatalog = versionCatalog) + addLibBuildType(type = BuildType.Release, enableConfig = true, versionCatalog = versionCatalog) + + sourceSets { + getByName("main") { + java.srcDirs("src/main/kotlin") + } + getByName("androidTest") { + java.srcDirs("src/androidTest/kotlin") + } + getByName("test") { + java.srcDirs("src/test/kotlin") + } + } + + addMobileServicesFlavor() + + testOptions { + unitTests { + isReturnDefaultValues = true + } + } +} + +dependencies { + implementation(project(":domain")) + implementation(project(":mobile_services")) + + testImplementation(libs.junit) + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.espresso.core) +} + +val Project.buildScriptDir: String + get() = rootProject.ext["buildScriptsDir"] as String \ No newline at end of file diff --git a/data/src/androidTest/java/ru/template/data/ExampleInstrumentedTest.kt b/data/src/androidTest/java/ru/template/data/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..ddc2651 --- /dev/null +++ b/data/src/androidTest/java/ru/template/data/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package ru.template.data + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("ru.template.data.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/data/src/main/AndroidManifest.xml b/data/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7277dc3 --- /dev/null +++ b/data/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/data/src/main/java/ru/template/data/network/ServerUrl.kt b/data/src/main/java/ru/template/data/network/ServerUrl.kt new file mode 100644 index 0000000..47ff727 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/ServerUrl.kt @@ -0,0 +1,73 @@ +import ru.template.data.network.sslpinning.ServerInfo +import ru.template.data.network.sslpinning.UrlInfo + +enum class ServerUrl( + val title: String, + val serverType: String, + val serverInfo: ServerInfo +) { + CUSTOMER_PROD( + title = "customerProd", + serverType = "Prod", + ServerInfo( + UrlInfo( + protocol = "https", + url = "customerProd", // TODO change this url + ), + pinsHashSet = hashSetOf() + ) + ), + + CUSTOMER_STAGE( + title = "customerStage", + serverType = "Stage", + ServerInfo( + UrlInfo( + protocol = "https", + url = "customerStage", // TODO change this url + ), + pinsHashSet = hashSetOf() + ) + ), + + TOUCHIN_MOCK( + title = "touchinMock", + serverType = "Dev", + ServerInfo( + UrlInfo( + protocol = "https", + url = "touchinMock", // TODO change this url + ), + pinsHashSet = hashSetOf() + ) + ), + + CUSTOMER_TEST( + title = "customerTest", + serverType = "Test", + ServerInfo( + UrlInfo( + protocol = "https", + url = "customerTest", // TODO change this url + ), + pinsHashSet = hashSetOf() + ) + ); + + companion object { + fun fromTitle(title: String?): ServerUrl? = values().associateBy { it.title }[title] + + fun fromServerType(type: String?): ServerUrl? = values().associateBy { it.serverType }[type] + } + + override fun toString() = title + + fun getBaseUrl() = serverInfo.urlInfo.getBaseUrl() + + fun getAddress() = serverInfo.urlInfo.getAddress() + + fun getId() = ordinal + + fun getPins() = serverInfo.getPins() + +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt b/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt new file mode 100644 index 0000000..01524d5 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/ServerInfo.kt @@ -0,0 +1,18 @@ +package ru.template.data.network.sslpinning + +import ru.template.data.network.storage.EncryptedStorage + +data class ServerInfo( + val urlInfo: UrlInfo, + val pinsHashSet: HashSet +) { + fun getPins() = pinsHashSet.toSet() + + fun addPins(pinSet: Set) = pinsHashSet.addAll(pinSet) + + fun updatePins(baseUrl: String, encryptedStorage: EncryptedStorage) { + with(pinsHashSet) { + addAll(encryptedStorage.getCertificatesSet(baseUrl)) + } + } +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt new file mode 100644 index 0000000..02ce243 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerUnsafe.kt @@ -0,0 +1,16 @@ +package com.redmadrobot.data.network.sslpinning + +import java.security.cert.CertificateException +import java.security.cert.X509Certificate +import javax.net.ssl.X509TrustManager + +object TrustManagerUnsafe: X509TrustManager { + + @Throws(CertificateException::class) + override fun checkClientTrusted(chain: Array?, authType: String?) = Unit + + @Throws(CertificateException::class) + override fun checkServerTrusted(chain: Array?, authType: String?) = Unit + + override fun getAcceptedIssuers() = emptyArray() +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt new file mode 100644 index 0000000..92d52ca --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/TrustManagerWithoutTls.kt @@ -0,0 +1,49 @@ +package com.redmadrobot.data.network.sslpinning + +import android.annotation.SuppressLint +import com.redmadrobot.data.network.NetworkConfig +import com.redmadrobot.domain.extension.toHex +import com.redmadrobot.domain.repository.ssl.SslPublicKeyRepository +import java.security.MessageDigest +import java.security.cert.CertificateException +import java.security.cert.X509Certificate +import java.util.Locale +import javax.net.ssl.X509TrustManager + +@SuppressLint("CustomX509TrustManager") +class TrustManagerWithoutTls( + private val networkConfig: NetworkConfig, + private val sslPublicKeyRepository: SslPublicKeyRepository +) : X509TrustManager { + + @SuppressLint("TrustAllX509TrustManager") + override fun checkClientTrusted(chain: Array?, authType: String?) = Unit + + override fun checkServerTrusted(chain: Array?, authType: String?) { + if (networkConfig.isSslPinningEnabled()) { + chain?.let { checkCertificateFingerprint(it) } + } + } + + override fun getAcceptedIssuers(): Array = emptyArray() + + private fun checkCertificateFingerprint(chain: Array) { + val pinFromServer = chain[0]?.let { getSha256FingerprintFormatted(it) } + networkConfig.getCurrentServer().getPins().also { + if (it.contains(pinFromServer)) { + sslPublicKeyRepository.setPublicKey(isValid = true) + return + } + } + sslPublicKeyRepository.setPublicKey(isValid = false) + throw CertificateException("Cannot validate server certificate") + } + + private fun getSha256FingerprintFormatted(certificate: X509Certificate): String { + return MessageDigest + .getInstance("SHA-256") + .digest(certificate.encoded) + .toHex(separator = ":").toUpperCase(Locale.getDefault()) + } + +} diff --git a/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt b/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt new file mode 100644 index 0000000..08d9570 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/sslpinning/UrlInfo.kt @@ -0,0 +1,11 @@ +package ru.template.data.network.sslpinning + +data class UrlInfo( + private val protocol: String, + private val url: String, +) { + fun getAddress(): String = "$protocol://$url/api/v1/" + + fun getBaseUrl(): String = url + +} diff --git a/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt b/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt new file mode 100644 index 0000000..d51da41 --- /dev/null +++ b/data/src/main/java/ru/template/data/network/storage/EncryptedStorage.kt @@ -0,0 +1,15 @@ +package ru.template.data.network.storage + +import android.content.SharedPreferences + +class EncryptedStorage(private val preferences: SharedPreferences) { + + fun saveCertificatesToSet(host: String, certificates: Set) = + preferences.edit().putStringSet(host, + getCertificatesSet(host).toMutableSet().apply { addAll(certificates) }.toSet()).apply() + + + fun getCertificatesSet(host: String): Set + = preferences.getStringSet(host, emptySet()) ?: emptySet() + +} diff --git a/data/src/test/java/ru/template/data/ExampleUnitTest.kt b/data/src/test/java/ru/template/data/ExampleUnitTest.kt new file mode 100644 index 0000000..9a22feb --- /dev/null +++ b/data/src/test/java/ru/template/data/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package ru.template.data + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/domain/.gitignore b/domain/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/domain/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts new file mode 100644 index 0000000..dc77d4f --- /dev/null +++ b/domain/build.gradle.kts @@ -0,0 +1,29 @@ +plugins { + id(libs.plugins.android.lib.get().pluginId) +} + +private val versionCatalog: VersionCatalog + get() = extensions.getByType().named("libs") + +android { + namespace = "ru.template.domain" + + buildTypes { + addLibBuildType(BuildType.Develop, versionCatalog = versionCatalog) + addLibBuildType(BuildType.Debug, versionCatalog = versionCatalog) + addLibBuildType(BuildType.Customer, versionCatalog = versionCatalog) + addLibBuildType(BuildType.Release, versionCatalog = versionCatalog) + } + + addMobileServicesFlavor() + + sourceSets { + getByName("main") { + java.srcDirs("src/main/kotlin") + } + } +} + +dependencies { + implementation(project(":mobile_services")) +} \ No newline at end of file diff --git a/domain/src/androidTest/java/ru/template/domain/ExampleInstrumentedTest.kt b/domain/src/androidTest/java/ru/template/domain/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..9995ba3 --- /dev/null +++ b/domain/src/androidTest/java/ru/template/domain/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package ru.template.domain + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("ru.template.domain.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/domain/src/main/AndroidManifest.xml b/domain/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7277dc3 --- /dev/null +++ b/domain/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/domain/src/test/java/ru/template/domain/ExampleUnitTest.kt b/domain/src/test/java/ru/template/domain/ExampleUnitTest.kt new file mode 100644 index 0000000..ae2529c --- /dev/null +++ b/domain/src/test/java/ru/template/domain/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package ru.template.domain + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/features/feature_login/.gitignore b/features/feature_login/.gitignore deleted file mode 100644 index 796b96d..0000000 --- a/features/feature_login/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/features/feature_login/build.gradle.kts b/features/feature_login/build.gradle.kts deleted file mode 100644 index a8d397e..0000000 --- a/features/feature_login/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG) -} - -dependencies { - dagger() - mvi() -} diff --git a/features/feature_login/src/main/AndroidManifest.xml b/features/feature_login/src/main/AndroidManifest.xml deleted file mode 100644 index d139092..0000000 --- a/features/feature_login/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/LoginDeps.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/LoginDeps.kt deleted file mode 100644 index db52f3e..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/LoginDeps.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ru.touchin.template.feature_login - -import ru.touchin.template.feature_login.navigation.LoginCoordinator - -interface LoginDeps { - fun loginCoordinator(): LoginCoordinator -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/LoginComponent.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/LoginComponent.kt deleted file mode 100644 index b475563..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/LoginComponent.kt +++ /dev/null @@ -1,19 +0,0 @@ -package ru.touchin.template.feature_login.di - -import dagger.Component -import ru.touchin.roboswag.navigation_base.scopes.FragmentScope -import ru.touchin.template.feature_login.LoginDeps -import ru.touchin.template.feature_login.presentation.LoginFragment - -@FragmentScope -@Component(modules = [ViewModelModule::class], dependencies = [LoginDeps::class]) -interface LoginComponent { - - fun inject(fragment: LoginFragment) - - @Component.Factory - interface Factory { - fun create(deps: LoginDeps): LoginComponent - } - -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/ViewModelModule.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/ViewModelModule.kt deleted file mode 100644 index 3160570..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/di/ViewModelModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package ru.touchin.template.feature_login.di - -import androidx.lifecycle.ViewModel -import com.squareup.inject.assisted.dagger2.AssistedModule -import dagger.Binds -import dagger.Module -import dagger.multibindings.IntoMap -import ru.touchin.template.feature_login.presentation.LoginViewModel -import ru.touchin.roboswag.mvi_arch.di.ViewModelAssistedFactory -import ru.touchin.roboswag.mvi_arch.di.ViewModelKey - -@Module(includes = [ViewModelAssistedFactoriesModule::class]) -interface ViewModelModule { - - @Binds - @IntoMap - @ViewModelKey(LoginViewModel::class) - fun bindLoginByPinFactory(factory: LoginViewModel.Factory): ViewModelAssistedFactory - -} - -@AssistedModule -@Module(includes = [AssistedInject_ViewModelAssistedFactoriesModule::class]) -abstract class ViewModelAssistedFactoriesModule diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/navigation/LoginCoordinator.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/navigation/LoginCoordinator.kt deleted file mode 100644 index e2240da..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/navigation/LoginCoordinator.kt +++ /dev/null @@ -1,8 +0,0 @@ -package ru.touchin.template.feature_login.navigation - -import ru.touchin.roboswag.navigation_base.scopes.FragmentScope - -@FragmentScope -interface LoginCoordinator { - fun openMainScreen() -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginFragment.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginFragment.kt deleted file mode 100644 index e2001aa..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginFragment.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.touchin.template.feature_login.presentation - -import android.os.Bundle -import android.view.View -import me.vponomarenko.injectionmanager.IHasComponent -import me.vponomarenko.injectionmanager.x.XInjectionManager -import ru.touchin.template.feature_login.R -import ru.touchin.template.feature_login.databinding.FragmentLoginBinding -import ru.touchin.template.feature_login.di.DaggerLoginComponent -import ru.touchin.template.feature_login.di.LoginComponent -import ru.touchin.roboswag.mvi_arch.core.MviFragment -import ru.touchin.roboswag.navigation_base.fragments.EmptyState -import ru.touchin.roboswag.navigation_base.fragments.viewBinding - -class LoginFragment : MviFragment(R.layout.fragment_login), - IHasComponent { - - private val binding by viewBinding(FragmentLoginBinding::bind) - - override val viewModel: LoginViewModel by viewModel() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - injectDependencies() - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.goToMainScreenButton.dispatchActionOnRippleClick(LoginViewAction.GoToMainScreenButtonClicked) - } - - private fun injectDependencies() { - XInjectionManager.bindComponent(this) - .inject(this) - } - - override fun getComponent(): LoginComponent = DaggerLoginComponent - .factory() - .create(XInjectionManager.findComponent()) - -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewAction.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewAction.kt deleted file mode 100644 index e04c6c0..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewAction.kt +++ /dev/null @@ -1,7 +0,0 @@ -package ru.touchin.template.feature_login.presentation - -import ru.touchin.roboswag.mvi_arch.marker.ViewAction - -sealed class LoginViewAction : ViewAction { - object GoToMainScreenButtonClicked : LoginViewAction() -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewModel.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewModel.kt deleted file mode 100644 index da0ce3d..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewModel.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ru.touchin.template.feature_login.presentation - -import androidx.lifecycle.SavedStateHandle -import com.squareup.inject.assisted.Assisted -import com.squareup.inject.assisted.AssistedInject -import ru.touchin.template.feature_login.navigation.LoginCoordinator -import ru.touchin.roboswag.mvi_arch.core.MviViewModel -import ru.touchin.roboswag.mvi_arch.di.ViewModelAssistedFactory -import ru.touchin.roboswag.navigation_base.fragments.EmptyState - -class LoginViewModel @AssistedInject constructor( - @Assisted arg0: SavedStateHandle, - private val coordinator: LoginCoordinator -) : MviViewModel(LoginViewState, arg0) { - - override fun dispatchAction(action: LoginViewAction) { - when (action) { - LoginViewAction.GoToMainScreenButtonClicked -> coordinator.openMainScreen() - } - } - - @AssistedInject.Factory - interface Factory : ViewModelAssistedFactory - -} diff --git a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewState.kt b/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewState.kt deleted file mode 100644 index 1508948..0000000 --- a/features/feature_login/src/main/java/ru/touchin/template/feature_login/presentation/LoginViewState.kt +++ /dev/null @@ -1,5 +0,0 @@ -package ru.touchin.template.feature_login.presentation - -import ru.touchin.roboswag.mvi_arch.marker.ViewState - -object LoginViewState : ViewState diff --git a/features/feature_login/src/main/res/layout/fragment_login.xml b/features/feature_login/src/main/res/layout/fragment_login.xml deleted file mode 100644 index e566a1a..0000000 --- a/features/feature_login/src/main/res/layout/fragment_login.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - -