From b9c24e3c4aa473b3e5b657ee9a1fa3150f48738c Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Thu, 31 Mar 2022 21:26:06 +0300 Subject: [PATCH 1/6] add dagger --- app/build.gradle | 5 +- app/src/main/AndroidManifest.xml | 3 +- .../java/ru/itis/karakurik/androidLab2/App.kt | 21 +++ .../karakurik/androidLab2/data/api/Api.kt | 1 + .../data/api/mapper/WeatherIconUrlMapper.kt | 5 +- .../data/api/mapper/WeatherMapper.kt | 4 +- .../data/api/mapper/WindDegMapper.kt | 3 +- .../karakurik/androidLab2/di/AppComponent.kt | 38 +++++ .../karakurik/androidLab2/di/DiContainer.kt | 80 ---------- .../di/annotation/InterceptorAnnotation.kt | 19 +++ .../androidLab2/di/annotation/ViewModelKey.kt | 14 ++ .../di/interceptors/ApiKeyInterceptor.kt | 22 --- .../di/interceptors/LangInterceptor.kt | 22 --- .../di/interceptors/UnitsInterceptor.kt | 22 --- .../androidLab2/di/module/AppModule.kt | 43 +++++ .../androidLab2/di/module/DatabaseModule.kt | 4 + .../androidLab2/di/module/NetModule.kt | 147 ++++++++++++++++++ .../androidLab2/di/module/RepoModule.kt | 15 ++ .../androidLab2/di/module/ViewModelModule.kt | 26 ++++ .../repository/WeatherRepositoryImpl.kt | 3 +- .../domain/usecase/GetWeatherListUseCase.kt | 7 +- .../domain/usecase/GetWeatherUseCase.kt | 7 +- .../androidLab2/presentation/MainViewModel.kt | 3 +- .../convertors/TempColorConverter.kt | 1 + .../presentation/fragments/DetailsFragment.kt | 19 +-- .../fragments/list/SearchFragment.kt | 22 ++- .../list/recycler/CityWeatherDiffCallback.kt | 4 +- .../list/recycler/ListItemViewHolder.kt | 2 - .../list/recycler/ListRecyclerAdapter.kt | 1 + .../presentation/utils/AppViewModelFactory.kt | 18 +++ .../presentation/utils/ViewModelFactory.kt | 26 ---- build.gradle | 23 +++ 32 files changed, 419 insertions(+), 211 deletions(-) create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/App.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/DiContainer.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorAnnotation.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/ViewModelKey.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/NetModule.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/ViewModelModule.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/ViewModelFactory.kt diff --git a/app/build.gradle b/app/build.gradle index 4d45170..84ddb1b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,7 +24,6 @@ android { dataBinding true } - buildTypes { release { minifyEnabled false @@ -90,6 +89,10 @@ dependencies { debugImplementation "com.squareup.okhttp3:logging-interceptor:$okhttp" // endregion + def dagger = "2.41" + implementation "com.google.dagger:dagger:${dagger}" + kapt "com.google.dagger:dagger-compiler:${dagger}" + 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..63a5541 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,10 +4,11 @@ - + WindDeg.N 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..37c8406 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt @@ -0,0 +1,38 @@ +package ru.itis.karakurik.androidLab2.di + +import android.app.Application +import android.content.Context +import dagger.BindsInstance +import dagger.Component +import ru.itis.karakurik.androidLab2.App +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.activities.MainActivity +import javax.inject.Singleton + +@Component( + modules = [ + AppModule::class, + NetModule::class, + RepoModule::class, + ViewModelModule::class + ] +) +@Singleton +interface AppComponent { + + fun inject(mainActivity: MainActivity) + + @Component.Builder + interface Builder { + fun build(): AppComponent + +// @BindsInstance +// fun context(context: Context): Builder + + @BindsInstance + fun application(application: App): 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/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..14a2a32 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt @@ -0,0 +1,43 @@ +package ru.itis.karakurik.androidLab2.di.module + +import android.content.Context +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import ru.itis.karakurik.androidLab2.App +import javax.inject.Qualifier + +@Module +class AppModule { + + @Provides + fun provideContext(app: App): Context = app.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 + + //fused loc client + //мапперы не здесь +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt new file mode 100644 index 0000000..bb1f6a9 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt @@ -0,0 +1,4 @@ +package ru.itis.karakurik.androidLab2.di.module + +class DatabaseModule { +} 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..40d3926 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt @@ -0,0 +1,15 @@ +package ru.itis.karakurik.androidLab2.di.module + +import dagger.Binds +import dagger.Module +import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository +import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepositoryImpl + +@Module +interface RepoModule { + + @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..43c1abe --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/ViewModelModule.kt @@ -0,0 +1,26 @@ +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.MainViewModel +import ru.itis.karakurik.androidLab2.presentation.utils.AppViewModelFactory + +@Module +interface ViewModelModule { + + @Binds + fun bindViewModelFactory( + factory: AppViewModelFactory + ): ViewModelProvider.Factory + + @Binds + @IntoMap + @ViewModelKey(MainViewModel::class) + fun bindMainViewModel( + viewModel: MainViewModel + ): ViewModel +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt index d109d54..86684ff 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt @@ -4,8 +4,9 @@ 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 javax.inject.Inject -class WeatherRepositoryImpl( +class WeatherRepositoryImpl @Inject constructor( private val api: Api, private val weatherMapper: WeatherMapper ) : WeatherRepository { 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..fbc24bc 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,16 @@ 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 -class GetWeatherListUseCase( +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..52f0dec 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,15 @@ 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 -class GetWeatherUseCase( +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/MainViewModel.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt index 7f3e983..c1f2428 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainViewModel.kt @@ -9,8 +9,9 @@ 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 MainViewModel @Inject constructor( private val getWeatherUseCase: GetWeatherUseCase, private val getWeatherListUseCase: GetWeatherListUseCase ) : ViewModel() { 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/convertors/TempColorConverter.kt index 365a004..929aaf3 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt @@ -3,6 +3,7 @@ package ru.itis.karakurik.androidLab2.presentation.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/fragments/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/DetailsFragment.kt index 9ee987e..4693986 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/DetailsFragment.kt @@ -7,29 +7,26 @@ 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.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.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: MainViewModel by viewModels { + factory } override fun onCreateView( 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/list/SearchFragment.kt index 24e22c1..be1f7f8 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/list/SearchFragment.kt @@ -13,25 +13,24 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.widget.SearchView import androidx.core.app.ActivityCompat import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider +import androidx.fragment.app.viewModels 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 dagger.Lazy 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.utils.AppViewModelFactory 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() { @@ -50,14 +49,11 @@ class SearchFragment : Fragment() { 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: AppViewModelFactory + + private val viewModel: MainViewModel by viewModels { + factory } private val requestPermissions = 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/list/recycler/CityWeatherDiffCallback.kt index a732bab..a901789 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/list/recycler/CityWeatherDiffCallback.kt @@ -3,7 +3,8 @@ package ru.itis.karakurik.androidLab2.presentation.fragments.list.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/list/recycler/ListItemViewHolder.kt index 6f96bf5..6f25795 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/list/recycler/ListItemViewHolder.kt @@ -16,7 +16,6 @@ class ListItemViewHolder( ) : RecyclerView.ViewHolder(binding.root) { fun bind(weather: Weather) { - with(binding) { tvCityItem.text = weather.name tvTempItem.text = weather.temp.toString() @@ -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/list/recycler/ListRecyclerAdapter.kt index 97ae06d..a44991a 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/list/recycler/ListRecyclerAdapter.kt @@ -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/utils/AppViewModelFactory.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt new file mode 100644 index 0000000..8585457 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt @@ -0,0 +1,18 @@ +package ru.itis.karakurik.androidLab2.presentation.utils + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import javax.inject.Inject +import javax.inject.Provider + +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/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/build.gradle b/build.gradle index 4079b88..5f9d081 100644 --- a/build.gradle +++ b/build.gradle @@ -8,6 +8,7 @@ buildscript { 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 +18,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) { From 0097fa22ef7c8a9cb81e45183ef25a77bba13215 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Mon, 4 Apr 2022 18:23:17 +0300 Subject: [PATCH 2/6] add dagger app --- app/build.gradle | 10 ++-- app/src/main/AndroidManifest.xml | 4 +- .../androidLab2/{App.kt => WeatherApp.kt} | 4 +- .../api}/repository/WeatherRepositoryImpl.kt | 4 +- .../karakurik/androidLab2/di/AppComponent.kt | 17 +++---- .../androidLab2/di/module/AppModule.kt | 4 +- .../androidLab2/di/module/DatabaseModule.kt | 4 -- .../androidLab2/di/module/RepoModule.kt | 2 +- .../androidLab2/di/module/ViewModelModule.kt | 18 +++++-- .../{activities => }/MainActivity.kt | 4 +- .../convertors/TempColorConverter.kt | 2 +- .../{ => common}/utils/AppViewModelFactory.kt | 2 +- .../presentation/{ => common}/utils/Event.kt | 4 +- .../cities/CityListViewModel.kt} | 28 +++-------- .../{list => cities}/SearchFragment.kt | 50 ++++++++++--------- .../recycler/CityWeatherDiffCallback.kt | 2 +- .../recycler/ListItemViewHolder.kt | 5 +- .../recycler/ListRecyclerAdapter.kt | 2 +- .../{ => weather}/DetailsFragment.kt | 15 ++++-- .../fragments/weather/WeatherViewModel.kt | 29 +++++++++++ app/src/main/res/layout/activity_main.xml | 2 +- app/src/main/res/layout/fragment_details.xml | 2 +- .../main/res/navigation/nav_graph_main.xml | 4 +- build.gradle | 1 + 24 files changed, 123 insertions(+), 96 deletions(-) rename app/src/main/java/ru/itis/karakurik/androidLab2/{App.kt => WeatherApp.kt} (75%) rename app/src/main/java/ru/itis/karakurik/androidLab2/{domain => data/api}/repository/WeatherRepositoryImpl.kt (87%) delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/{activities => }/MainActivity.kt (83%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/{ => common}/convertors/TempColorConverter.kt (86%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/{ => common}/utils/AppViewModelFactory.kt (91%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/{ => common}/utils/Event.kt (87%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/{MainViewModel.kt => fragments/cities/CityListViewModel.kt} (61%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/{list => cities}/SearchFragment.kt (87%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/{list => cities}/recycler/CityWeatherDiffCallback.kt (84%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/{list => cities}/recycler/ListItemViewHolder.kt (87%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/{list => cities}/recycler/ListRecyclerAdapter.kt (88%) rename app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/{ => weather}/DetailsFragment.kt (83%) create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/WeatherViewModel.kt diff --git a/app/build.gradle b/app/build.gradle index 84ddb1b..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 { @@ -89,9 +89,11 @@ dependencies { debugImplementation "com.squareup.okhttp3:logging-interceptor:$okhttp" // endregion - def dagger = "2.41" - implementation "com.google.dagger:dagger:${dagger}" - kapt "com.google.dagger:dagger-compiler:${dagger}" + 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' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 63a5541..f74a81e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,7 +8,7 @@ diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/App.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt similarity index 75% rename from app/src/main/java/ru/itis/karakurik/androidLab2/App.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt index 76d677e..3c6dbc3 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/App.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/WeatherApp.kt @@ -3,10 +3,8 @@ package ru.itis.karakurik.androidLab2 import android.app.Application import ru.itis.karakurik.androidLab2.di.AppComponent import ru.itis.karakurik.androidLab2.di.DaggerAppComponent -import ru.itis.karakurik.androidLab2.di.module.AppModule -import ru.itis.karakurik.androidLab2.di.module.NetModule -class App : Application() { +class WeatherApp : Application() { lateinit var appComponent: AppComponent 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 87% 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 86684ff..913b5ac 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,9 +1,9 @@ -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 class WeatherRepositoryImpl @Inject constructor( 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 index 37c8406..9098c3f 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/AppComponent.kt @@ -1,17 +1,18 @@ package ru.itis.karakurik.androidLab2.di -import android.app.Application -import android.content.Context import dagger.BindsInstance import dagger.Component -import ru.itis.karakurik.androidLab2.App +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.activities.MainActivity +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, @@ -20,19 +21,17 @@ import javax.inject.Singleton ViewModelModule::class ] ) -@Singleton interface AppComponent { fun inject(mainActivity: MainActivity) + fun inject(searchFragment: SearchFragment) + fun inject(detailsFragment: DetailsFragment) @Component.Builder interface Builder { fun build(): AppComponent -// @BindsInstance -// fun context(context: Context): Builder - @BindsInstance - fun application(application: App): Builder + fun application(application: WeatherApp): Builder } } 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 index 14a2a32..fbe33d6 100644 --- 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 @@ -5,14 +5,14 @@ import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers -import ru.itis.karakurik.androidLab2.App +import ru.itis.karakurik.androidLab2.WeatherApp import javax.inject.Qualifier @Module class AppModule { @Provides - fun provideContext(app: App): Context = app.applicationContext + fun provideContext(weatherApp: WeatherApp): Context = weatherApp.applicationContext @Provides @DefaultDispatcher diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt deleted file mode 100644 index bb1f6a9..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/DatabaseModule.kt +++ /dev/null @@ -1,4 +0,0 @@ -package ru.itis.karakurik.androidLab2.di.module - -class DatabaseModule { -} 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 index 40d3926..da1a6aa 100644 --- 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 @@ -3,7 +3,7 @@ package ru.itis.karakurik.androidLab2.di.module import dagger.Binds import dagger.Module import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository -import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepositoryImpl +import ru.itis.karakurik.androidLab2.data.api.repository.WeatherRepositoryImpl @Module interface RepoModule { 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 index 43c1abe..6fa6dcc 100644 --- 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 @@ -6,8 +6,9 @@ import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap import ru.itis.karakurik.androidLab2.di.annotation.ViewModelKey -import ru.itis.karakurik.androidLab2.presentation.MainViewModel -import ru.itis.karakurik.androidLab2.presentation.utils.AppViewModelFactory +import ru.itis.karakurik.androidLab2.presentation.fragments.cities.CityListViewModel +import ru.itis.karakurik.androidLab2.presentation.common.utils.AppViewModelFactory +import ru.itis.karakurik.androidLab2.presentation.fragments.weather.WeatherViewModel @Module interface ViewModelModule { @@ -19,8 +20,15 @@ interface ViewModelModule { @Binds @IntoMap - @ViewModelKey(MainViewModel::class) - fun bindMainViewModel( - viewModel: MainViewModel + @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/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..a816b2a 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,9 +1,10 @@ -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.WeatherApp import ru.itis.karakurik.androidLab2.R 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 86% 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 929aaf3..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,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.presentation.convertors +package ru.itis.karakurik.androidLab2.presentation.common.convertors import ru.itis.karakurik.androidLab2.R diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt similarity index 91% rename from app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt index 8585457..48d038e 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/utils/AppViewModelFactory.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/AppViewModelFactory.kt @@ -1,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.presentation.utils +package ru.itis.karakurik.androidLab2.presentation.common.utils import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider 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 87% 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 index ac71031..e2f1e83 100644 --- 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 @@ -1,7 +1,9 @@ +import javax.inject.Inject + /** * Used as a wrapper for data that is exposed via a LiveData that represents an event. */ -open class Event(private val content: T) { +open class Event (private val content: T) { var hasBeenHandled = false private set // Allow external read but not write 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 c1f2428..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 @@ -11,31 +10,16 @@ import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherListUseCase import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase import javax.inject.Inject -class MainViewModel @Inject constructor( +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 { @@ -54,9 +38,9 @@ class MainViewModel @Inject constructor( 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 87% 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 be1f7f8..6b5aeb8 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 @@ -14,17 +14,16 @@ 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 dagger.Lazy +import ru.itis.karakurik.androidLab2.WeatherApp import ru.itis.karakurik.androidLab2.databinding.FragmentSearchBinding -import ru.itis.karakurik.androidLab2.presentation.MainViewModel -import ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler.ListRecyclerAdapter -import ru.itis.karakurik.androidLab2.presentation.utils.AppViewModelFactory +import ru.itis.karakurik.androidLab2.presentation.fragments.cities.recycler.ListRecyclerAdapter import timber.log.Timber import javax.inject.Inject @@ -50,14 +49,14 @@ class SearchFragment : Fragment() { private var userLocation: FusedLocationProviderClient? = null @Inject - lateinit var factory: AppViewModelFactory + lateinit var factory: ViewModelProvider.Factory - private val viewModel: MainViewModel by viewModels { + private val viewModel: CityListViewModel by viewModels { factory } private val requestPermissions = - registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { it -> + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { var allPermissionsGranted = true for (granted in it.values) { allPermissionsGranted = allPermissionsGranted and granted @@ -81,6 +80,11 @@ class SearchFragment : Fragment() { } } + override fun onCreate(savedInstanceState: Bundle?) { + (activity?.application as WeatherApp).appComponent.inject(this) + super.onCreate(savedInstanceState) + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -105,22 +109,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 -> 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 84% 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 a901789..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,4 +1,4 @@ -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 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 87% 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 6f25795..7ae8aa7 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,13 @@ -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.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, 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 a44991a..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 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 83% 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 4693986..b385af5 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 @@ -9,12 +9,12 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import coil.load +import ru.itis.karakurik.androidLab2.WeatherApp import ru.itis.karakurik.androidLab2.R import ru.itis.karakurik.androidLab2.databinding.FragmentDetailsBinding 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.AppViewModelFactory +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 @@ -25,10 +25,15 @@ class DetailsFragment : Fragment() { @Inject lateinit var factory: AppViewModelFactory - private val viewModel: MainViewModel by viewModels { + 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( inflater: LayoutInflater, container: ViewGroup?, 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..27af9d9 --- /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/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/build.gradle b/build.gradle index 5f9d081..5c5efc3 100644 --- a/build.gradle +++ b/build.gradle @@ -3,6 +3,7 @@ buildscript { repositories { google() mavenCentral() + gradlePluginPortal() } dependencies { classpath 'com.android.tools.build:gradle:7.1.2' From cf235c44c7277289f52f086e6680dec016413f16 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Mon, 4 Apr 2022 18:50:57 +0300 Subject: [PATCH 3/6] add dagger app --- .../di/annotation/InterceptorKey.kt | 17 ++++++++++++++++ .../androidLab2/di/module/AppModule.kt | 20 +++++++++++++++++-- .../fragments/cities/SearchFragment.kt | 17 +++++++--------- 3 files changed, 42 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt 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..2022e01 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt @@ -0,0 +1,17 @@ +package ru.itis.karakurik.androidLab2.di.annotation + +import androidx.lifecycle.ViewModel +import dagger.MapKey +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +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/module/AppModule.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/module/AppModule.kt index fbe33d6..2d1624a 100644 --- 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 @@ -1,6 +1,11 @@ package ru.itis.karakurik.androidLab2.di.module import android.content.Context +import androidx.recyclerview.widget.LinearSmoothScroller +import androidx.recyclerview.widget.LinearSmoothScroller.SNAP_TO_START +import androidx.recyclerview.widget.RecyclerView +import com.google.android.gms.location.LocationServices +import dagger.Binds import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineDispatcher @@ -38,6 +43,17 @@ class AppModule { @Retention(AnnotationRetention.RUNTIME) annotation class IODispatcher - //fused loc client - //мапперы не здесь + @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/presentation/fragments/cities/SearchFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt index 6b5aeb8..0c36819 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt @@ -46,7 +46,6 @@ 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 @Inject lateinit var factory: ViewModelProvider.Factory @@ -55,6 +54,12 @@ class SearchFragment : Fragment() { factory } + @Inject + lateinit var smoothScroller: RecyclerView.SmoothScroller + + @Inject + lateinit var userLocation: FusedLocationProviderClient + private val requestPermissions = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { var allPermissionsGranted = true @@ -72,14 +77,6 @@ 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) @@ -174,7 +171,7 @@ class SearchFragment : Fragment() { requestPermissions.launch(permissions) } else { Timber.d("Get user location") - userLocation = LocationServices.getFusedLocationProviderClient(requireContext()) +// userLocation = LocationServices.getFusedLocationProviderClient(requireContext()) userLocation?.lastLocation?.addOnSuccessListener { location -> if (location != null) { userLon = location.longitude From bdd21238f05b859f44e01b0a5732cb4f937594f7 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Mon, 4 Apr 2022 19:15:39 +0300 Subject: [PATCH 4/6] cleanup code --- .../ru/itis/karakurik/androidLab2/di/module/AppModule.kt | 2 -- .../ru/itis/karakurik/androidLab2/di/module/RepoModule.kt | 2 +- .../itis/karakurik/androidLab2/di/module/ViewModelModule.kt | 2 +- .../itis/karakurik/androidLab2/presentation/MainActivity.kt | 2 +- .../karakurik/androidLab2/presentation/common/utils/Event.kt | 4 +--- .../presentation/fragments/cities/SearchFragment.kt | 5 +---- .../presentation/fragments/weather/DetailsFragment.kt | 2 +- .../presentation/fragments/weather/WeatherViewModel.kt | 4 ++-- 8 files changed, 8 insertions(+), 15 deletions(-) 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 index 2d1624a..9b81fa3 100644 --- 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 @@ -2,10 +2,8 @@ package ru.itis.karakurik.androidLab2.di.module import android.content.Context import androidx.recyclerview.widget.LinearSmoothScroller -import androidx.recyclerview.widget.LinearSmoothScroller.SNAP_TO_START import androidx.recyclerview.widget.RecyclerView import com.google.android.gms.location.LocationServices -import dagger.Binds import dagger.Module import dagger.Provides import kotlinx.coroutines.CoroutineDispatcher 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 index da1a6aa..fa21d16 100644 --- 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 @@ -2,8 +2,8 @@ package ru.itis.karakurik.androidLab2.di.module import dagger.Binds import dagger.Module -import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository import ru.itis.karakurik.androidLab2.data.api.repository.WeatherRepositoryImpl +import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository @Module interface RepoModule { 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 index 6fa6dcc..8c8f0df 100644 --- 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 @@ -6,8 +6,8 @@ import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap import ru.itis.karakurik.androidLab2.di.annotation.ViewModelKey -import ru.itis.karakurik.androidLab2.presentation.fragments.cities.CityListViewModel 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 diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt index a816b2a..e63d555 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/MainActivity.kt @@ -4,8 +4,8 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.navigation.NavController import by.kirich1409.viewbindingdelegate.viewBinding -import ru.itis.karakurik.androidLab2.WeatherApp 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 diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt index e2f1e83..ac71031 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/common/utils/Event.kt @@ -1,9 +1,7 @@ -import javax.inject.Inject - /** * Used as a wrapper for data that is exposed via a LiveData that represents an event. */ -open class Event (private val content: T) { +open class Event(private val content: T) { var hasBeenHandled = false private set // Allow external read but not write diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt index 0c36819..03f64ee 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/SearchFragment.kt @@ -16,10 +16,8 @@ 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.WeatherApp import ru.itis.karakurik.androidLab2.databinding.FragmentSearchBinding @@ -171,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/weather/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt index b385af5..1bcafd1 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt @@ -9,8 +9,8 @@ import androidx.core.content.ContextCompat import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import coil.load -import ru.itis.karakurik.androidLab2.WeatherApp import ru.itis.karakurik.androidLab2.R +import ru.itis.karakurik.androidLab2.WeatherApp import ru.itis.karakurik.androidLab2.databinding.FragmentDetailsBinding import ru.itis.karakurik.androidLab2.domain.entity.Weather import ru.itis.karakurik.androidLab2.presentation.common.convertors.TempColorConverter 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 index 27af9d9..1dcdfb6 100644 --- 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 @@ -11,11 +11,11 @@ import javax.inject.Inject class WeatherViewModel @Inject constructor( private val getWeatherUseCase: GetWeatherUseCase -): ViewModel() { +) : ViewModel() { private var _weather: MutableLiveData> = MutableLiveData() val weather: LiveData> get() = _weather - fun onGetWeather(cityId : Int) { + fun onGetWeather(cityId: Int) { viewModelScope.launch { kotlin.runCatching { getWeatherUseCase(cityId) From fb417182c1f3a74192335d4ff853b2f011030bb3 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Mon, 4 Apr 2022 20:09:07 +0300 Subject: [PATCH 5/6] add scopes --- .../androidLab2/data/api/mapper/WeatherIconUrlMapper.kt | 2 ++ .../karakurik/androidLab2/data/api/mapper/WeatherMapper.kt | 2 ++ .../karakurik/androidLab2/data/api/mapper/WindDegMapper.kt | 2 ++ .../androidLab2/data/api/repository/WeatherRepositoryImpl.kt | 2 ++ .../itis/karakurik/androidLab2/di/annotation/InterceptorKey.kt | 3 --- .../java/ru/itis/karakurik/androidLab2/di/module/RepoModule.kt | 2 ++ .../androidLab2/domain/usecase/GetWeatherListUseCase.kt | 2 ++ .../karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt | 2 ++ .../presentation/common/utils/AppViewModelFactory.kt | 1 + 9 files changed, 15 insertions(+), 3 deletions(-) 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 4576ad6..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,7 +1,9 @@ package ru.itis.karakurik.androidLab2.data.api.mapper import javax.inject.Inject +import javax.inject.Singleton +@Singleton class WeatherIconUrlMapper @Inject constructor() { fun mapToLargeIcon(iconId: String): String { 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 d68c1fe..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 @@ -4,7 +4,9 @@ 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 +@Singleton class WeatherMapper @Inject constructor( private val windDegMapper: WindDegMapper, private val weatherIconUrlMapper: WeatherIconUrlMapper 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 a68e77b..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 @@ -2,7 +2,9 @@ package ru.itis.karakurik.androidLab2.data.api.mapper import ru.itis.karakurik.androidLab2.domain.enum.WindDeg import javax.inject.Inject +import javax.inject.Singleton +@Singleton class WindDegMapper @Inject constructor() { fun map(deg: Int) : WindDeg { return when ((deg / 45 + 2* (deg%45) / 45) * 45 % 360) { diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt index 913b5ac..b8497c8 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/repository/WeatherRepositoryImpl.kt @@ -5,7 +5,9 @@ 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 +@Singleton class WeatherRepositoryImpl @Inject constructor( private val api: Api, private val weatherMapper: WeatherMapper 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 index 2022e01..a3744be 100644 --- 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 @@ -1,9 +1,6 @@ package ru.itis.karakurik.androidLab2.di.annotation -import androidx.lifecycle.ViewModel import dagger.MapKey -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import okhttp3.Interceptor import kotlin.reflect.KClass 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 index fa21d16..d2516ac 100644 --- 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 @@ -4,10 +4,12 @@ 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, 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 fbc24bc..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 @@ -7,7 +7,9 @@ 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 +@Singleton class GetWeatherListUseCase @Inject constructor( private val weatherRepository: WeatherRepository, @AppModule.DefaultDispatcher private val dispatcher: CoroutineDispatcher = Dispatchers.Main 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 52f0dec..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 @@ -8,7 +8,9 @@ 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 +@Singleton class GetWeatherUseCase @Inject constructor( private val weatherRepository: WeatherRepository, @AppModule.DefaultDispatcher private val dispatcher: CoroutineDispatcher = Dispatchers.Main 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 index 48d038e..3df0820 100644 --- 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 @@ -4,6 +4,7 @@ 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> From 54dd59495c5f54d627cedb43e9f840a455ecafac Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Tue, 5 Apr 2022 15:17:07 +0300 Subject: [PATCH 6/6] string resourses formatted --- .../fragments/cities/recycler/ListItemViewHolder.kt | 3 ++- .../fragments/weather/DetailsFragment.kt | 12 ++++++------ app/src/main/res/values/strings.xml | 5 +++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt index 7ae8aa7..a9554a2 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/cities/recycler/ListItemViewHolder.kt @@ -5,6 +5,7 @@ import android.view.ViewGroup 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.common.convertors.TempColorConverter.getColor @@ -17,7 +18,7 @@ class ListItemViewHolder( 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, diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt index 1bcafd1..39cb4be 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/weather/DetailsFragment.kt @@ -72,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/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