Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
2777304
Пробую сделать только одну вьюмодель
Aug 10, 2024
521dcf7
Пробую сделать только одну вьюмодель
Aug 10, 2024
fe356b1
Настроил иерархию папок
Aug 10, 2024
599380f
Создал 1 и 2 фрагмент (верстка)
Aug 10, 2024
4b67edd
Создал 1 и 2 фрагмент (верстка)
Aug 10, 2024
95e5c8e
Делаю переход по кнопкам
Aug 11, 2024
7c9148c
Импортнул Hilt
Aug 11, 2024
c5c8a89
Починил Hilt, сделал переход на второй фрагмент и даже созраняю введе…
Aug 11, 2024
4f84405
Создал WizardCache
Aug 14, 2024
f95b268
Создал WizardCache
Aug 14, 2024
c15cc5d
Сделал сохранение и валидацию данных но это не работает
Aug 14, 2024
ef9dd02
Сделал сохранение
Aug 14, 2024
e8c0b3d
Сделал переход на второй фрагмент, делаю валидацию даты
Aug 14, 2024
4ee48f3
Сделал переход на второй фрагмент, делаю валидацию даты
Aug 14, 2024
a7f81d0
Сделал тост неверной валидации полей
Aug 14, 2024
7d83624
Меняю макет для даты рождения
Aug 14, 2024
2cd8ebc
СДелал валидацию даты рождения
Aug 14, 2024
bf0e8d7
СДелал валидацию даты рождения
Aug 14, 2024
b8d115a
СДелал валидацию даты рождения
Aug 14, 2024
13311b2
СДелал валидацию даты рождения
Aug 15, 2024
5edd533
Сделал 2 пункт
Aug 15, 2024
1bfbb82
Сделал 3 пункт
Aug 15, 2024
604a96c
Сделал 3 пункт
Aug 15, 2024
7bb52f2
Сделал 3 пункт
Aug 15, 2024
767cfbb
Сделал 3 пункт
Aug 15, 2024
6233970
Сделал 4 пункт? правлю верстку
Aug 15, 2024
375cfec
Готово
Aug 15, 2024
e414463
Готово
Aug 15, 2024
c393863
Готово
Aug 15, 2024
7bb83af
Готово
Aug 15, 2024
443ea67
Готово
Aug 15, 2024
e748ec7
Сделал network
Aug 15, 2024
16a581d
Убрал лишнее
Aug 15, 2024
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: 31 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.dagger.hilt.android'
id 'kotlin-kapt'
}

