From c9e759aafc3d84c435c79110e453a85165e0b2c5 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Sun, 27 Feb 2022 14:12:33 +0300 Subject: [PATCH 01/10] some work with api --- .idea/misc.xml | 10 ++++ app/build.gradle | 12 +++-- app/src/main/AndroidManifest.xml | 4 +- .../karakurik/androidLab2/MainActivity.kt | 12 ----- .../androidLab2/data/WeatherRepository.kt | 52 +++++++++++++++++++ .../karakurik/androidLab2/data/api/Api.kt | 18 +++++++ .../api/Interceptors/ApiKeyInterceptor.kt | 22 ++++++++ .../data/api/Interceptors/LangInterceptor.kt | 22 ++++++++ .../data/api/Interceptors/UnitsInterceptor.kt | 22 ++++++++ .../androidLab2/data/api/response/Clouds.kt | 9 ++++ .../androidLab2/data/api/response/Coord.kt | 11 ++++ .../androidLab2/data/api/response/Main.kt | 23 ++++++++ .../androidLab2/data/api/response/Sys.kt | 9 ++++ .../androidLab2/data/api/response/Weather.kt | 15 ++++++ .../api/response/WeatherCitiesResponse.kt | 15 ++++++ .../data/api/response/WeatherResponse.kt | 29 +++++++++++ .../androidLab2/data/api/response/Wind.kt | 11 ++++ .../androidLab2/extentions/ActivityExt.kt | 9 ++++ .../androidLab2/models/CityWeather.kt | 10 ++++ .../karakurik/androidLab2/models/WindDeg.kt | 13 +++++ .../models/convertors/WindDegConvertor.kt | 20 +++++++ .../karakurik/androidLab2/myTestsApi/Main.kt | 25 +++++++++ .../androidLab2/ui/activities/MainActivity.kt | 41 +++++++++++++++ .../ui/fragments/DetailsFragment.kt | 8 +++ .../ui/fragments/list/SearchFragment.kt | 15 ++++++ .../list/recycler/CityWeatherDiffCallback.kt | 15 ++++++ .../list/recycler/ListRecyclerAdapter.kt | 19 +++++++ .../fragments/list/recycler/ListViewHolder.kt | 10 ++++ app/src/main/res/layout/activity_main.xml | 30 ++++++----- app/src/main/res/layout/fragment_details.xml | 14 +++++ app/src/main/res/layout/fragment_search.xml | 34 ++++++++++++ app/src/main/res/layout/list_item_city.xml | 33 ++++++++++++ .../main/res/navigation/nav_graph_main.xml | 26 ++++++++++ app/src/main/res/values/strings.xml | 6 ++- build.gradle | 1 + 35 files changed, 592 insertions(+), 33 deletions(-) delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/MainActivity.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/ApiKeyInterceptor.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/LangInterceptor.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/UnitsInterceptor.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Clouds.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Coord.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Main.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Sys.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Weather.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherCitiesResponse.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherResponse.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Wind.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/extentions/ActivityExt.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListViewHolder.kt create mode 100644 app/src/main/res/layout/fragment_details.xml create mode 100644 app/src/main/res/layout/fragment_search.xml create mode 100644 app/src/main/res/layout/list_item_city.xml create mode 100644 app/src/main/res/navigation/nav_graph_main.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 2a4d5b5..cb089fa 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,15 @@ + + + diff --git a/app/build.gradle b/app/build.gradle index eb6b307..7bb0ae7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,12 +6,12 @@ plugins { } android { - compileSdk 31 + compileSdk 32 defaultConfig { applicationId "ru.itis.karakurik.androidLab2" minSdk 23 - targetSdk 31 + targetSdk 32 versionCode 1 versionName "1.0" @@ -19,7 +19,7 @@ android { } buildFeatures { - viewBinding true + viewBinding = true } buildTypes { @@ -46,10 +46,12 @@ dependencies { implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'com.google.android.material:material:1.5.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' + implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1" def nav_version = "2.4.1" - implementation "androidx.navigation:navigation-fragment:$nav_version" - implementation "androidx.navigation:navigation-ui:$nav_version" + implementation "androidx.navigation:navigation-fragment:2.4.1" + implementation "androidx.navigation:navigation-ui:2.4.1" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 52ea1ca..4c5bf83 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,8 @@ + + diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/MainActivity.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/MainActivity.kt deleted file mode 100644 index 0e7574a..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/MainActivity.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ru.itis.karakurik.androidLab2 - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle - -class MainActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt new file mode 100644 index 0000000..3700a19 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt @@ -0,0 +1,52 @@ +package ru.itis.karakurik.androidLab2.data + +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.Interceptors.ApiKeyInterceptor +import ru.itis.karakurik.androidLab2.data.api.Interceptors.LangInterceptor +import ru.itis.karakurik.androidLab2.data.api.Interceptors.UnitsInterceptor +import ru.itis.karakurik.androidLab2.data.api.response.WeatherCitiesResponse +import ru.itis.karakurik.androidLab2.data.api.response.WeatherResponse + +private const val BASE_URL = "http://api.openweathermap.org/data/2.5/" + +class WeatherRepository { + 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() + } + + private val api: Api by lazy { + Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okhttp) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(Api::class.java) + } + + suspend fun getWeather(city: String): WeatherResponse { + return api.getWeather(city) + } + + suspend fun getWeatherCities(lat: Double, lon: Double, cnt: Int): WeatherCitiesResponse { + return api.getWeatherCities(lat, lon, cnt) + } +} 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 new file mode 100644 index 0000000..9166ff6 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Api.kt @@ -0,0 +1,18 @@ +package ru.itis.karakurik.androidLab2.data.api + +import retrofit2.http.GET +import retrofit2.http.Query +import ru.itis.karakurik.androidLab2.data.api.response.WeatherCitiesResponse +import ru.itis.karakurik.androidLab2.data.api.response.WeatherResponse + +interface Api { + @GET("weather") + suspend fun getWeather(@Query("q") city: String): WeatherResponse + + @GET("find") + suspend fun getWeatherCities( + @Query("lat") lat: Double, + @Query("lon") lon: Double, + @Query("cnt") cnt: Int + ) : WeatherCitiesResponse +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/ApiKeyInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/ApiKeyInterceptor.kt new file mode 100644 index 0000000..34b31c8 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/ApiKeyInterceptor.kt @@ -0,0 +1,22 @@ +package ru.itis.karakurik.androidLab2.data.api.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/data/api/Interceptors/LangInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/LangInterceptor.kt new file mode 100644 index 0000000..b6dc705 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/LangInterceptor.kt @@ -0,0 +1,22 @@ +package ru.itis.karakurik.androidLab2.data.api.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/data/api/Interceptors/UnitsInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/UnitsInterceptor.kt new file mode 100644 index 0000000..6fca84e --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/Interceptors/UnitsInterceptor.kt @@ -0,0 +1,22 @@ +package ru.itis.karakurik.androidLab2.data.api.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/data/api/response/Clouds.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Clouds.kt new file mode 100644 index 0000000..aa50d10 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Clouds.kt @@ -0,0 +1,9 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Clouds( + @SerializedName("all") + val all: Int +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Coord.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Coord.kt new file mode 100644 index 0000000..2e94711 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Coord.kt @@ -0,0 +1,11 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Coord( + @SerializedName("lat") + val lat: Double, + @SerializedName("lon") + val lon: Double +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Main.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Main.kt new file mode 100644 index 0000000..eec29ca --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Main.kt @@ -0,0 +1,23 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Main( + @SerializedName("feels_like") + val feelsLike: Double, + @SerializedName("grnd_level") + val grndLevel: Int, + @SerializedName("humidity") + val humidity: Int, + @SerializedName("pressure") + val pressure: Int, + @SerializedName("sea_level") + val seaLevel: Int, + @SerializedName("temp") + val temp: Double, + @SerializedName("temp_max") + val tempMax: Double, + @SerializedName("temp_min") + val tempMin: Double +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Sys.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Sys.kt new file mode 100644 index 0000000..5f48afc --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Sys.kt @@ -0,0 +1,9 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Sys( + @SerializedName("country") + val country: String +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Weather.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Weather.kt new file mode 100644 index 0000000..5c85e39 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Weather.kt @@ -0,0 +1,15 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Weather( + @SerializedName("description") + val description: String, + @SerializedName("icon") + val icon: String, + @SerializedName("id") + val id: Int, + @SerializedName("main") + val main: String +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherCitiesResponse.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherCitiesResponse.kt new file mode 100644 index 0000000..b4925d9 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherCitiesResponse.kt @@ -0,0 +1,15 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class WeatherCitiesResponse( + @SerializedName("cod") + val cod: String, + @SerializedName("count") + val count: Int, + @SerializedName("list") + val list: List, + @SerializedName("message") + val message: String +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherResponse.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherResponse.kt new file mode 100644 index 0000000..8e04ef2 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/WeatherResponse.kt @@ -0,0 +1,29 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class WeatherResponse ( + @SerializedName("clouds") + val clouds: Clouds, + @SerializedName("coord") + val coord: Coord, + @SerializedName("dt") + val dt: Int, + @SerializedName("id") + val id: Int, + @SerializedName("main") + val main: Main, + @SerializedName("name") + val name: String, + @SerializedName("rain") + val rain: Any, + @SerializedName("snow") + val snow: Any, + @SerializedName("sys") + val sys: Sys, + @SerializedName("weather") + val weather: List, + @SerializedName("wind") + val wind: Wind +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Wind.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Wind.kt new file mode 100644 index 0000000..61e0774 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/Wind.kt @@ -0,0 +1,11 @@ +package ru.itis.karakurik.androidLab2.data.api.response + + +import com.google.gson.annotations.SerializedName + +data class Wind( + @SerializedName("deg") + val deg: Int, + @SerializedName("speed") + val speed: Double +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/extentions/ActivityExt.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/extentions/ActivityExt.kt new file mode 100644 index 0000000..61a4dd6 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/extentions/ActivityExt.kt @@ -0,0 +1,9 @@ +package ru.itis.karakurik.androidLab2.extentions + +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.NavController +import androidx.navigation.fragment.NavHostFragment + +fun AppCompatActivity.findController (id: Int) : NavController { + return (supportFragmentManager.findFragmentById(id) as NavHostFragment).navController +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt new file mode 100644 index 0000000..d8db689 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt @@ -0,0 +1,10 @@ +package ru.itis.karakurik.androidLab2.models + +data class CityWeather ( + val id: Long, + val lat: Double, + val lon: Double, + val temp: Double, + val humidity: Int, + val windDeg: WindDeg +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt new file mode 100644 index 0000000..0227197 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt @@ -0,0 +1,13 @@ +package ru.itis.karakurik.androidLab2.models +enum class WindDeg ( + val deg: Int +) { + N(0), + NE(45), + E(90), + SE(135), + S(180), + SW(225), + W(270), + NW(315) +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt new file mode 100644 index 0000000..73ecf30 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt @@ -0,0 +1,20 @@ +package ru.itis.karakurik.androidLab2.models.convertors + +import androidx.room.TypeConverter +import ru.itis.karakurik.androidLab2.models.WindDeg + +object WindDegConvertor { + @TypeConverter + fun convertWindDeg(deg: Int): WindDeg { + return when ((deg / 45 + 2* (deg%45) / 45) * 45 % 360) { + 0 -> WindDeg.N + 45 -> WindDeg.NE + 90 -> WindDeg.E + 135 -> WindDeg.SE + 180 -> WindDeg.S + 225 -> WindDeg.SW + 270 -> WindDeg.W + else -> WindDeg.NW + } + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt new file mode 100644 index 0000000..1bfc48f --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt @@ -0,0 +1,25 @@ +package ru.itis.karakurik.androidLab2 + +import kotlinx.coroutines.runBlocking +import ru.itis.karakurik.androidLab2.data.WeatherRepository +import ru.itis.karakurik.androidLab2.models.convertors.WindDegConvertor + +private const val KAZAN_LON = 49.1221 +private const val KAZAN_LAT = 55.7887 + +fun main() { + val a = WindDegConvertor.convertWindDeg(350) + + val repository = WeatherRepository() + + runBlocking { + val weatherResponse = repository.getWeather("Kazan") + val lat = weatherResponse.coord.lat + val lon = weatherResponse.coord.lon + + val weatherCitiesResponse = repository.getWeatherCities(lat, lon, 10) + for (wR in weatherCitiesResponse.list) { + println(wR.name + " " + wR.weather[0].description) + } + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt new file mode 100644 index 0000000..a381c7d --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt @@ -0,0 +1,41 @@ +package ru.itis.karakurik.androidLab2.ui.activities + +import android.os.Bundle +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import androidx.navigation.NavController +import com.google.android.material.snackbar.Snackbar +import kotlinx.coroutines.launch +import ru.itis.karakurik.androidLab2.data.WeatherRepository +import ru.itis.karakurik.androidLab2.databinding.ActivityMainBinding +import ru.itis.karakurik.androidLab2.extentions.findController + +class MainActivity : AppCompatActivity() { + private var binding: ActivityMainBinding? = null + private var controller: NavController? = null + private val repository by lazy { + WeatherRepository() + } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding?.root) + controller = binding?.navHostFragmentMain?.id?.let { findController(it) } + + lifecycleScope.launch { + try { + val response = repository.getWeather("Kazan") + + } catch (ex: Exception) { + Log.e("arg", ex.message.toString()) + } + } + } + + override fun onDestroy() { + super.onDestroy() + binding = null + controller = null + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt new file mode 100644 index 0000000..90afe7e --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt @@ -0,0 +1,8 @@ +package ru.itis.karakurik.androidLab2.ui.fragments + +import androidx.fragment.app.Fragment +import ru.itis.karakurik.androidLab2.R + +class DetailsFragment : Fragment(R.layout.fragment_details) { + +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt new file mode 100644 index 0000000..b55409c --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt @@ -0,0 +1,15 @@ +package ru.itis.karakurik.androidLab2.ui.fragments.list + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import ru.itis.karakurik.androidLab2.R + + +class SearchFragment : Fragment(R.layout.fragment_search) { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt new file mode 100644 index 0000000..c0e4c29 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt @@ -0,0 +1,15 @@ +package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler + +import androidx.recyclerview.widget.DiffUtil +import ru.itis.karakurik.androidLab2.models.CityWeather + +object CityWeatherDiffCallback : DiffUtil.ItemCallback(){ + override fun areItemsTheSame(oldItem: CityWeather, newItem: CityWeather): Boolean { + TODO("Not yet implemented") + } + + override fun areContentsTheSame(oldItem: CityWeather, newItem: CityWeather): Boolean { + TODO("Not yet implemented") + } + +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt new file mode 100644 index 0000000..bc61f5e --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt @@ -0,0 +1,19 @@ +package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler; + +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import ru.itis.karakurik.androidLab2.models.CityWeather + +class ListRecyclerAdapter( + +) : ListAdapter(CityWeatherDiffCallback) { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListViewHolder { + TODO("Not yet implemented") + } + + override fun onBindViewHolder(holder: ListViewHolder, position: Int) { + TODO("Not yet implemented") + } + +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListViewHolder.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListViewHolder.kt new file mode 100644 index 0000000..0b2c509 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListViewHolder.kt @@ -0,0 +1,10 @@ +package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler; + +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +class ListViewHolder( + itemView: View + +) : RecyclerView.ViewHolder(itemView) { +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 16f09a2..e4e5d57 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,18 +1,22 @@ - - + android:orientation="vertical" + tools:context=".ui.activities.MainActivity"> + + - + diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml new file mode 100644 index 0000000..b476ae2 --- /dev/null +++ b/app/src/main/res/layout/fragment_details.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 0000000..b3fb19a --- /dev/null +++ b/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/app/src/main/res/layout/list_item_city.xml b/app/src/main/res/layout/list_item_city.xml new file mode 100644 index 0000000..2383974 --- /dev/null +++ b/app/src/main/res/layout/list_item_city.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph_main.xml b/app/src/main/res/navigation/nav_graph_main.xml new file mode 100644 index 0000000..680e706 --- /dev/null +++ b/app/src/main/res/navigation/nav_graph_main.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 93bdd74..dd97e12 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ - Android-lab-2 - \ No newline at end of file + Weather App + + Hello blank fragment + diff --git a/build.gradle b/build.gradle index a36524f..f8126c1 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ buildscript { plugins { id "io.gitlab.arturbosch.detekt" version "1.18.1" + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } task clean(type: Delete) { From 325a1a73e48fbd5be361e6f321f125934b3a0c8f Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Mon, 28 Feb 2022 22:17:20 +0300 Subject: [PATCH 02/10] add fragments --- .idea/misc.xml | 3 +- app/build.gradle | 6 + .../androidLab2/data/WeatherRepository.kt | 55 +++++-- .../karakurik/androidLab2/data/api/Api.kt | 6 +- .../ApiKeyInterceptor.kt | 2 +- .../LangInterceptor.kt | 2 +- .../UnitsInterceptor.kt | 2 +- .../CitiesResponse.kt} | 6 +- .../City.kt} | 4 +- .../response/{ => citiesResponse}/Clouds.kt | 2 +- .../response/{ => citiesResponse}/Coord.kt | 2 +- .../api/response/{ => citiesResponse}/Main.kt | 2 +- .../api/response/{ => citiesResponse}/Sys.kt | 2 +- .../response/{ => citiesResponse}/Weather.kt | 2 +- .../api/response/{ => citiesResponse}/Wind.kt | 2 +- .../api/response/weatherResponse/Clouds.kt | 9 ++ .../api/response/weatherResponse/Coord.kt | 11 ++ .../data/api/response/weatherResponse/Main.kt | 23 +++ .../data/api/response/weatherResponse/Sys.kt | 17 +++ .../api/response/weatherResponse/Weather.kt | 15 ++ .../weatherResponse/WeatherResponse.kt | 33 +++++ .../data/api/response/weatherResponse/Wind.kt | 13 ++ .../androidLab2/models/CityWeather.kt | 5 +- .../models/convertors/TempColorConverter.kt | 17 +++ .../models/convertors/WindDegConvertor.kt | 1 - .../karakurik/androidLab2/myTestsApi/Main.kt | 14 +- .../androidLab2/ui/activities/MainActivity.kt | 1 - .../ui/fragments/DetailsFragment.kt | 47 ++++++ .../ui/fragments/list/SearchFragment.kt | 134 +++++++++++++++++- .../list/recycler/CityWeatherDiffCallback.kt | 4 +- .../list/recycler/ListItemViewHolder.kt | 31 ++++ .../list/recycler/ListRecyclerAdapter.kt | 29 +++- .../fragments/list/recycler/ListViewHolder.kt | 10 -- app/src/main/res/drawable/air.xml | 9 ++ app/src/main/res/layout/fragment_details.xml | 111 ++++++++++++++- app/src/main/res/layout/fragment_search.xml | 6 +- app/src/main/res/layout/list_item_city.xml | 8 +- .../main/res/navigation/nav_graph_main.xml | 5 + app/src/main/res/values/colors.xml | 11 ++ app/src/main/res/values/strings.xml | 3 + build.gradle | 2 +- 41 files changed, 594 insertions(+), 73 deletions(-) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/{Interceptors => interceptors}/ApiKeyInterceptor.kt (90%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/{Interceptors => interceptors}/LangInterceptor.kt (89%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/{Interceptors => interceptors}/UnitsInterceptor.kt (89%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{WeatherCitiesResponse.kt => citiesResponse/CitiesResponse.kt} (64%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{WeatherResponse.kt => citiesResponse/City.kt} (86%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Clouds.kt (62%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Coord.kt (70%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Main.kt (87%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Sys.kt (63%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Weather.kt (79%) rename app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/{ => citiesResponse}/Wind.kt (70%) create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Clouds.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Coord.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Main.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Sys.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Weather.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/WeatherResponse.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/data/api/response/weatherResponse/Wind.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/TempColorConverter.kt create mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListItemViewHolder.kt delete mode 100644 app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListViewHolder.kt create mode 100644 app/src/main/res/drawable/air.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index cb089fa..8df56a3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,8 +3,9 @@ + diff --git a/app/build.gradle b/app/build.gradle index efe4032..5dee505 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,7 +54,7 @@ dependencies { //Coil implementation "io.coil-kt:coil:1.1.1" - def nav_version = "2.4.1" + def nav_version = "2.3.5" implementation "androidx.navigation:navigation-fragment:2.4.1" implementation "androidx.navigation:navigation-ui:2.4.1" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4c5bf83..8ea13b5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,19 +1,21 @@ + package="ru.itis.karakurik.androidLab2"> - + + + android:theme="@style/Theme.Androidlab2" + android:usesCleartextTraffic="true"> + android:name=".presentation.activities.MainActivity" + android:exported="true"> diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt deleted file mode 100644 index 644a7ee..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/WeatherRepository.kt +++ /dev/null @@ -1,87 +0,0 @@ -package ru.itis.karakurik.androidLab2.data - -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.interceptors.ApiKeyInterceptor -import ru.itis.karakurik.androidLab2.data.api.interceptors.LangInterceptor -import ru.itis.karakurik.androidLab2.data.api.interceptors.UnitsInterceptor -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.models.CityWeather -import ru.itis.karakurik.androidLab2.models.convertors.WindDegConvertor - -private const val BASE_URL = "https://api.openweathermap.org/data/2.5/" - -class WeatherRepository { - 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() - } - - private val api: Api by lazy { - Retrofit.Builder() - .baseUrl(BASE_URL) - .client(okhttp) - .addConverterFactory(GsonConverterFactory.create()) - .build() - .create(Api::class.java) - } - - suspend fun getWeather(city: String): CityWeather { - return getCityWeather(api.getWeather(city)) - } - - private fun getCityWeather(weatherResponse: WeatherResponse) : CityWeather { - weatherResponse.run { - return CityWeather( - id, - name, - coord.lat, - coord.lon, - main.temp, - main.humidity, - WindDegConvertor.convertWindDeg(wind.deg) - ) - } - } - - private fun getCityWeather(city: City) : CityWeather { - city.run { - return CityWeather( - id, - name, - coord.lat, - coord.lon, - main.temp, - main.humidity, - WindDegConvertor.convertWindDeg(wind.deg) - ) - } - } - - suspend fun getCities(lat: Double, lon: Double, cnt: Int): MutableList { - val citiesResponse = api.getWeatherCities(lat, lon, cnt) - val list = ArrayList(cnt) - for(i in 0 until cnt) { - list.add(getCityWeather(citiesResponse.list[i])) - } - return list - } -} 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 0af1938..84c2972 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 @@ -9,8 +9,11 @@ interface Api { @GET("weather") suspend fun getWeather(@Query("q") city: String): WeatherResponse + @GET("weather") + suspend fun getWeather(@Query("id") id: Int): WeatherResponse + @GET("find") - suspend fun getWeatherCities( + suspend fun getWeathers( @Query("lat") lat: Double, @Query("lon") lon: Double, @Query("cnt") cnt: Int 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 new file mode 100644 index 0000000..9ad9452 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherIconUrlMapper.kt @@ -0,0 +1,7 @@ +package ru.itis.karakurik.androidLab2.data.api.mapper + +class WeatherIconUrlMapper { + fun map(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 new file mode 100644 index 0000000..c0c2078 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WeatherMapper.kt @@ -0,0 +1,36 @@ +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 + +class WeatherMapper( + private val windDegMapper: WindDegMapper, + private val weatherIconUrlMapper: WeatherIconUrlMapper +) { + fun map(weatherResponse: WeatherResponse) : Weather = Weather( + id = weatherResponse.id, + name = weatherResponse.name, + lat = weatherResponse.coord.lat, + lon = weatherResponse.coord.lon, + temp = weatherResponse.main.temp, + tempMin = weatherResponse.main.tempMin, + tempMax = weatherResponse.main.tempMax, + humidity = weatherResponse.main.humidity, + windDeg = windDegMapper.map(weatherResponse.wind.deg), + iconUrl = weatherIconUrlMapper.map(weatherResponse.weather[0].icon) + ) + + fun map(city: City) : Weather = Weather( + id = city.id, + name = city.name, + lat = city.coord.lat, + lon = city.coord.lon, + temp = city.main.temp, + tempMin = city.main.tempMin, + tempMax = city.main.tempMax, + humidity = city.main.humidity, + windDeg = windDegMapper.map(city.wind.deg), + iconUrl = weatherIconUrlMapper.map(city.weather[0].icon) + ) +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt similarity index 59% rename from app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt index 45d2f23..9360b16 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/WindDegConvertor.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/mapper/WindDegMapper.kt @@ -1,10 +1,9 @@ -package ru.itis.karakurik.androidLab2.models.convertors +package ru.itis.karakurik.androidLab2.data.api.mapper -import androidx.room.TypeConverter -import ru.itis.karakurik.androidLab2.models.WindDeg +import ru.itis.karakurik.androidLab2.domain.enum.WindDeg -object WindDegConvertor { - fun convertWindDeg(deg: Int): WindDeg { +class WindDegMapper { + fun map(deg: Int) : WindDeg { return when ((deg / 45 + 2* (deg%45) / 45) * 45 % 360) { 0 -> WindDeg.N 45 -> WindDeg.NE 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 new file mode 100644 index 0000000..978150c --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/DiContainer.kt @@ -0,0 +1,69 @@ +package ru.itis.karakurik.androidLab2.di + +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 + +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() + } + + val weatherMapper: WeatherMapper by lazy { + WeatherMapper( + windDegMapper, + weatherIconUrlMapper + ) + } + + val weatherRepository: WeatherRepository by lazy { + WeatherRepositoryImpl( + api, + weatherMapper + ) + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/ApiKeyInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt similarity index 90% rename from app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/ApiKeyInterceptor.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt index d3d6561..3c36c4a 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/ApiKeyInterceptor.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/ApiKeyInterceptor.kt @@ -1,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.data.api.interceptors +package ru.itis.karakurik.androidLab2.di.interceptors import okhttp3.Interceptor import okhttp3.Response diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/LangInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt similarity index 89% rename from app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/LangInterceptor.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt index 60ade9b..f604354 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/LangInterceptor.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/LangInterceptor.kt @@ -1,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.data.api.interceptors +package ru.itis.karakurik.androidLab2.di.interceptors import okhttp3.Interceptor import okhttp3.Response diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/UnitsInterceptor.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt similarity index 89% rename from app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/UnitsInterceptor.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt index 5617666..bc6a739 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/data/api/interceptors/UnitsInterceptor.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/di/interceptors/UnitsInterceptor.kt @@ -1,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.data.api.interceptors +package ru.itis.karakurik.androidLab2.di.interceptors import okhttp3.Interceptor import okhttp3.Response diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/entity/Weather.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/entity/Weather.kt new file mode 100644 index 0000000..e97c147 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/entity/Weather.kt @@ -0,0 +1,16 @@ +package ru.itis.karakurik.androidLab2.domain.entity + +import ru.itis.karakurik.androidLab2.domain.enum.WindDeg + +data class Weather( + val id: Int, + val name: String, + val lat: Double, + val lon: Double, + val temp: Double, + val tempMin: Double, + val tempMax: Double, + val humidity: Int, + val windDeg: WindDeg, + val iconUrl: String +) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/enum/WindDeg.kt similarity index 73% rename from app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/domain/enum/WindDeg.kt index 0227197..272cb4b 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/models/WindDeg.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/enum/WindDeg.kt @@ -1,4 +1,4 @@ -package ru.itis.karakurik.androidLab2.models +package ru.itis.karakurik.androidLab2.domain.enum enum class WindDeg ( val deg: Int ) { diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepository.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepository.kt new file mode 100644 index 0000000..24661d7 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepository.kt @@ -0,0 +1,9 @@ +package ru.itis.karakurik.androidLab2.domain.repository + +import ru.itis.karakurik.androidLab2.domain.entity.Weather + +interface WeatherRepository { + suspend fun getWeather(city: String): Weather + suspend fun getWeather(id: Int): Weather + suspend fun getWeathers(lat: Double, lon: Double, cnt: Int): MutableList +} 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 new file mode 100644 index 0000000..d741851 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/repository/WeatherRepositoryImpl.kt @@ -0,0 +1,28 @@ +package ru.itis.karakurik.androidLab2.domain.repository + +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 + +class WeatherRepositoryImpl( + private val api: Api, + private val weatherMapper: WeatherMapper +) : WeatherRepository { + + override suspend fun getWeather(city: String): Weather { + return weatherMapper.map(api.getWeather(city)) + } + + override suspend fun getWeather(id: Int): Weather { + return weatherMapper.map(api.getWeather(id)) + } + + override suspend fun getWeathers(lat: Double, lon: Double, cnt: Int): MutableList { + val citiesResponse = api.getWeathers(lat, lon, cnt) + val list = ArrayList(cnt) + for (city in citiesResponse.list) { + list.add(weatherMapper.map(city)) + } + return list + } +} 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 new file mode 100644 index 0000000..a0c0ce6 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeatherUseCase.kt @@ -0,0 +1,25 @@ +package ru.itis.karakurik.androidLab2.domain.usecase + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import ru.itis.karakurik.androidLab2.domain.entity.Weather +import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository + +class GetWeatherUseCase( + private val weatherRepository: WeatherRepository, + private val dispatcher: CoroutineDispatcher = Dispatchers.Main +) { + + suspend operator fun invoke(city: String): Weather { + return withContext(dispatcher) { + weatherRepository.getWeather(city) + } + } + + suspend operator fun invoke(id: Int): Weather { + return withContext(dispatcher) { + weatherRepository.getWeather(id) + } + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeathersUseCase.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeathersUseCase.kt new file mode 100644 index 0000000..9e79f98 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/domain/usecase/GetWeathersUseCase.kt @@ -0,0 +1,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.domain.entity.Weather +import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository + +class GetWeathersUseCase( + private val weatherRepository: WeatherRepository, + private val dispatcher: CoroutineDispatcher = Dispatchers.Main +) { + suspend operator fun invoke(lat: Double, lon: Double, cnt: Int): MutableList { + return withContext(dispatcher) { + weatherRepository.getWeathers(lat, lon, cnt) + } + } +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt deleted file mode 100644 index 2c36485..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/models/CityWeather.kt +++ /dev/null @@ -1,11 +0,0 @@ -package ru.itis.karakurik.androidLab2.models - -data class CityWeather( - val id: Int, - val name: String, - val lat: Double, - val lon: Double, - val temp: Double, - val humidity: Int, - val windDeg: WindDeg -) diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt deleted file mode 100644 index d8e1f8a..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/myTestsApi/Main.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ru.itis.karakurik.androidLab2.myTestsApi - -import kotlinx.coroutines.runBlocking -import ru.itis.karakurik.androidLab2.data.WeatherRepository -import ru.itis.karakurik.androidLab2.models.convertors.WindDegConvertor - -private const val KAZAN_LON = 49.1221 -private const val KAZAN_LAT = 55.7887 - -fun main() { - val a = WindDegConvertor.convertWindDeg(350) - - val repository = WeatherRepository() - - runBlocking { - val weather = repository.getWeather("Kazan") - val lat = weather.lat - val lon = weather.lon - - val weatherCities = repository.getCities(lat, lon, 10) - for (wC in weatherCities) { - println(wC.name + " " + wC.temp) - } - } -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt similarity index 54% rename from app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt index 96f4c4b..ef3f1a7 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/activities/MainActivity.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/activities/MainActivity.kt @@ -1,35 +1,21 @@ -package ru.itis.karakurik.androidLab2.ui.activities +package ru.itis.karakurik.androidLab2.presentation.activities import android.os.Bundle -import android.util.Log import androidx.appcompat.app.AppCompatActivity -import androidx.lifecycle.lifecycleScope import androidx.navigation.NavController -import kotlinx.coroutines.launch -import ru.itis.karakurik.androidLab2.data.WeatherRepository import ru.itis.karakurik.androidLab2.databinding.ActivityMainBinding import ru.itis.karakurik.androidLab2.extentions.findController class MainActivity : AppCompatActivity() { private var binding: ActivityMainBinding? = null private var controller: NavController? = null - private val repository by lazy { - WeatherRepository() - } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding?.root) - controller = binding?.navHostFragmentMain?.id?.let { findController(it) } - - lifecycleScope.launch { - try { - val response = repository.getWeather("Kazan") - - } catch (ex: Exception) { - Log.e("arg", ex.message.toString()) - } + binding = ActivityMainBinding.inflate(layoutInflater).also { + setContentView(it.root) } + controller = binding?.navHostFragmentMain?.id?.let { findController(it) } } override fun onDestroy() { diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/TempColorConverter.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt similarity index 83% rename from app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/TempColorConverter.kt rename to app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt index e2a60a7..365a004 100644 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/models/convertors/TempColorConverter.kt +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/convertors/TempColorConverter.kt @@ -1,6 +1,5 @@ -package ru.itis.karakurik.androidLab2.models.convertors +package ru.itis.karakurik.androidLab2.presentation.convertors -import android.graphics.Color import ru.itis.karakurik.androidLab2.R object TempColorConverter { 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 new file mode 100644 index 0000000..1a52e88 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/DetailsFragment.kt @@ -0,0 +1,71 @@ +package ru.itis.karakurik.androidLab2.presentation.fragments + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import coil.imageLoader +import coil.load +import coil.request.CachePolicy +import coil.request.ImageRequest +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +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.domain.usecase.GetWeatherUseCase + +class DetailsFragment : Fragment(R.layout.fragment_details) { + private var cityId: Int? = null + private var weather: Weather? = null + + private val getWeatherUseCase: GetWeatherUseCase by lazy { + GetWeatherUseCase(DiContainer.weatherRepository, Dispatchers.Default) + } + + private var binding: FragmentDetailsBinding? = null + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + return inflater.inflate(R.layout.fragment_details, container, false) + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle? + ) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentDetailsBinding.bind(view) + + arguments?.let { + cityId = it.getInt("ID") + } + + lifecycleScope.launch { + weather = cityId?.let { getWeatherUseCase(it) } + + binding?.run { + weather?.let { + tvCity.text = it.name + tvTemp.text = it.temp.toString() + "°C" + tvHumidityValue.text = it.humidity.toString() + "%" + tvWindDegValue.text = it.windDeg.toString() + ivAir.context.imageLoader.execute( + ImageRequest.Builder(ivAir.context) + .data(it.iconUrl) + .memoryCachePolicy(CachePolicy.DISABLED) + .placeholder(R.drawable.air) + .target(ivAir) + .build() + ) + } + } + } + } +} 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 new file mode 100644 index 0000000..1b95f47 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/SearchFragment.kt @@ -0,0 +1,121 @@ +package ru.itis.karakurik.androidLab2.presentation.fragments.list + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.appcompat.widget.SearchView +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController +import kotlinx.coroutines.* +import ru.itis.karakurik.androidLab2.R +import ru.itis.karakurik.androidLab2.databinding.FragmentSearchBinding +import ru.itis.karakurik.androidLab2.di.DiContainer +import ru.itis.karakurik.androidLab2.domain.entity.Weather +import ru.itis.karakurik.androidLab2.domain.enum.WindDeg +import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase +import ru.itis.karakurik.androidLab2.domain.usecase.GetWeathersUseCase +import ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler.ListRecyclerAdapter +import timber.log.Timber + +private const val COUNT_OF_CITIES_IN_LIST = 10 +private const val HARDCODE_LAT = 55.7887 +private const val HARDCODE_LON = 49.1221 + +class SearchFragment : Fragment(R.layout.fragment_search) { + private var binding: FragmentSearchBinding? = null + private var listRecyclerAdapter: ListRecyclerAdapter? = null + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) + + private val getWeatherUseCase: GetWeatherUseCase by lazy { + GetWeatherUseCase(DiContainer.weatherRepository, Dispatchers.Default) + } + + private val getWeathersUseCase: GetWeathersUseCase by lazy { + GetWeathersUseCase(DiContainer.weatherRepository, Dispatchers.Default) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return inflater.inflate(R.layout.fragment_search, container, false) + } + + override fun onViewCreated( + view: View, + savedInstanceState: Bundle? + ) { + super.onViewCreated(view, savedInstanceState) + binding = FragmentSearchBinding.bind(view) + + initRecyclerView() + + binding?.svSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + lifecycleScope.launch { + Timber.d("Pressed query button") + try { + getWeather(query) + } catch (ex: Exception) { + Toast.makeText( + context, + "Не удалось найти", + Toast.LENGTH_LONG + ).show() + Timber.e(ex.message.toString()) + } + } + return false + } + + override fun onQueryTextChange(newText: String): Boolean { + Timber.d("Query text changed") + return false + } + }) + } + + private suspend fun getWeather(city: String) { + val weather = getWeatherUseCase(city) + showDetailsFragment(weather.id) + } + + + private fun initRecyclerView() { + binding?.rvSearch?.run { + listRecyclerAdapter = ListRecyclerAdapter { id -> + showDetailsFragment(id) + } + adapter = listRecyclerAdapter + } + + getWeathers(HARDCODE_LAT, HARDCODE_LON, COUNT_OF_CITIES_IN_LIST) + } + + private fun getWeathers(lat: Double, lon: Double, cnt: Int) { + lifecycleScope.launch { + try { + val weathers = getWeathersUseCase(lat, lon, cnt) + listRecyclerAdapter?.submitList(weathers) + } catch (ex: Exception) { + Toast.makeText( + context, + "Не удалось найти", + Toast.LENGTH_LONG + ).show() + } + } + } + + private fun showDetailsFragment(id: Int) { + val bundle = bundleOf( + "ID" to id + ) + findNavController().navigate(R.id.action_fragment_search_to_fragment_details, bundle) + } +} 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 new file mode 100644 index 0000000..a732bab --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/CityWeatherDiffCallback.kt @@ -0,0 +1,15 @@ +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(){ + override fun areItemsTheSame(oldItem: Weather, newItem: Weather): Boolean { + return oldItem.id == newItem.id + } + + 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 new file mode 100644 index 0000000..503d9e2 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListItemViewHolder.kt @@ -0,0 +1,48 @@ +package ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler; + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.ColorRes +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import ru.itis.karakurik.androidLab2.databinding.ListItemCityBinding +import ru.itis.karakurik.androidLab2.domain.entity.Weather +import ru.itis.karakurik.androidLab2.presentation.convertors.TempColorConverter.getColor + +class ListItemViewHolder( + private val binding: ListItemCityBinding, + private val onItemClick: (id: Int) -> Unit +) : RecyclerView.ViewHolder(binding.root) { + + fun bind(weather: Weather) { + with(binding) { + tvCityItem.text = weather.name + tvTempItem.text = weather.temp.toString() + tvTempItem.setTextColor( + ContextCompat.getColor( + tvTempItem.context, + getColor(weather.temp) + ) + ) + + root.setOnClickListener { + onItemClick(weather.id) + } + } + } + + companion object { + fun create( + parent: ViewGroup, + onItemClick: (id: Int) -> Unit + ) = ListItemViewHolder( + ListItemCityBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ), + 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 new file mode 100644 index 0000000..fd468f5 --- /dev/null +++ b/app/src/main/java/ru/itis/karakurik/androidLab2/presentation/fragments/list/recycler/ListRecyclerAdapter.kt @@ -0,0 +1,29 @@ +package ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler; + +import android.view.ViewGroup +import androidx.recyclerview.widget.ListAdapter +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 + ) = ListItemViewHolder.create( + parent, + onItemClick + ) + + override fun onBindViewHolder( + holder: ListItemViewHolder, + position: Int + ) = holder.bind( + getItem(position) + ) + + /*override fun submitList(list: MutableList?) { + super.submitList(if (list == null) null else ArrayList(list)) + }*/ + +} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt deleted file mode 100644 index e5e048c..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/DetailsFragment.kt +++ /dev/null @@ -1,55 +0,0 @@ -package ru.itis.karakurik.androidLab2.ui.fragments - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope -import kotlinx.coroutines.launch -import ru.itis.karakurik.androidLab2.R -import ru.itis.karakurik.androidLab2.data.WeatherRepository -import ru.itis.karakurik.androidLab2.databinding.FragmentDetailsBinding -import ru.itis.karakurik.androidLab2.models.CityWeather - -class DetailsFragment : Fragment(R.layout.fragment_details) { - private var cityName: String? = null - private var weather: CityWeather? = null - - private val repository by lazy { - WeatherRepository() - } - - private var binding: FragmentDetailsBinding? = null - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_details, container, false) - } - - override fun onViewCreated( - view: View, - savedInstanceState: Bundle? - ) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentDetailsBinding.bind(view) - - arguments?.let { - cityName = it.getString("CITY_NAME") - } - - lifecycleScope.launch { - weather = cityName?.let { repository.getWeather(it) } - } - - binding?.run { - tvCity.text = weather?.name - tvTemp.text = weather?.temp.toString() + "°C" - tvHumidityValue.text = weather?.humidity.toString() + "%" - tvWindDegValue.text = weather?.windDeg.toString() - } - } -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt deleted file mode 100644 index b453154..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/SearchFragment.kt +++ /dev/null @@ -1,145 +0,0 @@ -package ru.itis.karakurik.androidLab2.ui.fragments.list - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Toast -import androidx.appcompat.widget.SearchView -import androidx.core.os.bundleOf -import androidx.fragment.app.Fragment -import androidx.navigation.fragment.findNavController -import kotlinx.coroutines.* -import ru.itis.karakurik.androidLab2.R -import ru.itis.karakurik.androidLab2.data.WeatherRepository -import ru.itis.karakurik.androidLab2.databinding.FragmentSearchBinding -import ru.itis.karakurik.androidLab2.models.CityWeather -import ru.itis.karakurik.androidLab2.models.WindDeg -import ru.itis.karakurik.androidLab2.ui.fragments.list.recycler.ListRecyclerAdapter -import timber.log.Timber - -private const val COUNT_OF_CITIES_IN_RECYCLER_VIEW = 10 -private const val HARDCODE_LAT = 55.7887 -private const val HARDCODE_LON = 49.1221 - -class SearchFragment() : Fragment() { - private var binding: FragmentSearchBinding? = null - private val repository by lazy { - WeatherRepository() - } - private var listRecyclerAdapter: ListRecyclerAdapter? = null - private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main) - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - return inflater.inflate(R.layout.fragment_search, container, false) - } - - override fun onViewCreated( - view: View, - savedInstanceState: Bundle? - ) { - super.onViewCreated(view, savedInstanceState) - binding = FragmentSearchBinding.bind(view) - - initRecyclerView() - - binding?.svSearch?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - Timber.d("Pressed query button") -// getWeatherList(query) - try { - getWeather(query) - - } catch (ex: Exception) { - Toast.makeText( - context, - "Не удалось найти", - Toast.LENGTH_LONG - ).show() - } - return false - } - - override fun onQueryTextChange(newText: String): Boolean { - Timber.d("Query text changed") - return false - } - }) - } - - private fun getWeather(city: String) { - scope.launch { - withContext(Dispatchers.IO) { - val weather = repository.getWeather(city) - showDetailsFragment(weather.name) - } - } - } - - private fun getCities(city: String?) { - var weather: CityWeather? - scope.launch { - try { - withContext(Dispatchers.IO) { - weather = city?.let { repository.getWeather(it) } - } - var weatherCities: MutableList? = null - withContext(Dispatchers.IO) { - weather?.let { weather -> - weatherCities = - repository - .getCities( - weather.lat, - weather.lon, - COUNT_OF_CITIES_IN_RECYCLER_VIEW - ) - } - } - listRecyclerAdapter?.submitList(weatherCities) - } catch (ex: Exception) { - Toast.makeText( - context, - "Не удалось найти", - Toast.LENGTH_LONG - ).show() - } - } - } - - - private fun initRecyclerView() { - binding?.rvSearch?.run { - listRecyclerAdapter = ListRecyclerAdapter { city -> - showDetailsFragment(city) - } - adapter = listRecyclerAdapter - } - listRecyclerAdapter?.submitList( - arrayListOf( - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - CityWeather(551487, "Kazan", 0.0, 0.0, 0.0, 250, WindDeg.NW), - ) - ) -// getCities("Kazan") - getCities(HARDCODE_LAT, HARDCODE_LON, COUNT_OF_CITIES_IN_RECYCLER_VIEW) - } - - private fun getCities(lat: Double, lon: Double, cnt: Int) { - // TODO: - } - - private fun showDetailsFragment(city: String) { - val bundle = bundleOf( - "CITY_NAME" to city - ) - findNavController().navigate(R.id.action_searchFragment_to_detailsFragment, bundle) - } -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt deleted file mode 100644 index 9a53238..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/CityWeatherDiffCallback.kt +++ /dev/null @@ -1,15 +0,0 @@ -package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler - -import androidx.recyclerview.widget.DiffUtil -import ru.itis.karakurik.androidLab2.models.CityWeather - -object CityWeatherDiffCallback : DiffUtil.ItemCallback(){ - override fun areItemsTheSame(oldItem: CityWeather, newItem: CityWeather): Boolean { - return oldItem.id == newItem.id - } - - override fun areContentsTheSame(oldItem: CityWeather, newItem: CityWeather): Boolean { - return oldItem == newItem - } - -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListItemViewHolder.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListItemViewHolder.kt deleted file mode 100644 index 69ed1b6..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListItemViewHolder.kt +++ /dev/null @@ -1,31 +0,0 @@ -package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler; - -import android.annotation.SuppressLint -import android.graphics.Color -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import ru.itis.karakurik.androidLab2.R -import ru.itis.karakurik.androidLab2.databinding.ListItemCityBinding -import ru.itis.karakurik.androidLab2.models.CityWeather -import ru.itis.karakurik.androidLab2.models.convertors.TempColorConverter -import ru.itis.karakurik.androidLab2.models.convertors.TempColorConverter.getColor - -class ListItemViewHolder( - itemView: View, - private val onItemClick: (city: String) -> Unit -) : RecyclerView.ViewHolder(itemView) { - private val binding = ListItemCityBinding.bind(itemView) - - fun bind(cityWeather: CityWeather) { - with(binding) { - tvCityItem.text = cityWeather.name - tvTempItem.text = cityWeather.temp.toString() - tvTempItem.setTextColor(getColor(cityWeather.temp)) - - root.setOnClickListener { - onItemClick(cityWeather.name) - } - } - } - -} diff --git a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt b/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt deleted file mode 100644 index 77c8ba5..0000000 --- a/app/src/main/java/ru/itis/karakurik/androidLab2/ui/fragments/list/recycler/ListRecyclerAdapter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ru.itis.karakurik.androidLab2.ui.fragments.list.recycler; - -import android.view.LayoutInflater -import android.view.ViewGroup -import androidx.recyclerview.widget.ListAdapter -import ru.itis.karakurik.androidLab2.R -import ru.itis.karakurik.androidLab2.models.CityWeather -import java.util.ArrayList - -class ListRecyclerAdapter( - private val onItemClick: (city: String) -> Unit -) : ListAdapter(CityWeatherDiffCallback) { - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ) = ListItemViewHolder( - LayoutInflater - .from(parent.context) - .inflate(R.layout.list_item_city, parent, false), - onItemClick - ) - - override fun onBindViewHolder( - holder: ListItemViewHolder, - position: Int - ) = holder.bind( - getItem(position) - ) - - override fun submitList(list: MutableList?) { - super.submitList(if (list == null) null else ArrayList(list)) - } - -} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index e4e5d57..bc0b2ce 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,18 +5,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:context=".ui.activities.MainActivity"> + tools:context=".presentation.activities.MainActivity"> diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index 7c66c2b..b131eee 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -6,7 +6,7 @@ android:layout_height="match_parent" android:background="#373795" android:padding="20dp" - tools:context=".ui.fragments.DetailsFragment"> + tools:context=".presentation.fragments.DetailsFragment"> diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index d8f28a2..226c419 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -4,21 +4,15 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".ui.fragments.list.SearchFragment"> + tools:context=".presentation.fragments.list.SearchFragment"> - - - + app:layout_constraintBottom_toTopOf="@id/rv_search" + android:layout_height="wrap_content"/> + tools:listitem="@layout/list_item_city" + app:layout_constraintTop_toBottomOf="@id/sv_search"/> diff --git a/app/src/main/res/navigation/nav_graph_main.xml b/app/src/main/res/navigation/nav_graph_main.xml index 59d79cc..eb4ab08 100644 --- a/app/src/main/res/navigation/nav_graph_main.xml +++ b/app/src/main/res/navigation/nav_graph_main.xml @@ -3,29 +3,26 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/nav_graph_main" - app:startDestination="@id/searchFragment"> + app:startDestination="@id/fragment_search"> - - + + + android:id="@+id/action_fragment_details_to_fragment_search" + app:destination="@id/fragment_search" /> From bda69e032313a49c8675aad650a4719715c1dd32 Mon Sep 17 00:00:00 2001 From: Insaf Fayzrakhmanov Date: Wed, 9 Mar 2022 23:44:25 +0300 Subject: [PATCH 04/10] del action bar + items to cardV --- .../fragments/list/SearchFragment.kt | 2 +- app/src/main/res/layout/fragment_details.xml | 2 +- app/src/main/res/layout/fragment_search.xml | 1 + app/src/main/res/layout/list_item_city.xml | 52 ++++++++++--------- app/src/main/res/values-night/themes.xml | 2 +- app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/themes.xml | 2 +- 7 files changed, 34 insertions(+), 28 deletions(-) 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 1b95f47..b57fb03 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 @@ -21,7 +21,7 @@ import ru.itis.karakurik.androidLab2.domain.usecase.GetWeathersUseCase import ru.itis.karakurik.androidLab2.presentation.fragments.list.recycler.ListRecyclerAdapter import timber.log.Timber -private const val COUNT_OF_CITIES_IN_LIST = 10 +private const val COUNT_OF_CITIES_IN_LIST = 20 private const val HARDCODE_LAT = 55.7887 private const val HARDCODE_LON = 49.1221 diff --git a/app/src/main/res/layout/fragment_details.xml b/app/src/main/res/layout/fragment_details.xml index b131eee..20d5d21 100644 --- a/app/src/main/res/layout/fragment_details.xml +++ b/app/src/main/res/layout/fragment_details.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#373795" + android:background="@color/weatherColor" android:padding="20dp" tools:context=".presentation.fragments.DetailsFragment"> diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 226c419..787f50f 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -11,6 +11,7 @@ android:layout_width="match_parent" app:queryHint="@string/query_hint" app:layout_constraintTop_toTopOf="parent" + app:iconifiedByDefault="false" app:layout_constraintBottom_toTopOf="@id/rv_search" android:layout_height="wrap_content"/> diff --git a/app/src/main/res/layout/list_item_city.xml b/app/src/main/res/layout/list_item_city.xml index 3290255..8a2e615 100644 --- a/app/src/main/res/layout/list_item_city.xml +++ b/app/src/main/res/layout/list_item_city.xml @@ -1,33 +1,37 @@ - - - - + android:paddingVertical="10dp" + android:paddingHorizontal="30dp" + android:orientation="horizontal"> + - + + + diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 9189b75..db0a143 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,6 +1,6 @@ -