feat: current weather on home screen (MET/Yr API, no key)
The Yr app (no.nrk.yr) exposes no data to third parties; its backend, api.met.no locationforecast 2.0, works with no API key (User-Agent header only). New toggle 'Show Current Weather' under Display, right after 'Show App Icons'. The home element is formatted exactly like the daily word and shows a nerd-font weather glyph (nf-weather-*, from the installed JetBrainsMonoNerdFont) plus the temperature in Celsius, e.g. '\uE312 16°'. A 95KB subset of JetBrainsMonoNerdFont (ASCII + weather glyphs, OFL) is bundled as R.font.jetbrains_mono_nf_weather so the glyphs render even if the launcher font setting changes. Uses the launcher's existing saved location (lat/lon prefs).
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is available with a FAQ at: https://scripts.sil.org/OFL
|
||||||
@@ -33,14 +33,18 @@ import com.github.droidworksstudio.launcher.R
|
|||||||
import com.github.droidworksstudio.launcher.accessibility.ActionService
|
import com.github.droidworksstudio.launcher.accessibility.ActionService
|
||||||
import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO
|
import com.github.droidworksstudio.launcher.data.dao.AppInfoDAO
|
||||||
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
import com.github.droidworksstudio.launcher.data.entities.AppInfo
|
||||||
|
import com.github.droidworksstudio.launcher.helper.weather.MetForecastResponse
|
||||||
import com.github.droidworksstudio.launcher.helper.weather.WeatherResponse
|
import com.github.droidworksstudio.launcher.helper.weather.WeatherResponse
|
||||||
import com.github.droidworksstudio.launcher.utils.Constants
|
import com.github.droidworksstudio.launcher.utils.Constants
|
||||||
|
import com.github.droidworksstudio.launcher.utils.MetApiService
|
||||||
|
import com.github.droidworksstudio.launcher.utils.MetSymbolMapper
|
||||||
import com.github.droidworksstudio.launcher.utils.WeatherApiService
|
import com.github.droidworksstudio.launcher.utils.WeatherApiService
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlin.math.roundToInt
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
@@ -552,4 +556,89 @@ class AppHelper @Inject constructor() {
|
|||||||
|
|
||||||
// Data class to hold cached weather data along with timestamp
|
// Data class to hold cached weather data along with timestamp
|
||||||
data class CachedWeatherData(val timestamp: Long, val weatherResponse: WeatherResponse)
|
data class CachedWeatherData(val timestamp: Long, val weatherResponse: WeatherResponse)
|
||||||
|
|
||||||
|
sealed class MetWeatherResult {
|
||||||
|
data class Success(val temperature: Int, val symbolCode: String) : MetWeatherResult()
|
||||||
|
data class Failure(val errorMessage: String) : MetWeatherResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current temperature and symbol from the MET/Yr API
|
||||||
|
* (api.met.no locationforecast). No API key needed - only a
|
||||||
|
* descriptive User-Agent header. Result is cached for 15 minutes.
|
||||||
|
*/
|
||||||
|
fun fetchMetWeather(
|
||||||
|
context: Context,
|
||||||
|
latitude: Float,
|
||||||
|
longitude: Float,
|
||||||
|
): MetWeatherResult {
|
||||||
|
if (latitude == 0f && longitude == 0f) {
|
||||||
|
return MetWeatherResult.Failure("No location available")
|
||||||
|
}
|
||||||
|
|
||||||
|
val cached = context.getMetWeatherFromCache()
|
||||||
|
if (cached?.let {
|
||||||
|
System.currentTimeMillis() - it.first < TimeUnit.MINUTES.toMillis(15)
|
||||||
|
} == true
|
||||||
|
) {
|
||||||
|
return cached.second
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl("https://api.met.no/weatherapi/locationforecast/2.0/")
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val service = retrofit.create(MetApiService::class.java)
|
||||||
|
val response = service.getCompact(latitude.toDouble(), longitude.toDouble()).execute()
|
||||||
|
if (response.isSuccessful) {
|
||||||
|
val body = response.body()
|
||||||
|
val timeseries = body?.properties?.timeseries
|
||||||
|
val first = timeseries?.firstOrNull()
|
||||||
|
if (first != null) {
|
||||||
|
val temperature = first.data.instant.details.airTemperature.roundToInt()
|
||||||
|
val symbolCode = first.data.next1Hours?.summary?.symbolCode
|
||||||
|
?: first.data.next6Hours?.summary?.symbolCode
|
||||||
|
?: "cloudy"
|
||||||
|
val result = MetWeatherResult.Success(temperature, symbolCode)
|
||||||
|
context.cacheMetWeather(result)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
return MetWeatherResult.Failure("Empty forecast")
|
||||||
|
} else {
|
||||||
|
return MetWeatherResult.Failure("MET API error: ${response.code()}")
|
||||||
|
}
|
||||||
|
} catch (e: UnknownHostException) {
|
||||||
|
return MetWeatherResult.Failure("Unknown host: api.met.no")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return MetWeatherResult.Failure(e.message ?: "MET fetch failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the home-screen weather text: nerd-font weather glyph followed
|
||||||
|
* by the temperature in Celsius, e.g. "\uE312 16°".
|
||||||
|
*/
|
||||||
|
fun buildCurrentWeatherText(temperature: Int, symbolCode: String): String {
|
||||||
|
return "${MetSymbolMapper.glyphFor(symbolCode)} $temperature°"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Context.cacheMetWeather(result: MetWeatherResult.Success) {
|
||||||
|
val sharedPreferences = getSharedPreferences(Constants.MET_WEATHER_PREFS, Context.MODE_PRIVATE)
|
||||||
|
sharedPreferences.edit()
|
||||||
|
.putLong(Constants.MET_WEATHER_TIMESTAMP, System.currentTimeMillis())
|
||||||
|
.putInt(Constants.MET_WEATHER_TEMPERATURE, result.temperature)
|
||||||
|
.putString(Constants.MET_WEATHER_SYMBOL, result.symbolCode)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Context.getMetWeatherFromCache(): Pair<Long, MetWeatherResult>? {
|
||||||
|
val sharedPreferences = getSharedPreferences(Constants.MET_WEATHER_PREFS, Context.MODE_PRIVATE)
|
||||||
|
val timestamp = sharedPreferences.getLong(Constants.MET_WEATHER_TIMESTAMP, -1L)
|
||||||
|
if (timestamp == -1L) return null
|
||||||
|
val temperature = sharedPreferences.getInt(Constants.MET_WEATHER_TEMPERATURE, 0)
|
||||||
|
val symbol = sharedPreferences.getString(Constants.MET_WEATHER_SYMBOL, "cloudy") ?: "cloudy"
|
||||||
|
return timestamp to MetWeatherResult.Success(temperature, symbol)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,6 +202,10 @@ class PreferenceHelper @Inject constructor(@ApplicationContext context: Context)
|
|||||||
get() = prefs.getBoolean(Constants.SHOW_NOTIFICATION_DOTS, false)
|
get() = prefs.getBoolean(Constants.SHOW_NOTIFICATION_DOTS, false)
|
||||||
set(value) = prefs.edit().putBoolean(Constants.SHOW_NOTIFICATION_DOTS, value).apply()
|
set(value) = prefs.edit().putBoolean(Constants.SHOW_NOTIFICATION_DOTS, value).apply()
|
||||||
|
|
||||||
|
var showCurrentWeather: Boolean
|
||||||
|
get() = prefs.getBoolean(Constants.SHOW_CURRENT_WEATHER, false)
|
||||||
|
set(value) = prefs.edit().putBoolean(Constants.SHOW_CURRENT_WEATHER, value).apply()
|
||||||
|
|
||||||
var searchEngines: Constants.SearchEngines
|
var searchEngines: Constants.SearchEngines
|
||||||
get() {
|
get() {
|
||||||
return try {
|
return try {
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.github.droidworksstudio.launcher.helper.weather
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locationforecast 2.0 compact response (api.met.no) - only the fields the
|
||||||
|
* home-screen current-weather element needs.
|
||||||
|
*/
|
||||||
|
data class MetForecastResponse(
|
||||||
|
val properties: MetProperties
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetProperties(
|
||||||
|
val timeseries: List<MetTimeSeries>
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetTimeSeries(
|
||||||
|
val time: String,
|
||||||
|
val data: MetData
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetData(
|
||||||
|
val instant: MetInstant,
|
||||||
|
@SerializedName("next_1_hours")
|
||||||
|
val next1Hours: MetNextHours? = null,
|
||||||
|
@SerializedName("next_6_hours")
|
||||||
|
val next6Hours: MetNextHours? = null
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetInstant(
|
||||||
|
val details: MetDetails
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetDetails(
|
||||||
|
@SerializedName("air_temperature")
|
||||||
|
val airTemperature: Double = 0.0
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetNextHours(
|
||||||
|
val summary: MetSummary
|
||||||
|
)
|
||||||
|
|
||||||
|
data class MetSummary(
|
||||||
|
@SerializedName("symbol_code")
|
||||||
|
val symbolCode: String = ""
|
||||||
|
)
|
||||||
@@ -22,6 +22,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.appcompat.widget.AppCompatTextView
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
import androidx.biometric.BiometricPrompt
|
import androidx.biometric.BiometricPrompt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@@ -57,6 +58,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
@@ -114,6 +116,12 @@ class HomeFragment : Fragment(),
|
|||||||
setupRecyclerView()
|
setupRecyclerView()
|
||||||
observeSwipeTouchListener()
|
observeSwipeTouchListener()
|
||||||
observeUserInterfaceSettings()
|
observeUserInterfaceSettings()
|
||||||
|
|
||||||
|
// Nerd-font weather glyphs (nf-weather-*) live in the bundled
|
||||||
|
// subset font; the system font here is JetBrainsMonoNerdFont but
|
||||||
|
// this guarantees rendering even if the launcher font is changed.
|
||||||
|
binding.currentWeather.typeface =
|
||||||
|
ResourcesCompat.getFont(requireContext(), R.font.jetbrains_mono_nf_weather)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
@@ -323,6 +331,17 @@ class HomeFragment : Fragment(),
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preferenceViewModel.showCurrentWeatherLiveData.observe(viewLifecycleOwner) {
|
||||||
|
appHelper.updateUI(
|
||||||
|
binding.currentWeather,
|
||||||
|
preferenceHelper.homeDailyWordAlignment,
|
||||||
|
preferenceHelper.dailyWordColor,
|
||||||
|
preferenceHelper.dailyWordTextSize,
|
||||||
|
it
|
||||||
|
)
|
||||||
|
if (it) loadCurrentWeather()
|
||||||
|
}
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
mainView.hideKeyboard()
|
mainView.hideKeyboard()
|
||||||
|
|
||||||
@@ -343,6 +362,41 @@ class HomeFragment : Fragment(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the current temperature from the MET/Yr API and shows it as a
|
||||||
|
* nerd-font weather glyph plus Celsius value, e.g. "\uE312 16°".
|
||||||
|
* Formatted like the daily word (same alignment/color/size settings).
|
||||||
|
*/
|
||||||
|
private fun loadCurrentWeather() {
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
|
||||||
|
val sharedPreferences =
|
||||||
|
requireContext().getSharedPreferences(Constants.WEATHER_PREFS, Context.MODE_PRIVATE)
|
||||||
|
val latitude = sharedPreferences.getFloat(Constants.LATITUDE, 0f)
|
||||||
|
val longitude = sharedPreferences.getFloat(Constants.LONGITUDE, 0f)
|
||||||
|
val result = appHelper.fetchMetWeather(requireContext(), latitude, longitude)
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
when (result) {
|
||||||
|
is AppHelper.MetWeatherResult.Success -> {
|
||||||
|
binding.currentWeather.text = appHelper.buildCurrentWeatherText(
|
||||||
|
result.temperature,
|
||||||
|
result.symbolCode
|
||||||
|
)
|
||||||
|
binding.currentWeather.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
is AppHelper.MetWeatherResult.Failure -> {
|
||||||
|
// No data (offline, no location): keep any cached text,
|
||||||
|
// otherwise hide the element.
|
||||||
|
if (binding.currentWeather.text.isNullOrEmpty()) {
|
||||||
|
binding.currentWeather.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun observeBioAuthCheck(appInfo: AppInfo) {
|
private fun observeBioAuthCheck(appInfo: AppInfo) {
|
||||||
if (!appInfo.lock)
|
if (!appInfo.lock)
|
||||||
context.launchApp(appInfo)
|
context.launchApp(appInfo)
|
||||||
@@ -607,6 +661,7 @@ class HomeFragment : Fragment(),
|
|||||||
observeUserInterfaceSettings()
|
observeUserInterfaceSettings()
|
||||||
observeFavoriteAppList()
|
observeFavoriteAppList()
|
||||||
observeNotificationBadges()
|
observeNotificationBadges()
|
||||||
|
if (preferenceHelper.showCurrentWeather) loadCurrentWeather()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAppClicked(appInfo: AppInfo) {
|
override fun onAppClicked(appInfo: AppInfo) {
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ class SettingsLookFeelFragment : Fragment(),
|
|||||||
alarmClockSwitchCompat.isChecked = preferenceHelper.showAlarmClock
|
alarmClockSwitchCompat.isChecked = preferenceHelper.showAlarmClock
|
||||||
dailyWordSwitchCompat.isChecked = preferenceHelper.showDailyWord
|
dailyWordSwitchCompat.isChecked = preferenceHelper.showDailyWord
|
||||||
appIconsSwitchCompat.isChecked = preferenceHelper.showAppIcon
|
appIconsSwitchCompat.isChecked = preferenceHelper.showAppIcon
|
||||||
|
currentWeatherSwitchCompat.isChecked = preferenceHelper.showCurrentWeather
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,6 +189,12 @@ class SettingsLookFeelFragment : Fragment(),
|
|||||||
val feedbackType = if (isChecked) "on" else "off"
|
val feedbackType = if (isChecked) "on" else "off"
|
||||||
appHelper.triggerHapticFeedback(context, feedbackType)
|
appHelper.triggerHapticFeedback(context, feedbackType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentWeatherSwitchCompat.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
preferenceViewModel.setShowCurrentWeather(isChecked)
|
||||||
|
val feedbackType = if (isChecked) "on" else "off"
|
||||||
|
appHelper.triggerHapticFeedback(context, feedbackType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ object Constants {
|
|||||||
const val WIDGET_BATTERY = "WIDGET_BATTERY"
|
const val WIDGET_BATTERY = "WIDGET_BATTERY"
|
||||||
|
|
||||||
const val WEATHER_PREFS = "EasyWeather.pref"
|
const val WEATHER_PREFS = "EasyWeather.pref"
|
||||||
|
const val MET_WEATHER_PREFS = "met_weather_prefs"
|
||||||
|
const val MET_WEATHER_TIMESTAMP = "MET_WEATHER_TIMESTAMP"
|
||||||
|
const val MET_WEATHER_TEMPERATURE = "MET_WEATHER_TEMPERATURE"
|
||||||
|
const val MET_WEATHER_SYMBOL = "MET_WEATHER_SYMBOL"
|
||||||
const val WEATHER_RESPONSE = "WEATHER_RESPONSE"
|
const val WEATHER_RESPONSE = "WEATHER_RESPONSE"
|
||||||
const val WEATHER_UNITS = "WEATHER_UNITS"
|
const val WEATHER_UNITS = "WEATHER_UNITS"
|
||||||
const val LATITUDE = "LATITUDE"
|
const val LATITUDE = "LATITUDE"
|
||||||
@@ -71,6 +75,7 @@ object Constants {
|
|||||||
const val TOGGLE_SETTING_LOCK = "TOGGLE_SETTING_LOCK"
|
const val TOGGLE_SETTING_LOCK = "TOGGLE_SETTING_LOCK"
|
||||||
const val DISABLE_ANIMATIONS = "DISABLE_ANIMATIONS"
|
const val DISABLE_ANIMATIONS = "DISABLE_ANIMATIONS"
|
||||||
const val SHOW_NOTIFICATION_DOTS = "SHOW_NOTIFICATION_DOTS"
|
const val SHOW_NOTIFICATION_DOTS = "SHOW_NOTIFICATION_DOTS"
|
||||||
|
const val SHOW_CURRENT_WEATHER = "SHOW_CURRENT_WEATHER"
|
||||||
|
|
||||||
const val HOME_DATE_ALIGNMENT = "HOME_DATE_ALIGNMENT"
|
const val HOME_DATE_ALIGNMENT = "HOME_DATE_ALIGNMENT"
|
||||||
const val HOME_TIME_ALIGNMENT = "HOME_TIME_ALIGNMENT"
|
const val HOME_TIME_ALIGNMENT = "HOME_TIME_ALIGNMENT"
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.github.droidworksstudio.launcher.utils
|
||||||
|
|
||||||
|
import com.github.droidworksstudio.launcher.helper.weather.MetForecastResponse
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Headers
|
||||||
|
import retrofit2.http.Query
|
||||||
|
|
||||||
|
interface MetApiService {
|
||||||
|
|
||||||
|
@Headers(
|
||||||
|
"User-Agent: app.easy.launcher (https://gitea.haugesenspil.dk/jonas/EasyLauncher)"
|
||||||
|
)
|
||||||
|
@GET("compact")
|
||||||
|
fun getCompact(
|
||||||
|
@Query("lat") latitude: Double,
|
||||||
|
@Query("lon") longitude: Double
|
||||||
|
): Call<MetForecastResponse>
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.github.droidworksstudio.launcher.utils
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maps MET/Yr symbol codes to Nerd Font weather glyphs
|
||||||
|
* (nf-weather-* codepoints from the Weather Icons font embedded in
|
||||||
|
* JetBrainsMonoNerdFont). Rendered with R.font.jetbrains_mono_nf_weather.
|
||||||
|
*/
|
||||||
|
object MetSymbolMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the nf-weather glyph char for a MET `symbol_code`
|
||||||
|
* (e.g. "partlycloudy_day"). Unknown codes fall back to a cloud.
|
||||||
|
*/
|
||||||
|
fun glyphFor(symbolCode: String): Char {
|
||||||
|
val normalized = symbolCode.removeSuffix("_polartwilight")
|
||||||
|
val night = normalized.endsWith("_night")
|
||||||
|
val base = normalized
|
||||||
|
.removeSuffix("_day")
|
||||||
|
.removeSuffix("_night")
|
||||||
|
|
||||||
|
return when (base) {
|
||||||
|
"clearsky", "fair" -> if (night) '\uE32B' else '\uE30D'
|
||||||
|
"partlycloudy" -> if (night) '\uE379' else '\uE302'
|
||||||
|
"cloudy" -> '\uE312'
|
||||||
|
"fog" -> '\uE313'
|
||||||
|
"lightrain" -> '\uE31B'
|
||||||
|
"rain" -> '\uE318'
|
||||||
|
"heavyrain" -> '\uE319'
|
||||||
|
"lightrainshowers" -> if (night) '\uE328' else '\uE30B'
|
||||||
|
"rainshowers" -> if (night) '\uE326' else '\uE309'
|
||||||
|
"heavyrainshowers" -> if (night) '\uE324' else '\uE307'
|
||||||
|
"lightrainandthunder" -> '\uE315'
|
||||||
|
"rainandthunder" -> '\uE31D'
|
||||||
|
"heavyrainandthunder" -> '\uE31C'
|
||||||
|
"lightrainshowersandthunder" -> if (night) '\uE322' else '\uE305'
|
||||||
|
"rainshowersandthunder" -> if (night) '\uE32A' else '\uE30F'
|
||||||
|
"heavyrainshowersandthunder" -> if (night) '\uE329' else '\uE30E'
|
||||||
|
"sleet" -> '\uE316'
|
||||||
|
"lightssleetshowers", "sleetshowers" -> if (night) '\uE323' else '\uE306'
|
||||||
|
"heavysleetshowers" -> if (night) '\uE364' else '\uE362'
|
||||||
|
"sleetandthunder",
|
||||||
|
"lightsleetshowersandthunder",
|
||||||
|
"sleetshowersandthunder",
|
||||||
|
-> if (night) '\uE364' else '\uE362'
|
||||||
|
"lightssnow", "lightsnow", "snow" -> '\uE31A'
|
||||||
|
"heavysnow" -> '\uE35E'
|
||||||
|
"lightssnowshowers", "snowshowers" -> if (night) '\uE327' else '\uE30A'
|
||||||
|
"heavysnowshowers" -> if (night) '\uE361' else '\uE35F'
|
||||||
|
"snowandthunder",
|
||||||
|
"snowshowersandthunder",
|
||||||
|
-> if (night) '\uE367' else '\uE365'
|
||||||
|
"thunder" -> '\uE31D'
|
||||||
|
"wind" -> '\uE34B'
|
||||||
|
else -> '\uE312'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -49,7 +49,8 @@ class PreferenceViewModel @Inject constructor(
|
|||||||
private val autoKeyboardLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
private val autoKeyboardLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
private val lockSettingsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
private val lockSettingsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
private val disableAnimationsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
private val disableAnimationsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
private val showNotificationDotsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
val showNotificationDotsLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
|
val showCurrentWeatherLiveData: MutableLiveData<Boolean> = MutableLiveData()
|
||||||
private val appGroupPaddingSizeLiveData: MutableLiveData<Float> = MutableLiveData()
|
private val appGroupPaddingSizeLiveData: MutableLiveData<Float> = MutableLiveData()
|
||||||
private val appPaddingSizeLiveData: MutableLiveData<Float> = MutableLiveData()
|
private val appPaddingSizeLiveData: MutableLiveData<Float> = MutableLiveData()
|
||||||
|
|
||||||
@@ -303,6 +304,11 @@ class PreferenceViewModel @Inject constructor(
|
|||||||
showNotificationDotsLiveData.postValue((preferenceHelper.showNotificationDots))
|
showNotificationDotsLiveData.postValue((preferenceHelper.showNotificationDots))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setShowCurrentWeather(showCurrentWeather: Boolean) {
|
||||||
|
preferenceHelper.showCurrentWeather = showCurrentWeather
|
||||||
|
showCurrentWeatherLiveData.postValue((preferenceHelper.showCurrentWeather))
|
||||||
|
}
|
||||||
|
|
||||||
fun setAppLanguage(appLanguage: Constants.Language) {
|
fun setAppLanguage(appLanguage: Constants.Language) {
|
||||||
preferenceHelper.appLanguage = appLanguage
|
preferenceHelper.appLanguage = appLanguage
|
||||||
appLanguageLiveData.postValue((preferenceHelper.appLanguage))
|
appLanguageLiveData.postValue((preferenceHelper.appLanguage))
|
||||||
|
|||||||
BIN
app/src/main/res/font/jetbrains_mono_nf_weather.ttf
Normal file
BIN
app/src/main/res/font/jetbrains_mono_nf_weather.ttf
Normal file
Binary file not shown.
@@ -78,6 +78,14 @@
|
|||||||
android:textSize="32sp"
|
android:textSize="32sp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/currentWeather"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:textSize="32sp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
|||||||
@@ -300,6 +300,37 @@
|
|||||||
tools:ignore="TouchTargetSizeCheck" />
|
tools:ignore="TouchTargetSizeCheck" />
|
||||||
|
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
tools:ignore="MissingConstraints">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatTextView
|
||||||
|
android:id="@+id/currentWeather_text"
|
||||||
|
style="@style/TextDefaultStyle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="left|center"
|
||||||
|
android:text="@string/settings_display_current_weather"
|
||||||
|
android:textSize="@dimen/text_large"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:ignore="RtlHardcoded" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
|
android:id="@+id/currentWeather_switchCompat"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:scaleX="0.7"
|
||||||
|
android:scaleY="0.8"
|
||||||
|
android:thumb="@drawable/shape_switch_thumb"
|
||||||
|
app:track="@drawable/selector_switch"
|
||||||
|
tools:ignore="TouchTargetSizeCheck" />
|
||||||
|
|
||||||
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
</androidx.appcompat.widget.LinearLayoutCompat>
|
</androidx.appcompat.widget.LinearLayoutCompat>
|
||||||
|
|
||||||
<androidx.appcompat.widget.LinearLayoutCompat
|
<androidx.appcompat.widget.LinearLayoutCompat
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
<string name="settings_display_alarm_clock">Show Alarm Clock</string>
|
<string name="settings_display_alarm_clock">Show Alarm Clock</string>
|
||||||
<string name="settings_display_daily_word">Show Daily Word</string>
|
<string name="settings_display_daily_word">Show Daily Word</string>
|
||||||
<string name="settings_display_app_icons">Show App Icons</string>
|
<string name="settings_display_app_icons">Show App Icons</string>
|
||||||
|
<string name="settings_display_current_weather">Show Current Weather</string>
|
||||||
<string name="settings_display_automatic_keyboard">Auto Show Keyboard</string>
|
<string name="settings_display_automatic_keyboard">Auto Show Keyboard</string>
|
||||||
<string name="settings_display_auto_open_apps">Auto Open Last App</string>
|
<string name="settings_display_auto_open_apps">Auto Open Last App</string>
|
||||||
<string name="settings_home_alignment_bottom">Home Alignment Bottom</string>
|
<string name="settings_home_alignment_bottom">Home Alignment Bottom</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user