Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'org.jetbrains.kotlin.plugin.serialization' version "2.1.0"
id 'com.google.devtools.ksp' version '2.1.0-1.0.29'
id 'com.google.dagger.hilt.android' version '2.57.1'
}

android {
Expand All @@ -15,6 +18,13 @@ android {
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

def properties = new Properties()
properties.load(rootProject.file("local.properties").newDataInputStream())

def apiKey = properties.getProperty("API_KEY") ?: ""
buildConfigField "String", "API_KEY", apiKey

}

buildTypes {
Expand All @@ -23,6 +33,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}

buildFeatures {
buildConfig = true
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
Expand All @@ -41,4 +56,24 @@ dependencies {
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'

//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.11.0'

//Serialization
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.3'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3'
implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0'

//OkHttp (MediaType & Interceptor)
implementation 'com.squareup.okhttp3:okhttp:4.12.0'

//ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7'
implementation 'androidx.activity:activity-ktx:1.10.0'
implementation 'androidx.fragment:fragment-ktx:1.8.9'

//Hilt
implementation 'com.google.dagger:hilt-android:2.57.1'
ksp 'com.google.dagger:hilt-compiler:2.57.1'
}
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".BasicArchitectureApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.otus.basicarchitecture

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class BasicArchitectureApp: Application()
7 changes: 7 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ package ru.otus.basicarchitecture

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import dagger.hilt.android.AndroidEntryPoint
import ru.otus.basicarchitecture.ui.NameFragment

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, NameFragment())
.commit()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.otus.basicarchitecture.data.localbase

import ru.otus.basicarchitecture.domain.Interests

data class UserData(
val name: String = "",
val surname: String = "",
val age: Int? = null,

val address: String = "",

val interests: List<Interests> = emptyList()
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package ru.otus.basicarchitecture.data.localbase

import ru.otus.basicarchitecture.domain.Interests
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class WizardCache @Inject constructor() {
private var user = UserData()

fun updateUserPersonalInformation(name: String, surname: String, age: Int?) {
user = user.copy(name = name, surname = surname, age = age)
}

fun updateUserAddress(address: String) {
user = user.copy(address = address)
}

fun updateUserInterests(interests: List<Interests>) {
user = user.copy(interests = interests)
}

fun getUserInfo(): UserData {
return user
}

fun clearUserInfo() {
user = UserData()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.otus.basicarchitecture.data.localbase

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.otus.basicarchitecture.domain.WizardCacheService

@Module
@InstallIn(SingletonComponent::class)
abstract class WizardCacheModule {

@Binds
abstract fun bindWizardCacheService(impl: WizardCacheServiceImpl): WizardCacheService
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ru.otus.basicarchitecture.data.localbase

import ru.otus.basicarchitecture.domain.Interests
import ru.otus.basicarchitecture.domain.WizardCacheService
import javax.inject.Inject

class WizardCacheServiceImpl @Inject constructor(private val wizardCache: WizardCache) :
WizardCacheService {

override fun updateUserPersonalInformation(
name: String,
surname: String,
age: Int?
) {
wizardCache.updateUserPersonalInformation(name = name, surname = surname, age = age)
}

override fun updateUserAddress(address: String) {
wizardCache.updateUserAddress(address = address)
}

override fun updateUserInterests(interests: List<Interests>) {
wizardCache.updateUserInterests(interests = interests)
}

override fun getUserInfo(): UserData = wizardCache.getUserInfo()

override fun clearUserInfo() {
wizardCache.clearUserInfo()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.otus.basicarchitecture.data.network

import retrofit2.http.Body
import retrofit2.http.POST

interface AddressApi {
@POST("api/4_1/rs/suggest/address")
suspend fun getHints(
@Body query: AddressQuery
): AddressResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package ru.otus.basicarchitecture.data.network

import kotlinx.serialization.Serializable

@Serializable
data class AddressQuery(
val query: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.otus.basicarchitecture.data.network

import kotlinx.serialization.Serializable

@Serializable
data class AddressResponse(
val suggestions: List<AddressSuggestion>
)

@Serializable
data class AddressSuggestion(
val value: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.otus.basicarchitecture.data.network

import ru.otus.basicarchitecture.domain.AddressService
import javax.inject.Inject

class AddressServiceImpl @Inject constructor(private val addressApi: AddressApi) : AddressService {
override suspend fun getHints(query: AddressQuery): AddressResponse {
return addressApi.getHints(query = query)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.otus.basicarchitecture.data.network

import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import ru.otus.basicarchitecture.domain.AddressService

@Module
@InstallIn(SingletonComponent::class)
abstract class AddressServiceModule {

@Binds
abstract fun bindAddressService(impl: AddressServiceImpl): AddressService
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ru.otus.basicarchitecture.data.network

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import okhttp3.Request
import retrofit2.Retrofit
import ru.otus.basicarchitecture.BuildConfig
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

private const val apiKey = BuildConfig.API_KEY

private const val BASE_URL = "https://suggestions.dadata.ru/suggestions/"

private val json = Json {
ignoreUnknownKeys = true
}

//TODO api key вынести
private fun okHttpClient(
apiKey: String = NetworkModule.apiKey
) = OkHttpClient().newBuilder()
.addInterceptor(
Interceptor { chain ->
val request: Request = chain.request()
.newBuilder()
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("Authorization", "Token $apiKey")
.build()
chain.proceed(request)
}
)

@Provides
@Singleton
fun providesRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient().build())
.addConverterFactory(
json.asConverterFactory("application/json".toMediaType())
)
.build()
}

@Provides
@Singleton
fun provideAddressHintService(retrofit: Retrofit): AddressApi {
return retrofit.create(AddressApi::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ru.otus.basicarchitecture.domain

import ru.otus.basicarchitecture.data.network.AddressQuery
import ru.otus.basicarchitecture.data.network.AddressResponse

interface AddressService {
suspend fun getHints(
query: AddressQuery
): AddressResponse
}
16 changes: 16 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/domain/Interests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.otus.basicarchitecture.domain

enum class Interests {
MUSIC,
MUSEUMS,
MODERN_ART,
CARS,
BICYCLES,
PARTIES,
THEATRE,
MOVIES,
GARDENING,
HIKING,
LITERATURE,
CRAFT
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.otus.basicarchitecture.domain

import ru.otus.basicarchitecture.data.localbase.UserData

interface WizardCacheService {
fun updateUserPersonalInformation(name: String, surname: String, age: Int?)
fun updateUserAddress(address: String)
fun updateUserInterests(interests: List<Interests>)
fun getUserInfo(): UserData
fun clearUserInfo()
}
Loading