diff --git a/app/build.gradle b/app/build.gradle
index 4d45170..07e8fa5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,7 +3,7 @@ plugins {
id 'org.jetbrains.kotlin.android'
id 'kotlin-parcelize'
id 'kotlin-kapt'
- id 'androidx.navigation.safeargs'
+ id 'androidx.navigation.safeargs.kotlin'
}
android {
@@ -24,7 +24,6 @@ android {
dataBinding true
}
-
buildTypes {
release {
minifyEnabled false
@@ -90,6 +89,12 @@ dependencies {
debugImplementation "com.squareup.okhttp3:logging-interceptor:$okhttp"
// endregion
+ def dagger_version = "2.41"
+ implementation "com.google.dagger:dagger:$dagger_version"
+ implementation "com.google.dagger:dagger-android-support:$dagger_version"
+ kapt "com.google.dagger:dagger-compiler:$dagger_version"
+ kapt "com.google.dagger:dagger-android-processor:$dagger_version"
+
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 013d722..f74a81e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,10 +4,11 @@
-
+
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt
new file mode 100644
index 0000000..3c6dbc3
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt
@@ -0,0 +1,19 @@
+package ru.itis.karakurik.androidLab2
+
+import android.app.Application
+import ru.itis.karakurik.androidLab2.di.AppComponent
+import ru.itis.karakurik.androidLab2.di.DaggerAppComponent
+
+class WeatherApp : Application() {
+
+ lateinit var appComponent: AppComponent
+
+ override fun onCreate() {
+ super.onCreate()
+ appComponent = DaggerAppComponent
+ .builder()
+// .context(context = this)
+ .application(this)
+ .build()
+ }
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt
index 84c2972..a770337 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt
@@ -6,6 +6,7 @@ import ru.itis.karakurik.androidLab2.data.api.response.citiesResponse.CitiesResp
import ru.itis.karakurik.androidLab2.data.api.response.weatherResponse.WeatherResponse
interface Api {
+
@GET("weather")
suspend fun getWeather(@Query("q") city: String): WeatherResponse
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherIconUrlMapper.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherIconUrlMapper.kt
index 77f6917..e80f2ea 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherIconUrlMapper.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherIconUrlMapper.kt
@@ -1,6 +1,11 @@
package ru.itis.karakurik.androidLab2.data.api.mapper
-class WeatherIconUrlMapper {
+import javax.inject.Inject
+import javax.inject.Singleton
+
+@Singleton
+class WeatherIconUrlMapper @Inject constructor() {
+
fun mapToLargeIcon(iconId: String): String {
return "http://openweathermap.org/img/wn/${iconId}@2x.png"
}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherMapper.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherMapper.kt
index f10e35c..d4fd093 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherMapper.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherMapper.kt
@@ -3,11 +3,15 @@ package ru.itis.karakurik.androidLab2.data.api.mapper
import ru.itis.karakurik.androidLab2.data.api.response.citiesResponse.City
import ru.itis.karakurik.androidLab2.data.api.response.weatherResponse.WeatherResponse
import ru.itis.karakurik.androidLab2.domain.entity.Weather
+import javax.inject.Inject
+import javax.inject.Singleton
-class WeatherMapper(
+@Singleton
+class WeatherMapper @Inject constructor(
private val windDegMapper: WindDegMapper,
private val weatherIconUrlMapper: WeatherIconUrlMapper
) {
+
fun map(weatherResponse: WeatherResponse) : Weather = Weather(
id = weatherResponse.id,
name = weatherResponse.name,
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt
index 9360b16..f1290e2 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt
@@ -1,8 +1,11 @@
package ru.itis.karakurik.androidLab2.data.api.mapper
import ru.itis.karakurik.androidLab2.domain.enum.WindDeg
+import javax.inject.Inject
+import javax.inject.Singleton
-class WindDegMapper {
+@Singleton
+class WindDegMapper @Inject constructor() {
fun map(deg: Int) : WindDeg {
return when ((deg / 45 + 2* (deg%45) / 45) * 45 % 360) {
0 -> WindDeg.N
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt
similarity index 77%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt
index d109d54..b8497c8 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt
@@ -1,11 +1,14 @@
-package ru.itis.karakurik.androidLab2.domain.repository
+package ru.itis.karakurik.androidLab2.data.api.repository
-import ru.itis.karakurik.androidLab2.BuildConfig
import ru.itis.karakurik.androidLab2.data.api.Api
import ru.itis.karakurik.androidLab2.data.api.mapper.WeatherMapper
import ru.itis.karakurik.androidLab2.domain.entity.Weather
+import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository
+import javax.inject.Inject
+import javax.inject.Singleton
-class WeatherRepositoryImpl(
+@Singleton
+class WeatherRepositoryImpl @Inject constructor(
private val api: Api,
private val weatherMapper: WeatherMapper
) : WeatherRepository {
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt
new file mode 100644
index 0000000..9098c3f
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt
@@ -0,0 +1,37 @@
+package ru.itis.karakurik.androidLab2.di
+
+import dagger.BindsInstance
+import dagger.Component
+import ru.itis.karakurik.androidLab2.WeatherApp
+import ru.itis.karakurik.androidLab2.di.module.AppModule
+import ru.itis.karakurik.androidLab2.di.module.NetModule
+import ru.itis.karakurik.androidLab2.di.module.RepoModule
+import ru.itis.karakurik.androidLab2.di.module.ViewModelModule
+import ru.itis.karakurik.androidLab2.presentation.MainActivity
+import ru.itis.karakurik.androidLab2.presentation.fragments.cities.SearchFragment
+import ru.itis.karakurik.androidLab2.presentation.fragments.weather.DetailsFragment
+import javax.inject.Singleton
+
+@Singleton
+@Component(
+ modules = [
+ AppModule::class,
+ NetModule::class,
+ RepoModule::class,
+ ViewModelModule::class
+ ]
+)
+interface AppComponent {
+
+ fun inject(mainActivity: MainActivity)
+ fun inject(searchFragment: SearchFragment)
+ fun inject(detailsFragment: DetailsFragment)
+
+ @Component.Builder
+ interface Builder {
+ fun build(): AppComponent
+
+ @BindsInstance
+ fun application(application: WeatherApp): Builder
+ }
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/DiContainer.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/DiContainer.kt
deleted file mode 100644
index 4569eca..0000000
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/DiContainer.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-package ru.itis.karakurik.androidLab2.di
-
-import kotlinx.coroutines.Dispatchers
-import okhttp3.OkHttpClient
-import okhttp3.logging.HttpLoggingInterceptor
-import retrofit2.Retrofit
-import retrofit2.converter.gson.GsonConverterFactory
-import ru.itis.karakurik.androidLab2.BuildConfig
-import ru.itis.karakurik.androidLab2.data.api.Api
-import ru.itis.karakurik.androidLab2.data.api.mapper.WeatherIconUrlMapper
-import ru.itis.karakurik.androidLab2.data.api.mapper.WeatherMapper
-import ru.itis.karakurik.androidLab2.data.api.mapper.WindDegMapper
-import ru.itis.karakurik.androidLab2.di.interceptors.ApiKeyInterceptor
-import ru.itis.karakurik.androidLab2.di.interceptors.LangInterceptor
-import ru.itis.karakurik.androidLab2.di.interceptors.UnitsInterceptor
-import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository
-import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepositoryImpl
-import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherListUseCase
-import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
-
-private const val BASE_URL = "https://api.openweathermap.org/data/2.5/"
-
-object DiContainer {
- private val okhttp: OkHttpClient by lazy {
- OkHttpClient.Builder()
- .addInterceptor(ApiKeyInterceptor())
- .addInterceptor(UnitsInterceptor())
- .addInterceptor(LangInterceptor())
- .also {
- if (BuildConfig.DEBUG) {
- it.addInterceptor(
- HttpLoggingInterceptor()
- .setLevel(
- HttpLoggingInterceptor.Level.BODY
- )
- )
- }
- }
- .build()
- }
-
- val api: Api by lazy {
- Retrofit.Builder()
- .baseUrl(BASE_URL)
- .client(okhttp)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
- .create(Api::class.java)
- }
-
- private val windDegMapper: WindDegMapper by lazy {
- WindDegMapper()
- }
-
- private val weatherIconUrlMapper by lazy {
- WeatherIconUrlMapper()
- }
-
- private val weatherMapper: WeatherMapper by lazy {
- WeatherMapper(
- windDegMapper,
- weatherIconUrlMapper
- )
- }
-
- val weatherRepository: WeatherRepository by lazy {
- WeatherRepositoryImpl(
- api,
- weatherMapper
- )
- }
-
- val getWeatherUseCase: GetWeatherUseCase by lazy {
- GetWeatherUseCase(weatherRepository, Dispatchers.Default)
- }
-
- val getWeatherListUseCase: GetWeatherListUseCase by lazy {
- GetWeatherListUseCase(weatherRepository, Dispatchers.Default)
- }
-}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorAnnotation.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorAnnotation.kt
new file mode 100644
index 0000000..cab92aa
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorAnnotation.kt
@@ -0,0 +1,19 @@
+package ru.itis.karakurik.androidLab2.di.annotation
+
+import javax.inject.Qualifier
+
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class ApiKey
+
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Units
+
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Lang
+
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+annotation class Logger
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt
new file mode 100644
index 0000000..a3744be
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt
@@ -0,0 +1,14 @@
+package ru.itis.karakurik.androidLab2.di.annotation
+
+import dagger.MapKey
+import okhttp3.Interceptor
+import kotlin.reflect.KClass
+
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(AnnotationRetention.RUNTIME)
+@MapKey
+annotation class InterceptorKey(val value: KClass)
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/ViewModelKey.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/ViewModelKey.kt
new file mode 100644
index 0000000..f93d8b3
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/ViewModelKey.kt
@@ -0,0 +1,14 @@
+package ru.itis.karakurik.androidLab2.di.annotation
+
+import androidx.lifecycle.ViewModel
+import dagger.MapKey
+import kotlin.reflect.KClass
+
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@Retention(AnnotationRetention.RUNTIME)
+@MapKey
+annotation class ViewModelKey(val value: KClass)
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt
deleted file mode 100644
index 3c36c4a..0000000
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.itis.karakurik.androidLab2.di.interceptors
-
-import okhttp3.Interceptor
-import okhttp3.Response
-
-private const val API_KEY = "56fc6c6cb76c0864b4cd055080568268"
-private const val QUERY_API_KEY = "appid"
-
-class ApiKeyInterceptor : Interceptor {
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val newUrl = request.url.newBuilder()
- .addQueryParameter(QUERY_API_KEY, API_KEY)
- .build()
-
- return chain.proceed(
- request.newBuilder()
- .url(newUrl)
- .build()
- )
- }
-}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt
deleted file mode 100644
index f604354..0000000
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.itis.karakurik.androidLab2.di.interceptors
-
-import okhttp3.Interceptor
-import okhttp3.Response
-
-private const val QUERY_LANG = "lang"
-private const val LANG = "RU"
-
-class LangInterceptor : Interceptor {
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val newUrl = request.url.newBuilder()
- .addQueryParameter(QUERY_LANG, LANG)
- .build()
-
- return chain.proceed(
- request.newBuilder()
- .url(newUrl)
- .build()
- )
- }
-}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt
deleted file mode 100644
index bc6a739..0000000
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-package ru.itis.karakurik.androidLab2.di.interceptors
-
-import okhttp3.Interceptor
-import okhttp3.Response
-
-private const val QUERY_UNITS = "units"
-private const val UNITS = "metric"
-
-class UnitsInterceptor : Interceptor {
- override fun intercept(chain: Interceptor.Chain): Response {
- val request = chain.request()
- val newUrl = request.url.newBuilder()
- .addQueryParameter(QUERY_UNITS, UNITS)
- .build()
-
- return chain.proceed(
- request.newBuilder()
- .url(newUrl)
- .build()
- )
- }
-}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt
new file mode 100644
index 0000000..9b81fa3
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt
@@ -0,0 +1,57 @@
+package ru.itis.karakurik.androidLab2.di.module
+
+import android.content.Context
+import androidx.recyclerview.widget.LinearSmoothScroller
+import androidx.recyclerview.widget.RecyclerView
+import com.google.android.gms.location.LocationServices
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import ru.itis.karakurik.androidLab2.WeatherApp
+import javax.inject.Qualifier
+
+@Module
+class AppModule {
+
+ @Provides
+ fun provideContext(weatherApp: WeatherApp): Context = weatherApp.applicationContext
+
+ @Provides
+ @DefaultDispatcher
+ fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
+
+ @Qualifier
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class DefaultDispatcher
+
+ @Provides
+ @MainDispatcher
+ fun provideMainDispatcher(): CoroutineDispatcher = Dispatchers.Main
+
+ @Qualifier
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class MainDispatcher
+
+ @Provides
+ @IODispatcher
+ fun provideIODispatcher(): CoroutineDispatcher = Dispatchers.IO
+
+ @Qualifier
+ @Retention(AnnotationRetention.RUNTIME)
+ annotation class IODispatcher
+
+ @Provides
+ fun provideSmoothScroller(
+ context: Context
+ ): RecyclerView.SmoothScroller = object : LinearSmoothScroller(context) {
+ override fun getVerticalSnapPreference(): Int {
+ return SNAP_TO_START
+ }
+ }
+
+ @Provides
+ fun provideFusedLocationProviderClient(
+ context: Context
+ ) = LocationServices.getFusedLocationProviderClient(context)
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/NetModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/NetModule.kt
new file mode 100644
index 0000000..f72778f
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/NetModule.kt
@@ -0,0 +1,147 @@
+package ru.itis.karakurik.androidLab2.di.module
+
+import dagger.Module
+import dagger.Provides
+import okhttp3.Cache
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import ru.itis.karakurik.androidLab2.BuildConfig
+import ru.itis.karakurik.androidLab2.data.api.Api
+import ru.itis.karakurik.androidLab2.di.annotation.ApiKey
+import ru.itis.karakurik.androidLab2.di.annotation.Lang
+import ru.itis.karakurik.androidLab2.di.annotation.Logger
+import ru.itis.karakurik.androidLab2.di.annotation.Units
+import java.io.File
+import javax.inject.Qualifier
+
+private const val BASE_URL = "https://api.openweathermap.org/data/2.5/"
+private const val API_KEY = "56fc6c6cb76c0864b4cd055080568268"
+private const val QUERY_API_KEY = "appid"
+private const val QUERY_LANG = "lang"
+private const val LANG = "RU"
+private const val QUERY_UNITS = "units"
+private const val UNITS = "metric"
+
+@Module
+class NetModule {
+
+ @Provides
+ @ApiKey
+ fun apiKeyInterceptor(): Interceptor = Interceptor { chain ->
+ val request = chain.request()
+ val newUrl = request.url.newBuilder()
+ .addQueryParameter(QUERY_API_KEY, API_KEY)
+ .build()
+
+ chain.proceed(
+ request.newBuilder()
+ .url(newUrl)
+ .build()
+ )
+ }
+
+ @Provides
+ @Lang
+ fun langInterceptor(): Interceptor = Interceptor { chain ->
+ val request = chain.request()
+ val newUrl = request.url.newBuilder()
+ .addQueryParameter(QUERY_LANG, LANG)
+ .build()
+
+ chain.proceed(
+ request.newBuilder()
+ .url(newUrl)
+ .build()
+ )
+ }
+
+ @Provides
+ @Units
+ fun unitsInterceptor(): Interceptor = Interceptor { chain ->
+ val request = chain.request()
+ val newUrl = request.url.newBuilder()
+ .addQueryParameter(QUERY_UNITS, UNITS)
+ .build()
+
+ chain.proceed(
+ request.newBuilder()
+ .url(newUrl)
+ .build()
+ )
+ }
+
+ @Provides
+ @Logger
+ fun provideLoggingInterceptor(): Interceptor {
+ return HttpLoggingInterceptor()
+ .setLevel(
+ HttpLoggingInterceptor.Level.BODY
+ )
+ }
+
+ @Provides
+ fun provideCacheDirectory(): File {
+ return File("cache");
+ }
+
+ @Qualifier
+ annotation class CacheMaxSize
+
+ @CacheMaxSize
+ @Provides
+ fun provideCacheMaxSize(): Long {
+ return 50 * 1024 * 1024;
+ }
+
+ @Provides
+ fun provideCache(
+ cacheDirectory: File,
+ @CacheMaxSize cacheMaxSize: Long
+ ): Cache {
+ return Cache(cacheDirectory, cacheMaxSize)
+ }
+
+ @Provides
+ fun provideOkhttpClient(
+ cache: Cache,
+ @ApiKey apiKeyInterceptor: Interceptor,
+ @Lang langInterceptor: Interceptor,
+ @Units unitsInterceptor: Interceptor,
+ @Logger loggingInterceptor: Interceptor,
+ ): OkHttpClient =
+ OkHttpClient.Builder()
+ .cache(cache)
+ .addInterceptor(apiKeyInterceptor)
+ .addInterceptor(langInterceptor)
+ .addInterceptor(unitsInterceptor)
+ .also {
+ if (BuildConfig.DEBUG) {
+ it.addInterceptor(
+ loggingInterceptor
+ )
+ }
+ }
+ .build()
+
+ @Provides
+ fun provideApi(
+ okHttpClient: OkHttpClient,
+ baseUrl: String,
+ gsonConverter: GsonConverterFactory,
+ ): Api =
+ Retrofit.Builder()
+ .baseUrl(baseUrl)
+ .client(okHttpClient)
+ .addConverterFactory(gsonConverter)
+ .build()
+ .create(Api::class.java)
+
+ @Provides
+ fun provideBaseUrl(): String = BASE_URL
+
+ @Provides
+ fun provideGsonConverter(): GsonConverterFactory = GsonConverterFactory.create()
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt
new file mode 100644
index 0000000..d2516ac
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt
@@ -0,0 +1,17 @@
+package ru.itis.karakurik.androidLab2.di.module
+
+import dagger.Binds
+import dagger.Module
+import ru.itis.karakurik.androidLab2.data.api.repository.WeatherRepositoryImpl
+import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository
+import javax.inject.Singleton
+
+@Module
+interface RepoModule {
+
+ @Singleton
+ @Binds
+ fun bindWeatherRepository(
+ impl: WeatherRepositoryImpl,
+ ): WeatherRepository
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/ViewModelModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/ViewModelModule.kt
new file mode 100644
index 0000000..8c8f0df
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/ViewModelModule.kt
@@ -0,0 +1,34 @@
+package ru.itis.karakurik.androidLab2.di.module
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoMap
+import ru.itis.karakurik.androidLab2.di.annotation.ViewModelKey
+import ru.itis.karakurik.androidLab2.presentation.common.utils.AppViewModelFactory
+import ru.itis.karakurik.androidLab2.presentation.fragments.cities.CityListViewModel
+import ru.itis.karakurik.androidLab2.presentation.fragments.weather.WeatherViewModel
+
+@Module
+interface ViewModelModule {
+
+ @Binds
+ fun bindViewModelFactory(
+ factory: AppViewModelFactory
+ ): ViewModelProvider.Factory
+
+ @Binds
+ @IntoMap
+ @ViewModelKey(CityListViewModel::class)
+ fun bindCityListViewModel(
+ viewModel: CityListViewModel
+ ): ViewModel
+
+ @Binds
+ @IntoMap
+ @ViewModelKey(WeatherViewModel::class)
+ fun bindWeatherViewModel(
+ viewModel: WeatherViewModel
+ ): ViewModel
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherListUseCase.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherListUseCase.kt
index 4058eee..8385944 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherListUseCase.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherListUseCase.kt
@@ -3,13 +3,18 @@ package ru.itis.karakurik.androidLab2.domain.usecase
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import ru.itis.karakurik.androidLab2.di.module.AppModule
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository
+import javax.inject.Inject
+import javax.inject.Singleton
-class GetWeatherListUseCase(
+@Singleton
+class GetWeatherListUseCase @Inject constructor(
private val weatherRepository: WeatherRepository,
- private val dispatcher: CoroutineDispatcher = Dispatchers.Main
+ @AppModule.DefaultDispatcher private val dispatcher: CoroutineDispatcher = Dispatchers.Main
) {
+
suspend operator fun invoke(lat: Double, lon: Double, cnt: Int): MutableList {
return withContext(dispatcher) {
weatherRepository.getWeatherList(lat, lon, cnt)
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt
index a0c0ce6..e36d6fd 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt
@@ -3,12 +3,17 @@ package ru.itis.karakurik.androidLab2.domain.usecase
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
+import ru.itis.karakurik.androidLab2.di.module.AppModule
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository
+import javax.inject.Inject
+import javax.inject.Named
+import javax.inject.Singleton
-class GetWeatherUseCase(
+@Singleton
+class GetWeatherUseCase @Inject constructor(
private val weatherRepository: WeatherRepository,
- private val dispatcher: CoroutineDispatcher = Dispatchers.Main
+ @AppModule.DefaultDispatcher private val dispatcher: CoroutineDispatcher = Dispatchers.Main
) {
suspend operator fun invoke(city: String): Weather {
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt
similarity index 83%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt
index 403f339..e63d555 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt
@@ -1,10 +1,11 @@
-package ru.itis.karakurik.androidLab2.presentation.activities
+package ru.itis.karakurik.androidLab2.presentation
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import by.kirich1409.viewbindingdelegate.viewBinding
import ru.itis.karakurik.androidLab2.R
+import ru.itis.karakurik.androidLab2.WeatherApp
import ru.itis.karakurik.androidLab2.databinding.ActivityMainBinding
import ru.itis.karakurik.androidLab2.extentions.findController
@@ -13,6 +14,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
private var controller: NavController? = null
override fun onCreate(savedInstanceState: Bundle?) {
+ (application as WeatherApp).appComponent.inject(this)
super.onCreate(savedInstanceState)
controller = binding.navHostFragmentMain.id.let { findController(it) }
}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/convertors/TempColorConverter.kt
similarity index 85%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/convertors/TempColorConverter.kt
index 365a004..38a656f 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/convertors/TempColorConverter.kt
@@ -1,8 +1,9 @@
-package ru.itis.karakurik.androidLab2.presentation.convertors
+package ru.itis.karakurik.androidLab2.presentation.common.convertors
import ru.itis.karakurik.androidLab2.R
object TempColorConverter {
+
fun getColor(temp: Double): Int {
return when {
temp < -20 -> R.color.temp_less_minus20
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt
new file mode 100644
index 0000000..3df0820
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt
@@ -0,0 +1,19 @@
+package ru.itis.karakurik.androidLab2.presentation.common.utils
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import javax.inject.Inject
+import javax.inject.Provider
+import javax.inject.Singleton
+
+class AppViewModelFactory @Inject constructor(
+ private val viewModelMap: Map, @JvmSuppressWildcards Provider>
+) : ViewModelProvider.Factory {
+ override fun create(modelClass: Class): T {
+ val result = viewModelMap[modelClass] ?: viewModelMap.entries.firstOrNull {
+ modelClass.isAssignableFrom(it.key)
+ }?.value ?: throw IllegalArgumentException("Unknown model class $modelClass")
+ @Suppress("UNCHECKED_CAST")
+ return result.get() as T
+ }
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/Event.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt
similarity index 100%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/Event.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/CityListViewModel.kt
similarity index 61%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/CityListViewModel.kt
index 7f3e983..173ac7e 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/CityListViewModel.kt
@@ -1,6 +1,5 @@
-package ru.itis.karakurik.androidLab2.presentation
+package ru.itis.karakurik.androidLab2.presentation.fragments.cities
-import Event
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
@@ -9,32 +8,18 @@ import kotlinx.coroutines.launch
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherListUseCase
import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
+import javax.inject.Inject
-class MainViewModel(
+class CityListViewModel @Inject constructor(
private val getWeatherUseCase: GetWeatherUseCase,
private val getWeatherListUseCase: GetWeatherListUseCase
) : ViewModel() {
- private val _weather: MutableLiveData> = MutableLiveData()
- val weather: LiveData> = _weather
-
private val _weatherList: MutableLiveData>> = MutableLiveData()
val weatherList: LiveData>> = _weatherList
- private val _cityId: MutableLiveData>> = MutableLiveData()
- val cityId: LiveData>> = _cityId
-
- fun onGetWeather(cityId : Int) {
- viewModelScope.launch {
- kotlin.runCatching {
- getWeatherUseCase(cityId)
- }.onSuccess {
- _weather.value = Result.success(it)
- }.onFailure {
- _weather.value = Result.failure(it)
- }
- }
- }
+ private val _cityId: MutableLiveData> = MutableLiveData()
+ val cityId: LiveData> = _cityId
fun onGetWeatherList(lat: Double, lon: Double, cnt: Int) {
viewModelScope.launch {
@@ -53,9 +38,9 @@ class MainViewModel(
kotlin.runCatching {
getWeatherUseCase(cityName).id
}.onSuccess {
- _cityId.value = Event(Result.success(it))
+ _cityId.value = Result.success(it)
}.onFailure {
- _cityId.value = Event(Result.failure(it))
+ _cityId.value = Result.failure(it)
}
}
}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/SearchFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt
similarity index 78%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/SearchFragment.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt
index 24e22c1..03f64ee 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/SearchFragment.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt
@@ -1,4 +1,4 @@
-package ru.itis.karakurik.androidLab2.presentation.fragments.list
+package ru.itis.karakurik.androidLab2.presentation.fragments.cities
import android.Manifest.permission.*
import android.content.Intent
@@ -13,25 +13,21 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.SearchView
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
-import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import com.google.android.gms.location.FusedLocationProviderClient
-import com.google.android.gms.location.LocationServices
import com.google.android.material.snackbar.Snackbar
-import ru.itis.karakurik.androidLab2.R
+import ru.itis.karakurik.androidLab2.WeatherApp
import ru.itis.karakurik.androidLab2.databinding.FragmentSearchBinding
-import ru.itis.karakurik.androidLab2.di.DiContainer
-import ru.itis.karakurik.androidLab2.presentation.MainViewModel
-import ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler.ListRecyclerAdapter
-import ru.itis.karakurik.androidLab2.presentation.utils.ViewModelFactory
+import ru.itis.karakurik.androidLab2.presentation.fragments.cities.recycler.ListRecyclerAdapter
import timber.log.Timber
+import javax.inject.Inject
private const val COUNT_OF_CITIES_IN_LIST = 20
private const val DEFAULT_LAT = 55.7887
private const val DEFAULT_LON = 49.1221
-private const val TRANSITION_NAME = "transition_name"
class SearchFragment : Fragment() {
@@ -48,20 +44,22 @@ class SearchFragment : Fragment() {
private var listRecyclerAdapter: ListRecyclerAdapter? = null
private var userLat: Double = DEFAULT_LAT
private var userLon: Double = DEFAULT_LON
- private var userLocation: FusedLocationProviderClient? = null
- private val viewModel by lazy {
- ViewModelProvider(
- requireActivity(),
- ViewModelFactory(
- DiContainer.getWeatherUseCase,
- DiContainer.getWeatherListUseCase
- )
- )[MainViewModel::class.java]
+ @Inject
+ lateinit var factory: ViewModelProvider.Factory
+
+ private val viewModel: CityListViewModel by viewModels {
+ factory
}
+ @Inject
+ lateinit var smoothScroller: RecyclerView.SmoothScroller
+
+ @Inject
+ lateinit var userLocation: FusedLocationProviderClient
+
private val requestPermissions =
- registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { it ->
+ registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
var allPermissionsGranted = true
for (granted in it.values) {
allPermissionsGranted = allPermissionsGranted and granted
@@ -77,12 +75,9 @@ class SearchFragment : Fragment() {
}
}
- private val smoothScroller: RecyclerView.SmoothScroller by lazy {
- object : LinearSmoothScroller(context) {
- override fun getVerticalSnapPreference(): Int {
- return SNAP_TO_START
- }
- }
+ override fun onCreate(savedInstanceState: Bundle?) {
+ (activity?.application as WeatherApp).appComponent.inject(this)
+ super.onCreate(savedInstanceState)
}
override fun onCreateView(
@@ -109,22 +104,20 @@ class SearchFragment : Fragment() {
private fun initObservers() {
with(viewModel) {
- cityId.observe(viewLifecycleOwner) { event ->
- event.getContentIfNotHandled()?.let { result ->
- result.fold(
- onSuccess = {
- showDetailsFragment(it)
- },
- onFailure = {
- Toast.makeText(
- context,
- "Не удалось найти такой город",
- Toast.LENGTH_LONG
- ).show()
- Timber.d("Do not found city")
- }
- )
- }
+ cityId.observe(viewLifecycleOwner) { result ->
+ result.fold(
+ onSuccess = {
+ showDetailsFragment(it)
+ },
+ onFailure = {
+ Toast.makeText(
+ context,
+ "Не удалось найти такой город",
+ Toast.LENGTH_LONG
+ ).show()
+ Timber.d("Do not found city")
+ }
+ )
}
weatherList.observe(viewLifecycleOwner) { result ->
@@ -176,8 +169,7 @@ class SearchFragment : Fragment() {
requestPermissions.launch(permissions)
} else {
Timber.d("Get user location")
- userLocation = LocationServices.getFusedLocationProviderClient(requireContext())
- userLocation?.lastLocation?.addOnSuccessListener { location ->
+ userLocation.lastLocation.addOnSuccessListener { location ->
if (location != null) {
userLon = location.longitude
userLat = location.latitude
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/CityWeatherDiffCallback.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/CityWeatherDiffCallback.kt
similarity index 83%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/CityWeatherDiffCallback.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/CityWeatherDiffCallback.kt
index a732bab..bcede50 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/CityWeatherDiffCallback.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/CityWeatherDiffCallback.kt
@@ -1,9 +1,10 @@
-package ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler
+package ru.itis.karakurik.androidLab2.presentation.fragments.cities.recycler
import androidx.recyclerview.widget.DiffUtil
import ru.itis.karakurik.androidLab2.domain.entity.Weather
-object CityWeatherDiffCallback : DiffUtil.ItemCallback(){
+object CityWeatherDiffCallback : DiffUtil.ItemCallback() {
+
override fun areItemsTheSame(oldItem: Weather, newItem: Weather): Boolean {
return oldItem.id == newItem.id
}
@@ -11,5 +12,4 @@ object CityWeatherDiffCallback : DiffUtil.ItemCallback(){
override fun areContentsTheSame(oldItem: Weather, newItem: Weather): Boolean {
return oldItem == newItem
}
-
}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListItemViewHolder.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt
similarity index 80%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListItemViewHolder.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt
index 6f96bf5..a9554a2 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListItemViewHolder.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt
@@ -1,14 +1,14 @@
-package ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler;
+package ru.itis.karakurik.androidLab2.presentation.fragments.cities.recycler;
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView
import coil.load
+import ru.itis.karakurik.androidLab2.R
import ru.itis.karakurik.androidLab2.databinding.ListItemCityBinding
import ru.itis.karakurik.androidLab2.domain.entity.Weather
-import ru.itis.karakurik.androidLab2.presentation.convertors.TempColorConverter.getColor
+import ru.itis.karakurik.androidLab2.presentation.common.convertors.TempColorConverter.getColor
class ListItemViewHolder(
private val binding: ListItemCityBinding,
@@ -16,10 +16,9 @@ class ListItemViewHolder(
) : RecyclerView.ViewHolder(binding.root) {
fun bind(weather: Weather) {
-
with(binding) {
tvCityItem.text = weather.name
- tvTempItem.text = weather.temp.toString()
+ tvTempItem.text = itemView.context.resources.getString(R.string.temp_with_symbol, weather.temp)
tvTempItem.setTextColor(
ContextCompat.getColor(
tvTempItem.context,
@@ -50,5 +49,4 @@ class ListItemViewHolder(
onItemClick
)
}
-
}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListRecyclerAdapter.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListRecyclerAdapter.kt
similarity index 88%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListRecyclerAdapter.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListRecyclerAdapter.kt
index 97ae06d..368ca7b 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListRecyclerAdapter.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListRecyclerAdapter.kt
@@ -1,4 +1,4 @@
-package ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler;
+package ru.itis.karakurik.androidLab2.presentation.fragments.cities.recycler;
import android.view.ViewGroup
import androidx.recyclerview.widget.ListAdapter
@@ -7,6 +7,7 @@ import ru.itis.karakurik.androidLab2.domain.entity.Weather
class ListRecyclerAdapter(
private val onItemClick: (id: Int) -> Unit
) : ListAdapter(CityWeatherDiffCallback) {
+
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt
similarity index 65%
rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/DetailsFragment.kt
rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt
index 9ee987e..39cb4be 100644
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/DetailsFragment.kt
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt
@@ -1,4 +1,4 @@
-package ru.itis.karakurik.androidLab2.presentation.fragments
+package ru.itis.karakurik.androidLab2.presentation.fragments.weather
import android.annotation.SuppressLint
import android.os.Bundle
@@ -7,29 +7,31 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
-import androidx.lifecycle.ViewModelProvider
+import androidx.fragment.app.viewModels
import coil.load
import ru.itis.karakurik.androidLab2.R
+import ru.itis.karakurik.androidLab2.WeatherApp
import ru.itis.karakurik.androidLab2.databinding.FragmentDetailsBinding
-import ru.itis.karakurik.androidLab2.di.DiContainer
import ru.itis.karakurik.androidLab2.domain.entity.Weather
-import ru.itis.karakurik.androidLab2.presentation.MainViewModel
-import ru.itis.karakurik.androidLab2.presentation.convertors.TempColorConverter
-import ru.itis.karakurik.androidLab2.presentation.utils.ViewModelFactory
+import ru.itis.karakurik.androidLab2.presentation.common.convertors.TempColorConverter
+import ru.itis.karakurik.androidLab2.presentation.common.utils.AppViewModelFactory
import timber.log.Timber
+import javax.inject.Inject
class DetailsFragment : Fragment() {
private var _binding: FragmentDetailsBinding? = null
private val binding get() = _binding!!
- private val viewModel by lazy {
- ViewModelProvider(
- this,
- ViewModelFactory(
- DiContainer.getWeatherUseCase,
- DiContainer.getWeatherListUseCase
- )
- )[MainViewModel::class.java]
+ @Inject
+ lateinit var factory: AppViewModelFactory
+
+ private val viewModel: WeatherViewModel by viewModels {
+ factory
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ (activity?.application as WeatherApp).appComponent.inject(this)
+ super.onCreate(savedInstanceState)
}
override fun onCreateView(
@@ -70,13 +72,13 @@ class DetailsFragment : Fragment() {
weather.run {
setWeather(weather)
tvCity.text = name
- tvTemp.text = "${temp}°C"
- tvTempMin.text = "${tempMin}°C"
- tvTempMax.text = "${tempMax}°C"
+ tvTemp.text = getString(R.string.temp_with_symbol, temp)
+ tvTempMin.text = getString(R.string.temp_with_symbol, tempMin)
+ tvTempMax.text = getString(R.string.temp_with_symbol, tempMax)
tvWindDeg.text = windDeg.toString()
- tvWindSpeed.text = "${windSpeed}km/h"
- tvHumidity.text = "${humidity}%"
- tvPressure.text = "${pressure}P"
+ tvWindSpeed.text = getString(R.string.wind_speed_with_symbol, windSpeed)
+ tvHumidity.text = getString(R.string.humidity_with_symbol, humidity)
+ tvPressure.text = getString(R.string.pressure_with_symbol, pressure)
ivAir.load(iconUrl)
arguments?.getString(R.string.transition_name.toString())?.let {
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/WeatherViewModel.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/WeatherViewModel.kt
new file mode 100644
index 0000000..1dcdfb6
--- /dev/null
+++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/WeatherViewModel.kt
@@ -0,0 +1,29 @@
+package ru.itis.karakurik.androidLab2.presentation.fragments.weather
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.launch
+import ru.itis.karakurik.androidLab2.domain.entity.Weather
+import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
+import javax.inject.Inject
+
+class WeatherViewModel @Inject constructor(
+ private val getWeatherUseCase: GetWeatherUseCase
+) : ViewModel() {
+ private var _weather: MutableLiveData> = MutableLiveData()
+ val weather: LiveData> get() = _weather
+
+ fun onGetWeather(cityId: Int) {
+ viewModelScope.launch {
+ kotlin.runCatching {
+ getWeatherUseCase(cityId)
+ }.onSuccess {
+ _weather.value = Result.success(it)
+ }.onFailure {
+ _weather.value = Result.failure(it)
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/ViewModelFactory.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/ViewModelFactory.kt
deleted file mode 100644
index 1ccf37f..0000000
--- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/ViewModelFactory.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-package ru.itis.karakurik.androidLab2.presentation.utils
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherListUseCase
-import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
-import ru.itis.karakurik.androidLab2.presentation.MainViewModel
-import java.lang.IllegalArgumentException
-
-class ViewModelFactory(
- private val getWeatherUseCase: GetWeatherUseCase,
- private val getWeatherListUseCase: GetWeatherListUseCase
-) : ViewModelProvider.Factory {
-
- override fun create(modelClass: Class): T {
- return when {
- modelClass.isAssignableFrom(MainViewModel::class.java) ->
- MainViewModel(
- getWeatherUseCase,
- getWeatherListUseCase
- ) as? T ?: throw IllegalArgumentException("Unknown ViewModel class")
-
- else -> throw IllegalArgumentException("Unknown ViewModel class")
- }
- }
-}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index bc0b2ce..9a47ad7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -5,7 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- tools:context=".presentation.activities.MainActivity">
+ tools:context=".presentation.MainActivity">
+ tools:context=".presentation.fragments.weather.DetailsFragment">
@@ -18,7 +18,7 @@
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 57cbd11..000b771 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,4 +13,9 @@
transactionName
city_id
Температура
+
+ %1$.2f°C
+ %1$.2fkm/h
+ %1$d%%
+ %1$dP
diff --git a/build.gradle b/build.gradle
index 4079b88..5c5efc3 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,11 +3,13 @@ buildscript {
repositories {
google()
mavenCentral()
+ gradlePluginPortal()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.4.1"
+ classpath "com.google.dagger:hilt-android-gradle-plugin:2.37"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
@@ -17,6 +19,28 @@ buildscript {
plugins {
id "io.gitlab.arturbosch.detekt" version "1.18.1"
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
+ id "scabbard.gradle" version "0.5.0"
+}
+
+scabbard {
+ enabled true
+ outputFormat "svg"
+}
+
+allprojects {
+ /*repositories {
+ google()
+ mavenCentral()
+ }*/
+
+ configurations.all {
+ resolutionStrategy.eachDependency {
+ if (requested.group == "com.github.kittinunf.result" && requested.name == "result" && requested.version == "3.0.0") {
+ useVersion("3.0.1")
+ because("Transitive dependency of Scabbard, currently not available on mavenCentral()")
+ }
+ }
+ }
}
task clean(type: Delete) {