commit
a2cad6cba2
|
|
@ -1 +1 @@
|
|||
Subproject commit c957a38784af59fb341e3df651a60b0861aba50b
|
||||
Subproject commit be9bbb971e0864178ebf6d2f22bcd40d7fecd24e
|
||||
2
RoboSwag
2
RoboSwag
|
|
@ -1 +1 @@
|
|||
Subproject commit dc5cb987cfbc6ba37e59870650fae8c34b1a9c32
|
||||
Subproject commit 09061c1bcfd951127f1a62c25990a5c9d9cbbbce
|
||||
178
app/build.gradle
178
app/build.gradle
|
|
@ -1,178 +0,0 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
def customEndpoint = System.getenv("CUSTOM_ENDPOINT")
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.touchin.template"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion versions.compileSdk
|
||||
versionCode System.getenv("BUILD_NUMBER") as Integer ?: 10000
|
||||
versionName "1.0." + versionCode
|
||||
|
||||
rootProject.extensions.pathToApiSchemes = "$rootDir/Template-common/api"
|
||||
rootProject.extensions.applicationId = "com.touchin.template"
|
||||
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
firebaseCrashlytics {
|
||||
mappingFileUploadEnabled true
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile file("keystore/touchin.jks")
|
||||
storePassword "iphoneandroidwp7"
|
||||
keyAlias "touchin"
|
||||
keyPassword "iphoneandroidwp7"
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
versionNameSuffix ".debug"
|
||||
minifyEnabled false
|
||||
shrinkResources false
|
||||
ext.enableCrashlytics = false
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
ext.enableCrashlytics = true
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions "proguardSettings", "apiEndpoint", "sslPinning", "testPanel"
|
||||
|
||||
/*
|
||||
Use that guide for adding new server env. flavours https://github.com/TouchInstinct/Styleguide/blob/master/general/setupBuildGuide.md
|
||||
*/
|
||||
productFlavors {
|
||||
noObfuscate {
|
||||
dimension "proguardSettings"
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), "$buildScriptsDir/proguard/noObfuscate.pro"
|
||||
}
|
||||
obfuscate {
|
||||
dimension "proguardSettings"
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), "$buildScriptsDir/proguard/obfuscate.pro"
|
||||
}
|
||||
touchinTest {
|
||||
def endpoint = customEndpoint ?: 'https://template-server.test.touchin.ru'
|
||||
dimension "apiEndpoint"
|
||||
buildConfigField "String", "API_URL", """\"${endpoint}/\""""
|
||||
}
|
||||
customerProd {
|
||||
def endpoint = customEndpoint ?: 'https://template-server.prod.customer.ru'
|
||||
dimension "apiEndpoint"
|
||||
buildConfigField "String", "API_URL", """\"${endpoint}/\""""
|
||||
}
|
||||
withSSLPinning {
|
||||
dimension "sslPinning"
|
||||
buildConfigField "Boolean", "PIN_SSL", 'true'
|
||||
}
|
||||
withoutSSLPinning {
|
||||
dimension "sslPinning"
|
||||
buildConfigField "Boolean", "PIN_SSL", 'false'
|
||||
}
|
||||
withTestPanel {
|
||||
dimension "testPanel"
|
||||
}
|
||||
withoutTestPanel {
|
||||
dimension "testPanel"
|
||||
}
|
||||
}
|
||||
|
||||
extensions.languageMap = ["ru": "Template-common/strings/default_common_strings_ru.json"]
|
||||
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// RoboSwag
|
||||
gradle.ext.roboswag.forEach { module ->
|
||||
implementation project(":$module")
|
||||
}
|
||||
|
||||
// Kotlin
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
// AndroidX
|
||||
implementation "androidx.appcompat:appcompat:$versions.appcompat"
|
||||
implementation "androidx.recyclerview:recyclerview:$versions.androidx"
|
||||
implementation "androidx.cardview:cardview:$versions.androidx"
|
||||
implementation "androidx.gridlayout:gridlayout:$versions.androidx"
|
||||
implementation "androidx.core:core-ktx:$versions.androidxKtx"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4"
|
||||
implementation "com.google.android.material:material:$versions.material"
|
||||
|
||||
// Lifecycle
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$versions.lifecycle"
|
||||
implementation "androidx.lifecycle:lifecycle-reactivestreams:$versions.lifecycle"
|
||||
|
||||
// Room
|
||||
implementation "androidx.room:room-runtime:$versions.room"
|
||||
implementation "androidx.room:room-rxjava2:$versions.room"
|
||||
kapt "androidx.room:room-compiler:$versions.room"
|
||||
|
||||
// Retrofit
|
||||
implementation "com.squareup.retrofit2:retrofit:$versions.retrofit"
|
||||
implementation "com.squareup.retrofit2:adapter-rxjava2:$versions.retrofit"
|
||||
implementation "com.squareup.okhttp3:logging-interceptor:$versions.okhttp3"
|
||||
implementation "com.squareup.okhttp3:okhttp-urlconnection:$versions.okhttp3"
|
||||
|
||||
// Logan square
|
||||
implementation "ru.touchin:logansquare:$versions.logansquare"
|
||||
kapt "ru.touchin:logansquare-compiler:$versions.logansquare"
|
||||
|
||||
// Dagger
|
||||
implementation "com.google.dagger:dagger:$versions.dagger"
|
||||
kapt "com.google.dagger:dagger-compiler:$versions.dagger"
|
||||
|
||||
// RxJava
|
||||
implementation "io.reactivex.rxjava2:rxjava:$versions.rxJava"
|
||||
implementation "io.reactivex.rxjava2:rxandroid:$versions.rxAndroid"
|
||||
|
||||
// Glide
|
||||
implementation "com.github.bumptech.glide:glide:$versions.glide"
|
||||
implementation "com.github.bumptech.glide:okhttp3-integration:$versions.glide"
|
||||
kapt "com.github.bumptech.glide:compiler:$versions.glide"
|
||||
|
||||
// Chucker
|
||||
withTestPanelImplementation "com.github.ChuckerTeam.Chucker:library:$versions.chucker"
|
||||
withoutTestPanelImplementation "com.github.ChuckerTeam.Chucker:library-no-op:$versions.chucker"
|
||||
|
||||
// LeakCanary
|
||||
withTestPanelImplementation "com.squareup.leakcanary:leakcanary-android:$versions.leakcanary"
|
||||
|
||||
}
|
||||
//TODO: uncomment, when common repo become plugged
|
||||
|
||||
//gradle.projectsEvaluated {
|
||||
// preBuild.dependsOn('stringGenerator')
|
||||
//}
|
||||
|
||||
//apply from: "$buildScriptsDir/gradle/stringGenerator.gradle"
|
||||
apply from: "$buildScriptsDir/gradle/apiGenerator.gradle"
|
||||
apply from: "$buildScriptsDir/gradle/applicationFileNaming.gradle"
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_APP_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
id(Plugins.FIREBASE_CRASH)
|
||||
id(Plugins.GOOGLE_SERVICES)
|
||||
id(Plugins.LICENCE_PLUGIN)
|
||||
}
|
||||
|
||||
val customEndpoint: String? = Environment.ENDPOINT.getenv()?.takeIf(String::isNotBlank)
|
||||
|
||||
android {
|
||||
configureSigningConfig(this@Build_gradle::file)
|
||||
|
||||
with(defaultConfig) {
|
||||
applicationId = Environment.APP_ID.getenv() ?: AndroidConfig.TEST_APP_ID
|
||||
signingConfig = signingConfigs.getByName(SigningConfig.CONFIG_NAME)
|
||||
}
|
||||
|
||||
firebaseCrashlytics {
|
||||
mappingFileUploadEnabled = true
|
||||
}
|
||||
|
||||
addBuildType(BuildType.Debug, buildScriptDir = buildScriptDir)
|
||||
addBuildType(BuildType.Release, buildScriptDir = buildScriptDir)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
apply(from = "$buildScriptDir/gradle/scripts/applicationFileNaming.gradle")
|
||||
|
||||
val Project.buildScriptDir: String
|
||||
get() = rootProject.ext["buildScriptsDir"] as String
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,22 +1,21 @@
|
|||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.touchin.template">
|
||||
package="ru.touchin.template">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:name="ru.touchin.template.TemplateApplication"
|
||||
android:name="ru.touchin.template.App"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/common_global_app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="false"
|
||||
tools:ignore="GoogleAppIndexingWarning">
|
||||
|
||||
<activity
|
||||
android:name="ru.touchin.template.activities.StartupActivity"
|
||||
android:name="ru.touchin.template.SingleActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/AppTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
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
|
||||
|
||||
class App : TouchinApp(), IHasComponent<ApplicationComponent> {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
initDagger()
|
||||
}
|
||||
|
||||
fun initDagger() {
|
||||
XInjectionManager.init(this)
|
||||
XInjectionManager.bindComponent(this)
|
||||
}
|
||||
|
||||
override fun getComponent(): ApplicationComponent = DaggerApplicationComponent
|
||||
.factory()
|
||||
.create(this)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
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
|
||||
|
||||
// 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<ApplicationComponent>()
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
package ru.touchin.template
|
||||
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import com.bluelinelabs.logansquare.LoganSquare
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormat
|
||||
import ru.touchin.lifecycle.viewmodel.ViewModelFactory
|
||||
import ru.touchin.lifecycle.viewmodel.ViewModelFactoryProvider
|
||||
import ru.touchin.roboswag.components.navigation.TouchinApp
|
||||
import ru.touchin.template.di.app.DaggerApplicationComponent
|
||||
import ru.touchin.template.di.app.modules.ApplicationModule
|
||||
import ru.touchin.templates.logansquare.LoganSquareBigDecimalConverter
|
||||
import ru.touchin.templates.logansquare.LoganSquareJodaTimeConverter
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
class TemplateApplication : TouchinApp(), ViewModelFactoryProvider {
|
||||
@Inject
|
||||
override lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
//TODO remove after init
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
initializeLoganSquare()
|
||||
initializeDagger()
|
||||
|
||||
}
|
||||
|
||||
private fun initializeLoganSquare() {
|
||||
val formatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZZ")
|
||||
LoganSquare.registerTypeConverter(BigDecimal::class.java, LoganSquareBigDecimalConverter())
|
||||
LoganSquare.registerTypeConverter(DateTime::class.java, LoganSquareJodaTimeConverter(formatter))
|
||||
}
|
||||
|
||||
private fun initializeDagger() {
|
||||
DaggerApplicationComponent.builder()
|
||||
.applicationModule(ApplicationModule(this))
|
||||
.build()
|
||||
.inject(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package ru.touchin.template.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import com.touchin.template.R
|
||||
import ru.touchin.roboswag.components.navigation.activities.BaseActivity
|
||||
|
||||
class StartupActivity : BaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.common_activity)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
package ru.touchin.template.api
|
||||
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Response
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import ru.touchin.template.api.exceptions.ServerException
|
||||
import ru.touchin.template.extensions.cloneBody
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package ru.touchin.template.api
|
||||
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface UserApi {
|
||||
|
||||
@POST("user/session/create")
|
||||
fun getSession(@Body body: Body): Single<String>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
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)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
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<Router> = Cicerone.create()
|
||||
|
||||
@Provides
|
||||
@MainNavigation
|
||||
fun provideNavigatorHolder(@MainNavigation cicerone: Cicerone<Router>): NavigatorHolder = cicerone.navigatorHolder
|
||||
|
||||
@Provides
|
||||
@MainNavigation
|
||||
fun provideRouter(@MainNavigation cicerone: Cicerone<Router>): Router = cicerone.router
|
||||
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package ru.touchin.template.di.app
|
||||
|
||||
import dagger.Component
|
||||
import ru.touchin.template.TemplateApplication
|
||||
import ru.touchin.template.di.app.modules.ApplicationModule
|
||||
import ru.touchin.template.di.app.modules.NetworkModule
|
||||
import ru.touchin.template.di.app.modules.PersistentModule
|
||||
import ru.touchin.template.di.viewmodel.ViewModelModule
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
@Component(modules = [ApplicationModule::class, PersistentModule::class, ViewModelModule::class, NetworkModule::class])
|
||||
interface ApplicationComponent {
|
||||
|
||||
fun inject(application: TemplateApplication)
|
||||
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package ru.touchin.template.di.app.modules
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Module
|
||||
class ApplicationModule(private val context: Context) {
|
||||
|
||||
@Provides
|
||||
fun provideContext(): Context = context
|
||||
|
||||
@Provides
|
||||
fun provideDefaultSharedPreferences(context: Context): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package ru.touchin.template.di.app.modules
|
||||
|
||||
import android.content.Context
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.touchin.template.BuildConfig
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
||||
import ru.touchin.template.api.ExceptionsInterceptor
|
||||
import ru.touchin.template.api.UserApi
|
||||
import ru.touchin.template.di.qualifiers.PublicApi
|
||||
import ru.touchin.templates.logansquare.LoganSquareJsonFactory
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class NetworkModule {
|
||||
|
||||
companion object {
|
||||
private val CONVERTER_FACTORY = LoganSquareJsonFactory()
|
||||
private val CALL_ADAPTER_FACTORY = RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())
|
||||
private const val TIMEOUT = 30L
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideUserApi(@PublicApi retrofit: Retrofit): UserApi = retrofit.create(UserApi::class.java)
|
||||
|
||||
@Singleton
|
||||
@PublicApi
|
||||
@Provides
|
||||
fun providePublicRetrofit(@PublicApi client: OkHttpClient) = buildRetrofitInstance(client, BuildConfig.API_URL)
|
||||
|
||||
@Singleton
|
||||
@PublicApi
|
||||
@Provides
|
||||
fun providePublicClient(context: Context, exceptionsInterceptor: ExceptionsInterceptor): OkHttpClient =
|
||||
buildPublicClient(context, exceptionsInterceptor)
|
||||
|
||||
private fun buildRetrofitInstance(client: OkHttpClient, apiUrl: String): Retrofit = Retrofit.Builder()
|
||||
.baseUrl(apiUrl)
|
||||
.client(client)
|
||||
.addCallAdapterFactory(CALL_ADAPTER_FACTORY)
|
||||
.addConverterFactory(CONVERTER_FACTORY)
|
||||
.build()
|
||||
|
||||
private fun buildPublicClient(context: Context, exceptionsInterceptor: ExceptionsInterceptor): OkHttpClient = OkHttpClient.Builder()
|
||||
.apply {
|
||||
connectTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
readTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
writeTimeout(TIMEOUT, TimeUnit.SECONDS)
|
||||
addInterceptor(exceptionsInterceptor)
|
||||
addInterceptor(ChuckerInterceptor(context))
|
||||
if (BuildConfig.DEBUG) {
|
||||
addNetworkInterceptor(HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY })
|
||||
}
|
||||
}.build()
|
||||
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package ru.touchin.template.di.app.modules
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.room.Room
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import ru.touchin.roboswag.components.utils.storables.PreferenceUtils
|
||||
import ru.touchin.roboswag.core.observables.storable.NonNullStorable
|
||||
import ru.touchin.template.di.qualifiers.SessionStorable
|
||||
import ru.touchin.template.persistence.Database
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class PersistentModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDatabase(context: Context): Database = Room
|
||||
.databaseBuilder(context, Database::class.java, "database")
|
||||
.build()
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@SessionStorable
|
||||
fun provideSessionStorable(sharedPreferences: SharedPreferences): NonNullStorable<String, String, String> =
|
||||
PreferenceUtils.stringStorable("MIDDLE_SESSION", sharedPreferences, "")
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package ru.touchin.template.di.qualifiers
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class PublicApi
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package ru.touchin.template.di.qualifiers
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class SessionStorable
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
package ru.touchin.template.di.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.MapKey
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
|
||||
@MapKey
|
||||
annotation class ViewModelKey(val value: KClass<out ViewModel>)
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
package ru.touchin.template.di.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.multibindings.IntoMap
|
||||
import ru.touchin.template.viewmodel.StartupViewModel
|
||||
|
||||
@Module
|
||||
interface ViewModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(StartupViewModel::class)
|
||||
fun bindStartupViewModel(viewModel: StartupViewModel): ViewModel
|
||||
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
package ru.touchin.template.model
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonEnum
|
||||
import com.bluelinelabs.logansquare.annotation.JsonNumberValue
|
||||
|
||||
@JsonEnum
|
||||
enum class TemplateApiError {
|
||||
|
||||
@JsonNumberValue(-1)
|
||||
INVALID_PARAMETERS,
|
||||
|
||||
@JsonNumberValue(0)
|
||||
VALID_RESPONSE
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.template.navigation
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class MainNavigation
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
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()
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
package ru.touchin.template.persistence
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import ru.touchin.template.persistence.entities.UserEntity
|
||||
|
||||
@Database(version = 1, entities = [UserEntity::class], exportSchema = false)
|
||||
abstract class Database : RoomDatabase()
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
package ru.touchin.template.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "user")
|
||||
data class UserEntity(
|
||||
|
||||
@PrimaryKey
|
||||
val uid: String = "",
|
||||
|
||||
@ColumnInfo(name = "session_id")
|
||||
var sessionId: String?
|
||||
|
||||
)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package ru.touchin.template.viewmodel
|
||||
|
||||
import ru.touchin.lifecycle.viewmodel.RxViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class StartupViewModel @Inject constructor() : RxViewModel()
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
69
build.gradle
69
build.gradle
|
|
@ -1,69 +0,0 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.72'
|
||||
ext.gradle_version = '3.6.2'
|
||||
ext.fabric_version = '1.27.1'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.android.tools.build:gradle:${gradle_version}"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.0.0-beta04'
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
plugins {
|
||||
id "io.gitlab.arturbosch.detekt" version "1.7.4"
|
||||
id "de.aaschmid.cpd" version "3.1"
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url "http://dl.bintray.com/touchin/touchin-tools" }
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
ext {
|
||||
buildScriptsDir = "$rootProject.projectDir/BuildScripts"
|
||||
versions = [
|
||||
compileSdk : 29,
|
||||
androidx : '1.0.0',
|
||||
fragment : '1.2.1',
|
||||
appcompat : '1.1.0',
|
||||
material : '1.1.0',
|
||||
androidxKtx : '1.2.0',
|
||||
lifecycle : '2.2.0',
|
||||
room : '2.2.5',
|
||||
dagger : '2.17',
|
||||
retrofit : '2.4.0',
|
||||
okhttp3 : '3.14.1',
|
||||
logansquare : '1.4.3',
|
||||
rxJava : '2.2.3',
|
||||
rxAndroid : '2.1.1',
|
||||
crashlytics : '2.10.1',
|
||||
firebaseCrashlytics: '17.0.0-beta04',
|
||||
glide : '4.11.0',
|
||||
location : '16.0.0',
|
||||
chucker : '3.1.1',
|
||||
leakcanary : '2.1'
|
||||
]
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply plugin: "io.gitlab.arturbosch.detekt"
|
||||
}
|
||||
|
||||
apply from: "$buildScriptsDir/gradle/staticAnalysis.gradle"
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
buildscript {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
jcenter()
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
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()
|
||||
google()
|
||||
maven {
|
||||
url = uri("https://plugins.gradle.org/m2/")
|
||||
}
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
|
||||
|
||||
val compileKotlin: KotlinCompile by tasks
|
||||
compileKotlin.kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
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"
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
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 <T> uncheckedCast(obj: Any?): T = obj as T
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
object Environment {
|
||||
const val APP_ID = "BUNDLE_ID"
|
||||
|
||||
const val STORE_PASSWORD = "STORE_PASSWORD"
|
||||
const val KEY_ALIAS = "KEY_ALIAS"
|
||||
const val KEY_PASSWORD = "KEY_PASSWORD"
|
||||
|
||||
const val ENDPOINT = "CUSTOM_ENDPOINT"
|
||||
|
||||
const val BUILD_NUMBER = "BUILD_NUMBER"
|
||||
}
|
||||
|
||||
fun String.getenv(): String? = System.getenv(this)
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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}"
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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"
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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"
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
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"
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import com.android.build.gradle.BaseExtension
|
||||
|
||||
sealed class ApiFlavour(
|
||||
val name: String,
|
||||
val apiUrl: String
|
||||
) : Flavour(name, DIMENSION_NAME) {
|
||||
|
||||
companion object {
|
||||
const val DIMENSION_NAME = "apiEndpoint"
|
||||
}
|
||||
|
||||
// TODO: change url
|
||||
object CustomerStage : ApiFlavour(
|
||||
name = "customerStage",
|
||||
apiUrl = "https://wallet-api.staging.mnxsc.tech"
|
||||
)
|
||||
|
||||
// TODO: change url
|
||||
object CustomerProd : ApiFlavour(
|
||||
name = "customerProd",
|
||||
apiUrl = "https://wallet-api.prod.mnxsc.tech"
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun BaseExtension.addFlavour(flavour: ApiFlavour, customEndpoint: String?) {
|
||||
productFlavors {
|
||||
create(flavour.name) {
|
||||
dimension = flavour.dimensionName
|
||||
buildConfigField("String", "API_URL", "\"${customEndpoint ?: flavour.apiUrl}\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import com.android.build.gradle.BaseExtension
|
||||
|
||||
abstract class Flavour(val flavourName: String, val dimensionName: String)
|
||||
|
||||
fun BaseExtension.addEmptyFlavour(flavour: Flavour) {
|
||||
productFlavors {
|
||||
create(flavour.flavourName) {
|
||||
dimension = flavour.dimensionName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import com.android.build.gradle.BaseExtension
|
||||
|
||||
sealed class SSLPinningFlavour(
|
||||
val name: String,
|
||||
val withSslPinning: Boolean
|
||||
) : Flavour(name, DIMENSION_NAME) {
|
||||
|
||||
companion object {
|
||||
const val DIMENSION_NAME = "sslPinning"
|
||||
}
|
||||
|
||||
object OFF : SSLPinningFlavour(
|
||||
name = "withoutSSLPinning",
|
||||
withSslPinning = true
|
||||
)
|
||||
|
||||
object ON : SSLPinningFlavour(
|
||||
name = "withSSLPinning",
|
||||
withSslPinning = true
|
||||
)
|
||||
}
|
||||
|
||||
fun BaseExtension.addFlavour(flavour: SSLPinningFlavour) {
|
||||
productFlavors {
|
||||
create(flavour.name) {
|
||||
dimension = flavour.dimensionName
|
||||
buildConfigField("Boolean", "WithSSLPinning", flavour.withSslPinning.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import com.android.build.gradle.BaseExtension
|
||||
import java.io.File
|
||||
|
||||
object SigningConfig {
|
||||
const val CONFIG_NAME: String = "signing_key"
|
||||
const val PATH_TO_KEYSTORE_FILE: String = "signing_key.jks"
|
||||
|
||||
const val DEFAULT_STORE_PASSWORD: String = "iphoneandroidwp7"
|
||||
const val DEFAULT_KEY_ALIAS: String = "touchin"
|
||||
const val DEFAULT_KEY_PASSWORD: String = "iphoneandroidwp7"
|
||||
}
|
||||
|
||||
fun BaseExtension.configureSigningConfig(getRelativeFile: (String) -> File) {
|
||||
signingConfigs {
|
||||
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
|
||||
keyPassword = Environment.KEY_PASSWORD.getenv() ?: SigningConfig.DEFAULT_KEY_PASSWORD
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
sealed class TestPanelFlavour(
|
||||
name: String
|
||||
) : Flavour(name, DIMENSION_NAME) {
|
||||
|
||||
companion object {
|
||||
const val DIMENSION_NAME = "testPanel"
|
||||
}
|
||||
|
||||
object OFF : TestPanelFlavour(
|
||||
name = "withoutTestPanel"
|
||||
)
|
||||
|
||||
object ON : TestPanelFlavour(
|
||||
name = "withTestPanel"
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package plugins
|
||||
|
||||
import Plugins
|
||||
import org.gradle.api.Project
|
||||
|
||||
class AndroidAppPlugin : BaseAndroidPlugin() {
|
||||
|
||||
override fun apply(target: Project) {
|
||||
target.plugins.apply(Plugins.ANDROID_APPLICATION)
|
||||
super.apply(target)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package plugins
|
||||
|
||||
import Plugins
|
||||
import org.gradle.api.Project
|
||||
|
||||
class AndroidLibPlugin : BaseAndroidPlugin() {
|
||||
|
||||
override fun apply(target: Project) {
|
||||
target.plugins.apply(Plugins.ANDROID_LIBRARY)
|
||||
super.apply(target)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package plugins
|
||||
|
||||
import AndroidConfig
|
||||
import BuildType
|
||||
import Plugins
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import kotlinStd
|
||||
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.dsl.DependencyHandler
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
abstract class BaseAndroidPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project) {
|
||||
target.configurePlugins()
|
||||
target.configureAndroid()
|
||||
target.configureDependencies()
|
||||
}
|
||||
|
||||
private fun Project.configurePlugins() {
|
||||
plugins.apply(Plugins.KOTLIN_ANDROID)
|
||||
plugins.apply(Plugins.KOTLIN_ANDROID_EXTENSIONS)
|
||||
plugins.apply(Plugins.KOTLIN_KAPT)
|
||||
}
|
||||
|
||||
private fun Project.configureAndroid() = extensions.getByType<BaseExtension>().run {
|
||||
compileSdkVersion(AndroidConfig.COMPILE_SDK_VERSION)
|
||||
buildToolsVersion = AndroidConfig.BUILD_TOOLS_VERSION
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion(AndroidConfig.MIN_SDK_VERSION)
|
||||
targetSdkVersion(AndroidConfig.TARGET_SDK_VERSION)
|
||||
versionCode = AndroidConfig.VERSION_CODE
|
||||
versionName = AndroidConfig.VERSION_NAME
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
coreLibraryDesugaringEnabled = true
|
||||
}
|
||||
|
||||
buildFeatures.viewBinding = true
|
||||
|
||||
tasks.withType(KotlinCompile::class.java) {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
if (AndroidConfig.RELEASE_DEBUGGABLE) {
|
||||
buildTypes {
|
||||
getByName(BuildType.Release.name) {
|
||||
isDebuggable = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
private fun DependencyHandler.implementation(dependencyNotation: Any): Dependency? =
|
||||
add("implementation", dependencyNotation)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
implementation-class=plugins.AndroidAppPlugin
|
||||
|
|
@ -0,0 +1 @@
|
|||
implementation-class=plugins.AndroidLibPlugin
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_data" />
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_domain" />
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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()
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_network" />
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
package ru.touchin.template.extensions
|
||||
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()
|
||||
.also { it.request(Long.MAX_VALUE) }
|
||||
.buffer
|
||||
?.clone()
|
||||
?.readString(Charset.forName("UTF-8"))
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.template.network.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class ApiUrl
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.template.network.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class ChuckInterceptor
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
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()
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package ru.touchin.template.network.di
|
||||
|
||||
import javax.inject.Qualifier
|
||||
|
||||
@Qualifier
|
||||
annotation class WithSslPinning
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package ru.touchin.template.api.exceptions
|
||||
package ru.touchin.template.network.models
|
||||
|
||||
import ru.touchin.template.model.TemplateApiError
|
||||
import java.io.IOException
|
||||
|
||||
open class ServerException(val code: Int, message: String? = null) : IOException(message) {
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package ru.touchin.template.network.models
|
||||
|
||||
enum class TemplateApiError {
|
||||
|
||||
INVALID_PARAMETERS,
|
||||
|
||||
VALID_RESPONSE
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">My Library</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(Library.DAGGER)
|
||||
implementationModule(Module.RoboSwag.STORABLE)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_prefs" />
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
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<String, Boolean, Boolean> = PreferenceUtils
|
||||
.booleanStorable("TUTORIAL_STORABLE", sharedPreferences, false)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
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")
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template" />
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="common_global_yes" formatted="false">Да</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_ui" />
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
<manifest package="ru.touchin.template.core_utils" />
|
||||
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
plugins {
|
||||
id(Plugins.ANDROID_LIB_PLUGIN_WITH_DEFAULT_CONFIG)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
dagger()
|
||||
mvi()
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<manifest package="ru.touchin.template.feature_login">
|
||||
|
||||
</manifest>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ru.touchin.template.feature_login
|
||||
|
||||
import ru.touchin.template.feature_login.navigation.LoginCoordinator
|
||||
|
||||
interface LoginDeps {
|
||||
fun loginCoordinator(): LoginCoordinator
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
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<out ViewModel>
|
||||
|
||||
}
|
||||
|
||||
@AssistedModule
|
||||
@Module(includes = [AssistedInject_ViewModelAssistedFactoriesModule::class])
|
||||
abstract class ViewModelAssistedFactoriesModule
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package ru.touchin.template.feature_login.navigation
|
||||
|
||||
import ru.touchin.roboswag.navigation_base.scopes.FragmentScope
|
||||
|
||||
@FragmentScope
|
||||
interface LoginCoordinator {
|
||||
fun openMainScreen()
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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<EmptyState, LoginViewState, LoginViewAction, LoginViewModel>(R.layout.fragment_login),
|
||||
IHasComponent<LoginComponent> {
|
||||
|
||||
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())
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package ru.touchin.template.feature_login.presentation
|
||||
|
||||
import ru.touchin.roboswag.mvi_arch.marker.ViewAction
|
||||
|
||||
sealed class LoginViewAction : ViewAction {
|
||||
object GoToMainScreenButtonClicked : LoginViewAction()
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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<EmptyState, LoginViewAction, LoginViewState>(LoginViewState, arg0) {
|
||||
|
||||
override fun dispatchAction(action: LoginViewAction) {
|
||||
when (action) {
|
||||
LoginViewAction.GoToMainScreenButtonClicked -> coordinator.openMainScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedInject.Factory
|
||||
interface Factory : ViewModelAssistedFactory<LoginViewModel>
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package ru.touchin.template.feature_login.presentation
|
||||
|
||||
import ru.touchin.roboswag.mvi_arch.marker.ViewState
|
||||
|
||||
object LoginViewState : ViewState
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="8dp"
|
||||
android:text="Экран логина" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/go_to_main_screen_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="8dp"
|
||||
android:text="На главный экран" />
|
||||
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue