Implement plugin weather provider
This commit is contained in:
parent
d80e4c66a4
commit
211f30170c
@ -1,5 +1,7 @@
|
|||||||
package de.mm20.launcher2.plugin.config
|
package de.mm20.launcher2.plugin.config
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
|
||||||
data class WeatherPluginConfig(
|
data class WeatherPluginConfig(
|
||||||
/**
|
/**
|
||||||
* Minimum time (in ms) that needs to pass before the provider can be queried again.
|
* Minimum time (in ms) that needs to pass before the provider can be queried again.
|
||||||
@ -7,4 +9,22 @@ data class WeatherPluginConfig(
|
|||||||
* or weather provider.
|
* or weather provider.
|
||||||
*/
|
*/
|
||||||
val minUpdateInterval: Long = 60 * 60 * 1000L,
|
val minUpdateInterval: Long = 60 * 60 * 1000L,
|
||||||
)
|
) {
|
||||||
|
|
||||||
|
fun toBundle(): Bundle {
|
||||||
|
return Bundle().apply {
|
||||||
|
putLong("minUpdateInterval", minUpdateInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
operator fun invoke(bundle: Bundle): WeatherPluginConfig {
|
||||||
|
return WeatherPluginConfig(
|
||||||
|
minUpdateInterval = bundle.getLong(
|
||||||
|
"minUpdateInterval",
|
||||||
|
60 * 60 * 1000L
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ package de.mm20.launcher2.plugin.contracts
|
|||||||
|
|
||||||
object WeatherPluginContract {
|
object WeatherPluginContract {
|
||||||
object Paths {
|
object Paths {
|
||||||
const val Weather = "weather"
|
const val Forecasts = "forecasts"
|
||||||
const val Locations = "locations"
|
const val Locations = "locations"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,13 +5,14 @@ import de.mm20.launcher2.weather.brightsky.BrightSkyProvider
|
|||||||
import de.mm20.launcher2.weather.here.HereProvider
|
import de.mm20.launcher2.weather.here.HereProvider
|
||||||
import de.mm20.launcher2.weather.metno.MetNoProvider
|
import de.mm20.launcher2.weather.metno.MetNoProvider
|
||||||
import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider
|
import de.mm20.launcher2.weather.openweathermap.OpenWeatherMapProvider
|
||||||
|
import de.mm20.launcher2.weather.plugin.PluginWeatherProvider
|
||||||
import de.mm20.launcher2.weather.settings.WeatherSettings
|
import de.mm20.launcher2.weather.settings.WeatherSettings
|
||||||
import org.koin.android.ext.koin.androidContext
|
import org.koin.android.ext.koin.androidContext
|
||||||
import org.koin.core.qualifier.named
|
import org.koin.core.qualifier.named
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
val weatherModule = module {
|
val weatherModule = module {
|
||||||
single<WeatherRepository> { WeatherRepositoryImpl(androidContext(), get(), get()) }
|
single<WeatherRepository> { WeatherRepositoryImpl(androidContext(), get(), get(), get()) }
|
||||||
single<WeatherSettings> { WeatherSettings(androidContext()) }
|
single<WeatherSettings> { WeatherSettings(androidContext()) }
|
||||||
factory<Backupable>(named<WeatherSettings>()) { get<WeatherSettings>() }
|
factory<Backupable>(named<WeatherSettings>()) { get<WeatherSettings>() }
|
||||||
factory<WeatherProvider> { (providerId: String) ->
|
factory<WeatherProvider> { (providerId: String) ->
|
||||||
@ -20,7 +21,7 @@ val weatherModule = module {
|
|||||||
MetNoProvider.Id -> MetNoProvider(androidContext(), get())
|
MetNoProvider.Id -> MetNoProvider(androidContext(), get())
|
||||||
HereProvider.Id -> HereProvider(androidContext())
|
HereProvider.Id -> HereProvider(androidContext())
|
||||||
BrightSkyProvider.Id -> BrightSkyProvider(androidContext())
|
BrightSkyProvider.Id -> BrightSkyProvider(androidContext())
|
||||||
else -> TODO("Implement plugin provider")
|
else -> PluginWeatherProvider(androidContext(), providerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,8 +6,9 @@ import org.koin.core.parameter.parametersOf
|
|||||||
|
|
||||||
interface WeatherProvider {
|
interface WeatherProvider {
|
||||||
|
|
||||||
val updateInterval: Long
|
suspend fun getUpdateInterval(): Long {
|
||||||
get() = 1000 * 60 * 60L
|
return 1000 * 60 * 60L
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getWeatherData(location: WeatherLocation): List<Forecast>?
|
suspend fun getWeatherData(location: WeatherLocation): List<Forecast>?
|
||||||
suspend fun getWeatherData(lat: Double, lon: Double): List<Forecast>?
|
suspend fun getWeatherData(lat: Double, lon: Double): List<Forecast>?
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import de.mm20.launcher2.database.AppDatabase
|
|||||||
import de.mm20.launcher2.ktx.checkPermission
|
import de.mm20.launcher2.ktx.checkPermission
|
||||||
import de.mm20.launcher2.permissions.PermissionGroup
|
import de.mm20.launcher2.permissions.PermissionGroup
|
||||||
import de.mm20.launcher2.permissions.PermissionsManager
|
import de.mm20.launcher2.permissions.PermissionsManager
|
||||||
|
import de.mm20.launcher2.plugin.PluginRepository
|
||||||
|
import de.mm20.launcher2.plugin.PluginType
|
||||||
import de.mm20.launcher2.weather.brightsky.BrightSkyProvider
|
import de.mm20.launcher2.weather.brightsky.BrightSkyProvider
|
||||||
import de.mm20.launcher2.weather.here.HereProvider
|
import de.mm20.launcher2.weather.here.HereProvider
|
||||||
import de.mm20.launcher2.weather.metno.MetNoProvider
|
import de.mm20.launcher2.weather.metno.MetNoProvider
|
||||||
@ -39,6 +41,7 @@ internal class WeatherRepositoryImpl(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val database: AppDatabase,
|
private val database: AppDatabase,
|
||||||
private val settings: WeatherSettings,
|
private val settings: WeatherSettings,
|
||||||
|
private val pluginRepository: PluginRepository,
|
||||||
) : WeatherRepository, KoinComponent {
|
) : WeatherRepository, KoinComponent {
|
||||||
|
|
||||||
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
private val scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||||
@ -158,7 +161,12 @@ internal class WeatherRepositoryImpl(
|
|||||||
if (HereProvider.isAvailable(context)) {
|
if (HereProvider.isAvailable(context)) {
|
||||||
providers.add(WeatherProviderInfo(HereProvider.Id, context.getString(R.string.provider_here)))
|
providers.add(WeatherProviderInfo(HereProvider.Id, context.getString(R.string.provider_here)))
|
||||||
}
|
}
|
||||||
return flowOf(providers)
|
val pluginProviders = pluginRepository.findMany(type = PluginType.Weather, enabled = true)
|
||||||
|
return pluginProviders.map {
|
||||||
|
providers + it.map {
|
||||||
|
WeatherProviderInfo(it.authority, it.label)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +181,7 @@ class WeatherUpdateWorker(val context: Context, params: WorkerParameters) :
|
|||||||
val settingsData = settings.data.first()
|
val settingsData = settings.data.first()
|
||||||
val provider = WeatherProvider.getInstance(settingsData.provider)
|
val provider = WeatherProvider.getInstance(settingsData.provider)
|
||||||
|
|
||||||
val updateInterval = provider.updateInterval
|
val updateInterval = provider.getUpdateInterval()
|
||||||
val lastUpdate = settingsData.lastUpdate
|
val lastUpdate = settingsData.lastUpdate
|
||||||
|
|
||||||
if (lastUpdate + updateInterval > System.currentTimeMillis()) {
|
if (lastUpdate + updateInterval > System.currentTimeMillis()) {
|
||||||
|
|||||||
@ -0,0 +1,327 @@
|
|||||||
|
package de.mm20.launcher2.weather.plugin
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.CancellationSignal
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.database.getDoubleOrNull
|
||||||
|
import androidx.core.database.getIntOrNull
|
||||||
|
import androidx.core.database.getLongOrNull
|
||||||
|
import androidx.core.database.getStringOrNull
|
||||||
|
import de.mm20.launcher2.crashreporter.CrashReporter
|
||||||
|
import de.mm20.launcher2.plugin.config.WeatherPluginConfig
|
||||||
|
import de.mm20.launcher2.plugin.contracts.PluginContract
|
||||||
|
import de.mm20.launcher2.plugin.contracts.WeatherPluginContract
|
||||||
|
import de.mm20.launcher2.weather.Forecast
|
||||||
|
import de.mm20.launcher2.weather.WeatherLocation
|
||||||
|
import de.mm20.launcher2.weather.WeatherProvider
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.util.Locale
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
|
||||||
|
internal class PluginWeatherProvider(
|
||||||
|
private val context: Context,
|
||||||
|
private val pluginAuthority: String,
|
||||||
|
) : WeatherProvider {
|
||||||
|
override suspend fun getWeatherData(location: WeatherLocation): List<Forecast>? {
|
||||||
|
val uri = Uri.Builder()
|
||||||
|
.scheme("content")
|
||||||
|
.authority(pluginAuthority)
|
||||||
|
.path(WeatherPluginContract.Paths.Forecasts).apply {
|
||||||
|
if (location is WeatherLocation.LatLon) {
|
||||||
|
appendQueryParameter(
|
||||||
|
WeatherPluginContract.ForecastParams.Lat,
|
||||||
|
location.lat.toString()
|
||||||
|
)
|
||||||
|
appendQueryParameter(
|
||||||
|
WeatherPluginContract.ForecastParams.Lon,
|
||||||
|
location.lon.toString()
|
||||||
|
)
|
||||||
|
} else if (location is WeatherLocation.Id) {
|
||||||
|
appendQueryParameter(
|
||||||
|
WeatherPluginContract.ForecastParams.Id,
|
||||||
|
location.locationId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.appendQueryParameter(WeatherPluginContract.ForecastParams.LocationName, location.name)
|
||||||
|
.appendQueryParameter(WeatherPluginContract.ForecastParams.Language, getLang())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return getWeatherData(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getWeatherData(lat: Double, lon: Double): List<Forecast>? {
|
||||||
|
val uri = Uri.Builder()
|
||||||
|
.scheme("content")
|
||||||
|
.authority(pluginAuthority)
|
||||||
|
.appendQueryParameter(WeatherPluginContract.ForecastParams.Lat, lat.toString())
|
||||||
|
.appendQueryParameter(WeatherPluginContract.ForecastParams.Lon, lon.toString())
|
||||||
|
.appendQueryParameter(WeatherPluginContract.ForecastParams.Language, getLang())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
return getWeatherData(uri)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun getWeatherData(uri: Uri): List<Forecast>? = withContext(Dispatchers.IO) {
|
||||||
|
val cancellationSignal = CancellationSignal()
|
||||||
|
return@withContext suspendCancellableCoroutine {
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
cancellationSignal.cancel()
|
||||||
|
}
|
||||||
|
val cursor = try {
|
||||||
|
context.contentResolver.query(
|
||||||
|
uri,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationSignal
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MM20", "Plugin $pluginAuthority threw exception")
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
it.resume(emptyList())
|
||||||
|
return@suspendCancellableCoroutine
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.e("MM20", "Plugin $pluginAuthority returned null cursor")
|
||||||
|
it.resume(emptyList())
|
||||||
|
return@suspendCancellableCoroutine
|
||||||
|
}
|
||||||
|
|
||||||
|
val results = forecastsFromCursor(cursor) ?: emptyList()
|
||||||
|
it.resume(results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun forecastsFromCursor(cursor: Cursor): List<Forecast>? {
|
||||||
|
return cursor.use {
|
||||||
|
val results = mutableListOf<Forecast>()
|
||||||
|
|
||||||
|
val timestampIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Timestamp)
|
||||||
|
.takeIf { it >= 0 } ?: return null
|
||||||
|
val createdAtIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.CreatedAt)
|
||||||
|
.takeIf { it >= 0 } ?: return null
|
||||||
|
val temperatureIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Temperature)
|
||||||
|
.takeIf { it >= 0 } ?: return null
|
||||||
|
val conditionIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Condition)
|
||||||
|
.takeIf { it >= 0 } ?: return null
|
||||||
|
val iconIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Icon).takeIf { it >= 0 }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
val locationIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Location)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
val providerIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Provider)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
val providerUrlIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.ProviderUrl)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val precipitationIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Precipitation)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val precipProbabilityIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.RainProbability)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val cloudsIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Clouds)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val humidityIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Humidity)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val windSpeedIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.WindSpeed)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val windDirectionIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.WindDirection)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val pressureIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Pressure)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val nightIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.Night)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val minTempIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.TemperatureMin)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
val maxTempIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.ForecastColumns.TemperatureMax)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
results += Forecast(
|
||||||
|
timestamp = cursor.getLongOrNull(timestampIndex) ?: continue,
|
||||||
|
temperature = cursor.getDoubleOrNull(temperatureIndex) ?: continue,
|
||||||
|
updateTime = cursor.getLongOrNull(createdAtIndex) ?: continue,
|
||||||
|
condition = cursor.getStringOrNull(conditionIndex) ?: continue,
|
||||||
|
icon = getIcon(cursor.getStringOrNull(iconIndex) ?: continue),
|
||||||
|
location = cursor.getStringOrNull(locationIndex) ?: continue,
|
||||||
|
provider = cursor.getStringOrNull(providerIndex) ?: continue,
|
||||||
|
providerUrl = providerUrlIndex?.let { cursor.getStringOrNull(it) } ?: "",
|
||||||
|
clouds = cloudsIndex?.let { cursor.getIntOrNull(it) },
|
||||||
|
humidity = humidityIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
precipitation = precipitationIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
precipProbability = precipProbabilityIndex?.let { cursor.getIntOrNull(it) },
|
||||||
|
windSpeed = windSpeedIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
windDirection = windDirectionIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
pressure = pressureIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
night = nightIndex?.let { cursor.getIntOrNull(it) } == 1,
|
||||||
|
minTemp = minTempIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
maxTemp = maxTempIndex?.let { cursor.getDoubleOrNull(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getIcon(icon: String): Int {
|
||||||
|
return when (icon) {
|
||||||
|
"Clear" -> Forecast.CLEAR
|
||||||
|
"Cloudy" -> Forecast.CLOUDY
|
||||||
|
"Cold" -> Forecast.COLD
|
||||||
|
"Drizzle" -> Forecast.DRIZZLE
|
||||||
|
"Haze" -> Forecast.HAZE
|
||||||
|
"Fog" -> Forecast.FOG
|
||||||
|
"Hail" -> Forecast.HAIL
|
||||||
|
"HeavyThunderstorm" -> Forecast.HEAVY_THUNDERSTORM
|
||||||
|
"HeavyThunderstormWithRain" -> Forecast.HEAVY_THUNDERSTORM_WITH_RAIN
|
||||||
|
"Hot" -> Forecast.HOT
|
||||||
|
"MostlyCloudy" -> Forecast.MOSTLY_CLOUDY
|
||||||
|
"PartlyCloudy" -> Forecast.PARTLY_CLOUDY
|
||||||
|
"Showers" -> Forecast.SHOWERS
|
||||||
|
"Sleet" -> Forecast.SLEET
|
||||||
|
"Snow" -> Forecast.SNOW
|
||||||
|
"Storm" -> Forecast.STORM
|
||||||
|
"Thunderstorm" -> Forecast.THUNDERSTORM
|
||||||
|
"ThunderstormWithRain" -> Forecast.THUNDERSTORM_WITH_RAIN
|
||||||
|
"Wind" -> Forecast.WIND
|
||||||
|
"BrokenClouds" -> Forecast.BROKEN_CLOUDS
|
||||||
|
else -> Forecast.NONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLang(): String {
|
||||||
|
return Locale.getDefault().language
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun findLocation(query: String): List<WeatherLocation> = withContext(Dispatchers.IO) {
|
||||||
|
val cancellationSignal = CancellationSignal()
|
||||||
|
return@withContext suspendCancellableCoroutine {
|
||||||
|
it.invokeOnCancellation {
|
||||||
|
cancellationSignal.cancel()
|
||||||
|
}
|
||||||
|
val uri = Uri.Builder()
|
||||||
|
.scheme("content")
|
||||||
|
.authority(pluginAuthority)
|
||||||
|
.path(WeatherPluginContract.Paths.Locations)
|
||||||
|
.appendQueryParameter(WeatherPluginContract.LocationParams.Query, query)
|
||||||
|
.appendQueryParameter(WeatherPluginContract.LocationParams.Language, getLang())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val cursor = try {
|
||||||
|
context.contentResolver.query(
|
||||||
|
uri,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
cancellationSignal
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("MM20", "Plugin $pluginAuthority threw exception")
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
it.resume(emptyList())
|
||||||
|
return@suspendCancellableCoroutine
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.e("MM20", "Plugin $pluginAuthority returned null cursor")
|
||||||
|
it.resume(emptyList())
|
||||||
|
return@suspendCancellableCoroutine
|
||||||
|
}
|
||||||
|
|
||||||
|
val results = locationsFromCursor(cursor) ?: emptyList()
|
||||||
|
it.resume(results)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun locationsFromCursor(cursor: Cursor): List<WeatherLocation> {
|
||||||
|
return cursor.use {
|
||||||
|
val results = mutableListOf<WeatherLocation>()
|
||||||
|
|
||||||
|
val nameIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.LocationColumns.Name)
|
||||||
|
.takeIf { it >= 0 } ?: return emptyList()
|
||||||
|
val latIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.LocationColumns.Lat)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
val lonIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.LocationColumns.Lon)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
val locationIdIndex =
|
||||||
|
cursor.getColumnIndex(WeatherPluginContract.LocationColumns.Id)
|
||||||
|
.takeIf { it >= 0 }
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
val lat = latIndex?.let { cursor.getDoubleOrNull(it) }
|
||||||
|
val lon = lonIndex?.let { cursor.getDoubleOrNull(it) }
|
||||||
|
val locationId = locationIdIndex?.let { cursor.getStringOrNull(it) }
|
||||||
|
val name = cursor.getStringOrNull(nameIndex) ?: continue
|
||||||
|
|
||||||
|
if (lat != null && lon != null) {
|
||||||
|
results += WeatherLocation.LatLon(lat = lat, lon = lon, name = name)
|
||||||
|
} else if (locationId != null) {
|
||||||
|
results += WeatherLocation.Id(locationId, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getUpdateInterval(): Long {
|
||||||
|
return getPluginConfig()?.minUpdateInterval ?: super.getUpdateInterval()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPluginConfig(): WeatherPluginConfig? {
|
||||||
|
val configBundle = try {
|
||||||
|
context.contentResolver.call(
|
||||||
|
Uri.Builder()
|
||||||
|
.scheme("content")
|
||||||
|
.authority(pluginAuthority)
|
||||||
|
.build(),
|
||||||
|
PluginContract.Methods.GetConfig,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
) ?: return null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("PluginWeatherProvider", "Plugin $pluginAuthority threw exception", e)
|
||||||
|
CrashReporter.logException(e)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return WeatherPluginConfig(configBundle)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -53,7 +53,7 @@ abstract class WeatherProvider(
|
|||||||
checkPermissionOrThrow(context)
|
checkPermissionOrThrow(context)
|
||||||
|
|
||||||
when {
|
when {
|
||||||
uri.pathSegments.size == 1 && uri.pathSegments.first() == WeatherPluginContract.Paths.Weather -> {
|
uri.pathSegments.size == 1 && uri.pathSegments.first() == WeatherPluginContract.Paths.Forecasts -> {
|
||||||
val lat = uri.getQueryParameter(WeatherPluginContract.ForecastParams.Lat)
|
val lat = uri.getQueryParameter(WeatherPluginContract.ForecastParams.Lat)
|
||||||
?.toDoubleOrNull()
|
?.toDoubleOrNull()
|
||||||
val lon = uri.getQueryParameter(WeatherPluginContract.ForecastParams.Lon)
|
val lon = uri.getQueryParameter(WeatherPluginContract.ForecastParams.Lon)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user