From e872fe4e8a5843087de8f977b81c4d069ff032ac Mon Sep 17 00:00:00 2001 From: JUNGGWAN KIM Date: Thu, 26 Sep 2024 18:55:56 +0900 Subject: [PATCH] openWeatherMap -> weatherApi --- .../rasel/lunar/launcher/LauncherActivity.kt | 4 +- .../lunar/launcher/model/WeatherForcast.kt | 168 ++++++++++++++++++ .../rasel/lunar/launcher/model/WeatherInfo.kt | 120 ++++++------- .../launcher/workers/OpenWeatherGetter.kt | 136 +++++++------- .../rasel/lunar/launcher/workers/WorkersDb.kt | 12 +- app/src/main/res/drawable-nodpi/ico_time.png | Bin 0 -> 8728 bytes app/src/main/res/drawable/ico_.xml | 4 + .../main/res/layout/item_rec_hourly_dress.xml | 39 ++++ .../res/layout/recommended_hourly_dress.xml | 46 +++++ 9 files changed, 400 insertions(+), 129 deletions(-) create mode 100644 app/src/main/kotlin/rasel/lunar/launcher/model/WeatherForcast.kt create mode 100644 app/src/main/res/drawable-nodpi/ico_time.png create mode 100644 app/src/main/res/drawable/ico_.xml create mode 100644 app/src/main/res/layout/item_rec_hourly_dress.xml create mode 100644 app/src/main/res/layout/recommended_hourly_dress.xml diff --git a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt index c7ecb531..4dae59bf 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/LauncherActivity.kt @@ -33,6 +33,7 @@ import android.content.pm.PackageManager import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Color +import android.location.Location import android.net.Uri import android.net.http.SslError import android.os.Build @@ -157,7 +158,8 @@ internal class LauncherActivity : AppCompatActivity() { @JvmStatic var lActivity: LauncherActivity? = null @JvmStatic var appWidgetManager: AppWidgetManager? = null @JvmStatic var appWidgetHost: WidgetHost? = null - fun refreshDeviceData() { + fun refreshDeviceData() + { Executors.newSingleThreadScheduledExecutor().schedule({ mWorkManager?.cancelAllWorkByTag(SMS_WORK_TAG) mWorkManager?.enqueueUniquePeriodicWork( diff --git a/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherForcast.kt b/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherForcast.kt new file mode 100644 index 00000000..6a477824 --- /dev/null +++ b/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherForcast.kt @@ -0,0 +1,168 @@ +package rasel.lunar.launcher.model + +import io.realm.kotlin.ext.realmListOf +import io.realm.kotlin.types.RealmList +import io.realm.kotlin.types.RealmObject +import io.realm.kotlin.types.annotations.Ignore + + +class WeatherForcast: RealmObject { + var location: Location? = null + var current: Current? = null + var forecast: Forecast? = null + + fun readyForSaving() { + forecast?.fill() + } +} + +////////////////////////////////// +class Location: RealmObject { + var name: String? = null + var region: String? = null + var country: String? = null + var lat = 0.0 + var lon = 0.0 + var tz_id: String? = null + var localtime_epoch = 0 + var localtime: String? = null +} + +class Current: RealmObject { + var last_updated_epoch = 0 + var last_updated: String? = null + var temp_c = 0.0 + var temp_f = 0.0 + var is_day = 0 + var condition: Condition? = null + var wind_mph = 0.0 + var wind_kph = 0.0 + var wind_degree = 0 + var wind_dir: String? = null + var pressure_mb = 0.0 + var pressure_in = 0.0 + var precip_mm = 0.0 + var precip_in = 0.0 + var humidity = 0 + var cloud = 0 + var feelslike_c = 0.0 + var feelslike_f = 0.0 + var windchill_c = 0.0 + var windchill_f = 0.0 + var heatindex_c = 0.0 + var heatindex_f = 0.0 + var dewpoint_c = 0.0 + var dewpoint_f = 0.0 + var vis_km = 0.0 + var vis_miles = 0.0 + var uv = 0.0 + var gust_mph = 0.0 + var gust_kph = 0.0 +} + +class Forecast: RealmObject { + @Ignore + var forecastday: ArrayList = arrayListOf() + var forecastdayRealm: RealmList = realmListOf() + + fun fill() { + if (forecastdayRealm.addAll(forecastday)) { + forecastdayRealm.forEach { f -> f.fill() } + } + } +} + +////////////////////////////////// +// Current +class Condition: RealmObject { + var text: String? = null + var icon: String? = null + var code = 0 +} +// Forecast +class Forecastday: RealmObject { + var date: String? = null + var date_epoch = 0 + var day: Day? = null + var astro: Astro? = null + @Ignore + var hour: ArrayList = arrayListOf() + var hourRealm: RealmList = realmListOf() + + fun fill() { + hourRealm.addAll(hour) + } +} + +////////////////////////////////// +// Forecastday +class Day: RealmObject { + var maxtemp_c = 0.0 + var maxtemp_f = 0.0 + var mintemp_c = 0.0 + var mintemp_f = 0.0 + var avgtemp_c = 0.0 + var avgtemp_f = 0.0 + var maxwind_mph = 0.0 + var maxwind_kph = 0.0 + var totalprecip_mm = 0.0 + var totalprecip_in = 0.0 + var totalsnow_cm = 0.0 + var avgvis_km = 0.0 + var avgvis_miles = 0.0 + var avghumidity = 0 + var daily_will_it_rain = 0 + var daily_chance_of_rain = 0 + var daily_will_it_snow = 0 + var daily_chance_of_snow = 0 + var condition: Condition? = null + var uv = 0.0 +} + +class Astro: RealmObject { + var sunrise: String? = null + var sunset: String? = null + var moonrise: String? = null + var moonset: String? = null + var moon_phase: String? = null + var moon_illumination = 0 + var is_moon_up = 0 + var is_sun_up = 0 +} + +class Hour: RealmObject { + var time_epoch = 0 + var time: String? = null + var temp_c = 0.0 + var temp_f = 0.0 + var is_day = 0 + var condition: Condition? = null + var wind_mph = 0.0 + var wind_kph = 0.0 + var wind_degree = 0 + var wind_dir: String? = null + var pressure_mb = 0.0 + var pressure_in = 0.0 + var precip_mm = 0.0 + var precip_in = 0.0 + var snow_cm = 0.0 + var humidity = 0 + var cloud = 0 + var feelslike_c = 0.0 + var feelslike_f = 0.0 + var windchill_c = 0.0 + var windchill_f = 0.0 + var heatindex_c = 0.0 + var heatindex_f = 0.0 + var dewpoint_c = 0.0 + var dewpoint_f = 0.0 + var will_it_rain = 0 + var chance_of_rain = 0 + var will_it_snow = 0 + var chance_of_snow = 0 + var vis_km = 0.0 + var vis_miles = 0.0 + var gust_mph = 0.0 + var gust_kph = 0.0 + var uv = 0.0 +} \ No newline at end of file diff --git a/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherInfo.kt b/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherInfo.kt index bf249180..3c03bfee 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherInfo.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/model/WeatherInfo.kt @@ -7,67 +7,67 @@ import io.realm.kotlin.types.RealmList import io.realm.kotlin.types.RealmObject import io.realm.kotlin.types.annotations.Ignore -class WeatherInfo : RealmObject { - constructor() - constructor(coord: Location?, weather: Array?, main: Main?) { - this.coord = coord - weather?.let { - this.weather?.addAll(it) - } +//class WeatherInfo : RealmObject { +// constructor() +//// constructor(coord: Loc?, weather: Array?, main: Main?) { +//// this.coord = coord +//// weather?.let { +//// this.weather?.addAll(it) +//// } +//// +//// this.main = main +//// } +// +// @SerializedName("coord") +// var coord: Loc? = null +// +// @Ignore +// @SerializedName("weather") +// var weather: ArrayList? = arrayListOf() +// +// var weathers: RealmList = realmListOf() +// +// @SerializedName("base") +// var base: String? = null +// +// @SerializedName("main") +// var main: Main? = null +//fun filled() { +// weather?.let { +// weathers.addAll(it) +// } +// +//} +// +// @SerializedName("visibility") +// var visibility: String? = null +// +// @SerializedName("wind") +// var wind: Wind? = null +// +// @SerializedName("clouds") +// var clouds: Clouds? = null +// +// @SerializedName("dt") +// var dt: String? = null +// +// @SerializedName("sys") +// var sys: Sys? = null +// +// @SerializedName("timezone") +// var timezone: String? = null +// +// @SerializedName("id") +// var id: String? = null +// +// @SerializedName("name") +// var name: String? = null +// +// @SerializedName("cod") +// var cod: String? = null +// } - this.main = main - } - - @SerializedName("coord") - var coord: Location? = null - - @Ignore - @SerializedName("weather") - var weather: ArrayList? = arrayListOf() - - var weathers: RealmList = realmListOf() - - @SerializedName("base") - var base: String? = null - - @SerializedName("main") - var main: Main? = null -fun filled() { - weather?.let { - weathers.addAll(it) - } - -} - - @SerializedName("visibility") - var visibility: String? = null - - @SerializedName("wind") - var wind: Wind? = null - - @SerializedName("clouds") - var clouds: Clouds? = null - - @SerializedName("dt") - var dt: String? = null - - @SerializedName("sys") - var sys: Sys? = null - - @SerializedName("timezone") - var timezone: String? = null - - @SerializedName("id") - var id: String? = null - - @SerializedName("name") - var name: String? = null - - @SerializedName("cod") - var cod: String? = null - } - -class Location : RealmObject { +class Loc : RealmObject { var lon: String? = null var lat: String? = null } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/OpenWeatherGetter.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/OpenWeatherGetter.kt index d7b4080b..5700fe9a 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/OpenWeatherGetter.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/OpenWeatherGetter.kt @@ -7,105 +7,109 @@ import androidx.work.WorkerParameters import com.google.android.gms.location.LocationServices import com.google.android.gms.location.Priority import com.google.android.gms.tasks.CancellationTokenSource -import io.realm.kotlin.Realm -import io.realm.kotlin.RealmConfiguration import io.realm.kotlin.UpdatePolicy import io.realm.kotlin.ext.query -import io.realm.kotlin.types.TypedRealmObject -import rasel.lunar.launcher.model.WeatherInfo +import rasel.lunar.launcher.model.WeatherForcast import rasel.lunar.launcher.utils.BLog import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import retrofit2.create import retrofit2.http.GET import retrofit2.http.Path import retrofit2.http.Query -import kotlin.reflect.KClass class OpenWeatherGetter(context: Context, workerParams: WorkerParameters) : BaseGetter(context, workerParams) { companion object { val TAG = "OpenWeatherGetter" } ////////////////////////////////////////// - val KEY = "87cd0810b7e4b4debd31a6ef98b98154" // openWeatherMap api key - var alt: Double? = null // 경도 + // openWeatherMap + val URI_OPEN_WEATHER_MAP = "https://api.openweathermap.org" + val KEY_OPEN_WEATHER_MAP = "87cd0810b7e4b4debd31a6ef98b98154" + // weatherapi + val VER_WEATHERAPI = "v1" + val URI_WEATHERAPI = "https://api.weatherapi.com" + val KEY_WEATHERAPI = "8133d83d23ab4175a4160624241909" + val DAYS = 1 + ////////////////////////////////////////// + var lon: Double? = null // 경도 var lat: Double? = null // 위도 ////////////////////////////////////////// + @SuppressLint("MissingPermission") override fun realWork(): Result { BLog.LOGE("${TAG} realWork() ") +// setLocation() - // 위치 위,경도 가져오자 - setLocation() + fun location(loc: Location?) { + val service = Retrofit.Builder() + .baseUrl(URI_WEATHERAPI) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .run { this.create() } + service.getForecast( + ver = VER_WEATHERAPI, + key = KEY_WEATHERAPI, + q = "${loc?.latitude ?: lat},${loc?.longitude ?: lon}", + days = DAYS.toString() + )?.execute()?.let { response -> + response.body()?.let { weatherInfo -> + BLog.LOGE("Location error >>> $weatherInfo") + WorkersDb.getRealm().writeBlocking { + weatherInfo.readyForSaving() + copyToRealm(weatherInfo, UpdatePolicy.ALL) + } + BLog.LOGE("saved weatherForcast >>> ${WorkersDb.getRealm().query().first().find()}") + } + } + } + + val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this.applicationContext) + fusedLocationProviderClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY,CancellationTokenSource().token) + .addOnSuccessListener{ success: Location? -> + success?.let { + BLog.LOGE("Location >>> $it") + BLog.LOGE("Location >>> (latitude)${it.longitude}/(longitude)${it.latitude}") + lon = it.longitude + lat = it.latitude + + //{https://home.openweathermap.org/api 에서 정보를 조회 하자} + location(null) + } + }.addOnFailureListener{ + BLog.LOGE("Location error >>> $it") + } +// } return Result.success() } @SuppressLint("MissingPermission") fun setLocation() { - val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this.applicationContext) - fusedLocationProviderClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY,CancellationTokenSource().token) - .addOnSuccessListener{ success: Location? -> - success?.let { - BLog.LOGE("Location >>> $it") - BLog.LOGE("Location altitude >>> ${it.longitude}") - alt = it.longitude - BLog.LOGE("Location latitude >>> ${it.latitude}") - lat = it.latitude - //{https://home.openweathermap.org/api 에서 정보를 조회 하자} - getWeather() - - } - }.addOnFailureListener{ - BLog.LOGE("Location error >>> $it") - } - return } - fun getWeather() { - val retro = Retrofit.Builder() - .baseUrl("https://api.openweathermap.org") - .addConverterFactory(GsonConverterFactory.create()) - .build() - val service = retro.create() - BLog.LOGE("Location lat >>> ${lat}") - BLog.LOGE("Location alt >>> ${alt}") - if (lat != null && alt != null) { - val call = service.getPosts(lat!!, alt!!, KEY) - call?.execute()?.let {response -> - response.body()?.let { w -> - BLog.LOGE("Location error >>> ${w.coord}}") - WorkersDb.getRealm().writeBlocking { - w.filled().run { - copyToRealm(w, UpdatePolicy.ALL) - } - } - BLog.LOGE("db에 저장된 weatherInfo >>> ${WorkersDb.getRealm().query().first().find()?.base}") - } - } -// call?.enqueue(object : Callback { -// override fun onResponse( -// call: Call, -// response: Response -// ) { -// BLog.LOGE("Location error >>> $response") -// // Realm을 이용해서 db에 저장 -// -// } -// -// override fun onFailure(call: Call, t: Throwable) { -// BLog.LOGE("Location error >>> $t") -// } -// }) - } + fun getWeather(loc: Location?) { + } } interface RestrofitService { - @GET("/data/2.5/weather") - fun getPosts(@Query("lat") lat: Double, @Query("lon") alt: Double, @Query("appid") key: String): Call? + // open_weather_map +// @GET("/data/2.5/weather") +// fun getPosts( +// @Query("lat") lat: Double, +// @Query("lon") lon: Double, +// @Query("appid") key: String +// ): Call? + + // weather_api + @GET("/{ver}/forecast.json") + fun getForecast( + @Path("ver") ver: String, + @Query("key") key: String, + @Query("q") q: String, + @Query("days") days: String + ): Call? } diff --git a/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt b/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt index 4cdeee1c..889d4a04 100644 --- a/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt +++ b/app/src/main/kotlin/rasel/lunar/launcher/workers/WorkersDb.kt @@ -13,9 +13,16 @@ import io.realm.kotlin.types.annotations.Ignore import io.realm.kotlin.types.annotations.PrimaryKey import rasel.lunar.launcher.apps.SimpleContact import rasel.lunar.launcher.model.AppInfo +import rasel.lunar.launcher.model.Astro import rasel.lunar.launcher.model.BotCommandEentitie import rasel.lunar.launcher.model.Clouds +import rasel.lunar.launcher.model.Condition +import rasel.lunar.launcher.model.Current import rasel.lunar.launcher.model.CurrentPlayItem +import rasel.lunar.launcher.model.Day +import rasel.lunar.launcher.model.Forecast +import rasel.lunar.launcher.model.Forecastday +import rasel.lunar.launcher.model.Hour import rasel.lunar.launcher.model.Location import rasel.lunar.launcher.model.Main import rasel.lunar.launcher.model.NotificationItem @@ -28,7 +35,7 @@ import rasel.lunar.launcher.model.TelegramData import rasel.lunar.launcher.model.TelegramFrom import rasel.lunar.launcher.model.TelegramMessage import rasel.lunar.launcher.model.Weather -import rasel.lunar.launcher.model.WeatherInfo +import rasel.lunar.launcher.model.WeatherForcast import rasel.lunar.launcher.model.Wind import kotlin.reflect.KClass @@ -37,7 +44,8 @@ object WorkersDb { val clazz : Set> = setOf(RssData::class, NotificationItem::class, AppInfo::class,SimpleContact::class, RecentCall::class, RecentSms::class, CurrentPlayItem::class, - TelegramBotUpdate::class, TelegramData::class, TelegramMessage::class, TelegramChat::class, BotCommandEentitie::class, TelegramFrom::class, WeatherInfo::class, Main::class, Weather::class, Location::class, Wind::class, Clouds::class, Sys::class + TelegramBotUpdate::class, TelegramData::class, TelegramMessage::class, TelegramChat::class, BotCommandEentitie::class, TelegramFrom::class, + WeatherForcast::class, Location::class, Current::class, Forecast::class, Condition::class, Forecastday::class, Day::class, Astro::class, Hour::class ) val schemaVersion : Long = 0L diff --git a/app/src/main/res/drawable-nodpi/ico_time.png b/app/src/main/res/drawable-nodpi/ico_time.png new file mode 100644 index 0000000000000000000000000000000000000000..d77f61157d605098026ea36676a64e943b48348a GIT binary patch literal 8728 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 + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_rec_hourly_dress.xml b/app/src/main/res/layout/item_rec_hourly_dress.xml new file mode 100644 index 00000000..74b2ac4a --- /dev/null +++ b/app/src/main/res/layout/item_rec_hourly_dress.xml @@ -0,0 +1,39 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/recommended_hourly_dress.xml b/app/src/main/res/layout/recommended_hourly_dress.xml new file mode 100644 index 00000000..6613e55d --- /dev/null +++ b/app/src/main/res/layout/recommended_hourly_dress.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + \ No newline at end of file