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
19 changes: 19 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 'com.google.devtools.ksp'
id 'com.google.dagger.hilt.android'
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}

android {
Expand Down Expand Up @@ -30,6 +33,9 @@ android {
kotlinOptions {
jvmTarget = '17'
}
buildFeatures{
buildConfig = true
}
}

dependencies {
Expand All @@ -38,7 +44,20 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.7'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7'
implementation 'androidx.fragment:fragment-ktx:1.8.5'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7'
implementation 'com.squareup.okhttp3:logging-interceptor:4.9.3'
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'

implementation "com.google.dagger:hilt-android:2.53"
ksp "com.google.dagger:hilt-compiler:2.53"
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ru.otus.basicarchitecture.presentation

import org.junit.Assert.*

import org.junit.Test

class AddressViewModelTest {

@Test
fun loadSuggestions() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.otus.basicarchitecture.presentation

import org.junit.Assert.*

class PersonalDataViewModelTest
4 changes: 4 additions & 0 deletions app/src/androidTest/java/utils/RxRule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package utils

class RxRule {
}
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=".HiltApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
11 changes: 11 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/HiltApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.otus.basicarchitecture

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

@HiltAndroidApp
class HiltApp : Application() {
override fun onCreate() {
super.onCreate()
}
}
10 changes: 10 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,20 @@ package ru.otus.basicarchitecture

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import dagger.hilt.android.AndroidEntryPoint
import ru.otus.basicarchitecture.presentation.PersonalDataFragment


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

if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragmentContainer, PersonalDataFragment())
.commit()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package ru.otus.basicarchitecture.api

import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.Body
import retrofit2.http.Headers
import retrofit2.http.POST
import ru.otus.basicarchitecture.BuildConfig
import ru.otus.basicarchitecture.model.AddressSuggestionRequest
import ru.otus.basicarchitecture.model.DadataResponse
import java.util.concurrent.TimeUnit


interface AddressApi {
@Headers("Content-Type: application/json", "Accept: application/json")
@POST("suggestions/api/4_1/rs/suggest/address")
suspend fun getAddressSuggestions(@Body request: AddressSuggestionRequest): DadataResponse
}

@Module
@InstallIn(SingletonComponent::class)
object AddressApiService {
private const val BASE_URL = "https://suggestions.dadata.ru/"
private const val AUTH_KEY = BuildConfig.DADATA_API_KEY

private val headerInterceptor = Interceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "Token $AUTH_KEY")
.build()
chain.proceed(request)
}

private val client: OkHttpClient = OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()


@get:Provides
val api: AddressApi = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(AddressApi::class.java)
}
14 changes: 14 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/data/TagsList.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.otus.basicarchitecture.data

import javax.inject.Inject
class TagsList @Inject constructor() {
val interests = listOf(
"Board games",
"Computer games",
"Hiking",
"Bird watching",
"DIY",
"Reading",
"Parties"
)
}
15 changes: 15 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/data/WizardCache.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ru.otus.basicarchitecture.data

import dagger.hilt.android.scopes.ActivityRetainedScoped
import javax.inject.Inject

@ActivityRetainedScoped
class WizardCache @Inject constructor() {
var name: String = ""
var surname: String = ""
var birthDate: Long = 0
var city: String = ""
var country: String = ""
var address: String = ""
var interests: List<String> = listOf()
}
5 changes: 5 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/model/Address.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.otus.basicarchitecture.model

data class AddressSuggestionRequest(val query: String)
data class DadataResponse(val suggestions: List<AddressSuggestion>)
data class AddressSuggestion(val value: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package ru.otus.basicarchitecture.presentation

import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Button
import androidx.fragment.app.Fragment
import com.google.android.material.textfield.MaterialAutoCompleteTextView
import dagger.hilt.android.AndroidEntryPoint
import ru.otus.basicarchitecture.R
import javax.inject.Inject

@AndroidEntryPoint
class AddressFragment : Fragment() {

@Inject
lateinit var viewModel: AddressViewModel

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_address, container, false)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

view.findViewById<MaterialAutoCompleteTextView>(R.id.address)
.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
viewModel.loadSuggestions(s.toString())
viewModel.address.value = s.toString()
}

override fun beforeTextChanged(
s: CharSequence?,
start: Int,
count: Int,
after: Int
) {
}

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})

viewModel.addressSuggestions.observe(viewLifecycleOwner) { suggestions ->
val adapter = ArrayAdapter(
requireContext(),
android.R.layout.simple_dropdown_item_1line,
suggestions
)
val address = view.findViewById<MaterialAutoCompleteTextView>(R.id.address)
address.setAdapter(adapter)

if (suggestions.isNotEmpty()) {
address.showDropDown()
}
}

view.findViewById<Button>(R.id.nextToTags).setOnClickListener {
viewModel.saveToCash()

parentFragmentManager.beginTransaction()
.replace(R.id.fragmentContainer, TagsFragment())
.addToBackStack(null)
.commit()

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.otus.basicarchitecture.presentation

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch
import ru.otus.basicarchitecture.api.AddressApiService.api
import ru.otus.basicarchitecture.data.WizardCache
import ru.otus.basicarchitecture.model.AddressSuggestionRequest
import javax.inject.Inject

class AddressViewModel @Inject constructor(private val wizardCache: WizardCache) : ViewModel() {
val address = MutableLiveData<String>()
val addressSuggestions = MutableLiveData<List<String>>()
fun loadSuggestions(search: String) {
if (search.isBlank()) {
addressSuggestions.value = emptyList()
return
}

viewModelScope.launch {
try {
val response = api.getAddressSuggestions(AddressSuggestionRequest(search))
addressSuggestions.value = response.suggestions.map { it.value }
} catch (e: Exception) {
addressSuggestions.value = emptyList()
}
}
}


fun saveToCash() {
wizardCache.address = address.value ?: ""
}
}
Loading