android {
namespace 'ru.otus.basicarchitecture'
compileSdk 33
compileSdk 34

defaultConfig {
applicationId "ru.otus.basicarchitecture"
Expand All @@ -24,11 +26,11 @@ android {
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
}

Expand All @@ -38,7 +40,32 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.fragment:fragment-ktx:1.5.6'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

implementation 'com.google.android.flexbox:flexbox:3.0.0'


// Hilt
implementation 'com.google.dagger:hilt-android:2.52'
kapt 'com.google.dagger:hilt-compiler:2.52'

def lifecycle_version = "2.6.1"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
implementation 'androidx.activity:activity-ktx:1.7.2'

// Retrofit2
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'
}

kapt {
correctErrorTypes true
}
12 changes: 10 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<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=".di.MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -13,8 +15,14 @@
android:theme="@style/Theme.BasicArchitecture"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="false" />
android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
11 changes: 0 additions & 11 deletions app/src/main/java/ru/otus/basicarchitecture/MainActivity.kt

This file was deleted.

23 changes: 23 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/di/MyApplication.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ru.otus.basicarchitecture.di

import android.app.Application
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.HiltAndroidApp
import dagger.hilt.components.SingletonComponent
import ru.otus.basicarchitecture.repository.WizardCache
import javax.inject.Singleton

@HiltAndroidApp
class MyApplication : Application()

@InstallIn(SingletonComponent::class)
@Module
object CacheModule {
@Provides
@Singleton
fun provideWizardCache(): WizardCache {
return WizardCache()
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/network/DadataApi.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.otus.basicarchitecture.network

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

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

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,41 @@
package ru.otus.basicarchitecture.network

import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit

object DadataApiService {
private const val BASE_URL = "https://suggestions.dadata.ru/"
private const val API_KEY = "8fa10df494b20be347e86fb921643f852a13cc07"

private val client: OkHttpClient by lazy {
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
val headerInterceptor = Interceptor { chain ->
val request = chain.request().newBuilder()
.addHeader("Authorization", "Token $API_KEY")
.build()
chain.proceed(request)
}

OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.addInterceptor(headerInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
}

val api: DadataApi by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(DadataApi::class.java)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package ru.otus.basicarchitecture.repository

import javax.inject.Inject

class WizardCache @Inject constructor() {
var firstName: String? = null
var lastName: String? = null
var birthDate: String? = null
var country: String? = null
var city: String? = null
var address: String? = null
var interests: List<String> = emptyList()

fun setInterests(interests: Set<String>) {
this.interests = interests.toList()
}

}
24 changes: 24 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ru.otus.basicarchitecture.ui

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import dagger.hilt.android.AndroidEntryPoint
import ru.otus.basicarchitecture.ui.fragments.Fragment1
import ru.otus.basicarchitecture.R
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadFragment(Fragment1())
}

private fun loadFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, fragment)
transaction.addToBackStack("null")
transaction.commit()
}

}
107 changes: 107 additions & 0 deletions app/src/main/java/ru/otus/basicarchitecture/ui/fragments/Fragment1.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package ru.otus.basicarchitecture.ui.fragments

import android.os.Build
import androidx.fragment.app.viewModels
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.annotation.RequiresApi
import androidx.core.widget.addTextChangedListener
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import ru.otus.basicarchitecture.R
import ru.otus.basicarchitecture.viewmodels.Fragment1ViewModel
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit

@AndroidEntryPoint
class Fragment1 : Fragment() {
private val viewModel: Fragment1ViewModel by viewModels()

private lateinit var firstNameEditText: EditText
private lateinit var lastNameEditText: EditText
private lateinit var birthDateEditText: EditText
private lateinit var nextBtn: Button

private lateinit var toast: View

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


@RequiresApi(Build.VERSION_CODES.O)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

firstNameEditText = view.findViewById(R.id.name)
lastNameEditText = view.findViewById(R.id.surname)
birthDateEditText = view.findViewById(R.id.birthdate)
nextBtn = view.findViewById(R.id.fragment1Btn)
toast = view.findViewById(R.id.toast)

viewModel.firstName.observe(viewLifecycleOwner) {
if (it == null || it.isBlank()) {
firstNameEditText.error = "Поле не должно быть пустым"
} else {
firstNameEditText.error = null
}
}

viewModel.lastName.observe(viewLifecycleOwner) {
if (it == null || it.isBlank()) {
lastNameEditText.error = "Поле не должно быть пустым"
} else {
lastNameEditText.error = null
}
}

viewModel.birthDate.observe(viewLifecycleOwner) {
try {
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy")
val dob = LocalDate.parse(it, formatter)
val years = ChronoUnit.YEARS.between(dob, LocalDate.now())
if (years < 18) {
birthDateEditText.error = "Вам не исполнилось 18"
} else {
birthDateEditText.error = null
}
} catch (e: Exception) {
birthDateEditText.error = "Неверный формат даты"
}
}

viewModel.isFormValid.observe(viewLifecycleOwner) { isValid ->
nextBtn.isEnabled = isValid
}

firstNameEditText.addTextChangedListener { viewModel.firstName.value = it.toString() }
lastNameEditText.addTextChangedListener { viewModel.lastName.value = it.toString() }
birthDateEditText.addTextChangedListener { viewModel.birthDate.value = it.toString() }

nextBtn.setOnClickListener {
viewModel.firstName.value = firstNameEditText.text.toString()
viewModel.lastName.value = lastNameEditText.text.toString()
viewModel.birthDate.value = birthDateEditText.text.toString()

if (nextBtn.isEnabled) {
viewModel.saveData()
parentFragmentManager.beginTransaction()
.replace(R.id.fragment_container, Fragment2())
.addToBackStack(null)
.commit()
} else {
Snackbar.make(toast, "Валидация не пройдена", Snackbar.LENGTH_SHORT).show()
}
}
}

}
Loading