Skip to content

Commit 45eab5c

Browse files
fix
1 parent 38cd584 commit 45eab5c

1 file changed

Lines changed: 73 additions & 149 deletions

File tree

app/src/main/java/com/neko/marquee/text/Greetings.kt

Lines changed: 73 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,18 @@ import android.Manifest
44
import android.annotation.SuppressLint
55
import android.content.*
66
import android.content.pm.PackageManager
7-
import android.location.Geocoder
8-
import android.location.Location
97
import android.os.*
108
import android.util.AttributeSet
119
import androidx.annotation.StringRes
1210
import androidx.appcompat.widget.AppCompatTextView
1311
import androidx.core.content.ContextCompat
14-
import androidx.core.os.ConfigurationCompat
1512
import com.google.android.gms.location.*
16-
import com.google.android.gms.tasks.CancellationTokenSource
1713
import io.nekohasekai.sagernet.R
1814
import io.nekohasekai.sagernet.database.DataStore
1915
import kotlinx.coroutines.*
2016
import org.json.JSONObject
2117
import java.net.URL
18+
import java.net.URLEncoder
2219
import java.util.*
2320
import kotlin.math.roundToInt
2421

@@ -31,116 +28,66 @@ class Greetings @JvmOverloads constructor(
3128
private val fusedClient by lazy { LocationServices.getFusedLocationProviderClient(context) }
3229
private val handler = Handler(Looper.getMainLooper())
3330
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
34-
35-
private val prefs by lazy { context.getSharedPreferences("neko_weather_cache", Context.MODE_PRIVATE) }
3631

3732
private var showWeather = false
38-
private var useManualCity = false
39-
4033
private var lastWeatherTime = 0L
41-
private var lastLat = 0.0
42-
private var lastLon = 0.0
43-
private var cachedTemp = 0
44-
private var cachedCode = -1
45-
private var cachedLocName = ""
34+
private var lastWeatherText: String? = null
35+
private var useManualCity = false
4636

4737
private val weatherInterval = 30 * 60 * 1000L
48-
private val minDistanceChange = 2000f
38+
private val locationRequest = LocationRequest.Builder(
39+
Priority.PRIORITY_BALANCED_POWER_ACCURACY, 10_000L
40+
).setMinUpdateIntervalMillis(60_000L).build()
4941

5042
private val timeReceiver = object : BroadcastReceiver() {
5143
override fun onReceive(context: Context?, intent: Intent?) {
5244
if (intent?.action in listOf(
53-
Intent.ACTION_TIME_TICK,
54-
Intent.ACTION_TIME_CHANGED,
55-
Intent.ACTION_TIMEZONE_CHANGED
45+
Intent.ACTION_TIME_TICK, Intent.ACTION_TIME_CHANGED, Intent.ACTION_TIMEZONE_CHANGED
5646
)
57-
) {
58-
refreshGreetingText()
59-
}
47+
) updateGreeting(lastWeatherText ?: "")
6048
}
6149
}
6250

6351
init {
6452
showWeather = DataStore.showWeatherInfo
6553
useManualCity = DataStore.manualWeatherEnabled
66-
67-
loadCache()
68-
refreshGreetingText()
54+
updateGreeting()
6955
}
7056

7157
override fun onAttachedToWindow() {
7258
super.onAttachedToWindow()
73-
val filter = IntentFilter().apply {
59+
context.registerReceiver(timeReceiver, IntentFilter().apply {
7460
addAction(Intent.ACTION_TIME_TICK)
7561
addAction(Intent.ACTION_TIME_CHANGED)
7662
addAction(Intent.ACTION_TIMEZONE_CHANGED)
77-
}
78-
context.registerReceiver(timeReceiver, filter)
63+
})
7964

80-
checkSettingsAndFetch()
65+
showWeather = DataStore.showWeatherInfo
66+
useManualCity = DataStore.manualWeatherEnabled
67+
68+
if (showWeather) fetchWeather()
69+
scheduleWeatherRefresh()
8170
}
8271

8372
override fun onDetachedFromWindow() {
8473
super.onDetachedFromWindow()
85-
try {
86-
context.unregisterReceiver(timeReceiver)
87-
} catch (e: Exception) {
88-
}
74+
context.unregisterReceiver(timeReceiver)
8975
handler.removeCallbacksAndMessages(null)
9076
coroutineScope.cancel()
9177
}
9278

9379
override fun isFocused(): Boolean = true
9480

95-
private fun getAppLocale(): Locale {
96-
return ConfigurationCompat.getLocales(context.resources.configuration)[0]
97-
?: Locale.getDefault()
98-
}
99-
100-
private fun loadCache() {
101-
lastWeatherTime = prefs.getLong("cached_time", 0L)
102-
lastLat = prefs.getFloat("cached_lat", 0f).toDouble()
103-
lastLon = prefs.getFloat("cached_lon", 0f).toDouble()
104-
cachedTemp = prefs.getInt("cached_temp", 0)
105-
cachedCode = prefs.getInt("cached_code", -1)
106-
cachedLocName = prefs.getString("cached_loc_name", "") ?: ""
107-
}
108-
109-
private fun saveCache(time: Long, lat: Double, lon: Double, temp: Int, code: Int, locName: String) {
110-
lastWeatherTime = time
111-
lastLat = lat
112-
lastLon = lon
113-
cachedTemp = temp
114-
cachedCode = code
115-
cachedLocName = locName
116-
117-
prefs.edit().apply {
118-
putLong("cached_time", time)
119-
putFloat("cached_lat", lat.toFloat())
120-
putFloat("cached_lon", lon.toFloat())
121-
putInt("cached_temp", temp)
122-
putInt("cached_code", code)
123-
putString("cached_loc_name", locName)
124-
apply()
125-
}
126-
}
127-
128-
private fun refreshGreetingText() {
129-
val weatherString = if (cachedCode != -1 && showWeather) {
130-
val condition = getLocalizedCondition(cachedCode)
131-
val emoji = getWeatherEmoji(cachedCode)
132-
val prefix = context.getString(R.string.weather_today)
133-
val locSuffix = if (cachedLocName.isNotEmpty()) " ($cachedLocName)" else ""
134-
135-
"$prefix $condition $emoji , $cachedTemp°C$locSuffix"
136-
} else {
137-
""
138-
}
139-
140-
updateTextView(weatherString)
81+
private fun scheduleWeatherRefresh() {
82+
handler.postDelayed(object : Runnable {
83+
override fun run() {
84+
if (showWeather) fetchWeather()
85+
handler.postDelayed(this, weatherInterval)
86+
}
87+
}, weatherInterval)
14188
}
14289

143-
private fun updateTextView(weatherText: String) {
90+
private fun updateGreeting(weatherText: String = "") {
14491
val hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY)
14592
@StringRes val greetRes = when (hour) {
14693
in 5..10 -> R.string.uwu_greeting_morning
@@ -154,16 +101,6 @@ class Greetings @JvmOverloads constructor(
154101
text = if (weatherText.isNotEmpty() && showWeather) "$greeting $weatherText" else greeting
155102
}
156103

157-
private fun checkSettingsAndFetch() {
158-
showWeather = DataStore.showWeatherInfo
159-
useManualCity = DataStore.manualWeatherEnabled
160-
161-
if (showWeather) fetchWeather()
162-
163-
handler.removeCallbacksAndMessages(null)
164-
handler.postDelayed({ checkSettingsAndFetch() }, weatherInterval)
165-
}
166-
167104
private fun fetchWeather() {
168105
if (useManualCity) {
169106
val city = DataStore.manualWeatherCity.ifEmpty { "Tokyo" }
@@ -178,98 +115,85 @@ class Greetings @JvmOverloads constructor(
178115
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)
179116
!= PackageManager.PERMISSION_GRANTED
180117
) {
181-
fetchWeatherByCity(DataStore.manualWeatherCity.ifEmpty { "Tokyo" })
118+
fetchWeatherByCity("Tokyo")
182119
return
183120
}
184121

185-
val cancellationTokenSource = CancellationTokenSource()
186-
fusedClient.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, cancellationTokenSource.token)
187-
.addOnSuccessListener { location: Location? ->
188-
if (location != null) {
189-
fetchWeatherByCoords(location.latitude, location.longitude)
190-
} else {
191-
fusedClient.lastLocation.addOnSuccessListener { lastLoc ->
192-
if (lastLoc != null) {
193-
fetchWeatherByCoords(lastLoc.latitude, lastLoc.longitude)
194-
} else {
195-
fetchWeatherByCity("Tokyo")
196-
}
197-
}
198-
}
199-
}
200-
.addOnFailureListener {
201-
fetchWeatherByCity("Tokyo")
122+
fusedClient.requestLocationUpdates(locationRequest, object : LocationCallback() {
123+
override fun onLocationResult(result: LocationResult) {
124+
fusedClient.removeLocationUpdates(this)
125+
val loc = result.lastLocation
126+
if (loc != null) fetchWeatherByCoords(loc.latitude, loc.longitude)
127+
else fetchWeatherByCity("Tokyo")
202128
}
129+
}, Looper.getMainLooper())
203130
}
204131

205132
private fun fetchWeatherByCoords(lat: Double, lon: Double) {
206133
coroutineScope.launch(Dispatchers.IO) {
207134
try {
208135
val now = System.currentTimeMillis()
209-
val dist = FloatArray(1)
210-
Location.distanceBetween(lastLat, lastLon, lat, lon, dist)
211-
val distanceChanged = dist[0] > minDistanceChange
212-
213-
if ((now - lastWeatherTime) < weatherInterval && !distanceChanged && cachedCode != -1) {
214-
withContext(Dispatchers.Main) { refreshGreetingText() }
136+
if ((now - lastWeatherTime) < weatherInterval && lastWeatherText != null) {
137+
withContext(Dispatchers.Main) { updateGreeting(lastWeatherText!!) }
215138
return@launch
216139
}
217140

218-
var locationName = ""
219-
try {
220-
val geocoder = Geocoder(context, getAppLocale())
221-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
222-
geocoder.getFromLocation(lat, lon, 1) { addresses ->
223-
if (addresses.isNotEmpty()) {
224-
locationName = addresses[0].subLocality ?: addresses[0].locality ?: ""
225-
}
226-
}
227-
} else {
228-
@Suppress("DEPRECATION")
229-
val addresses = geocoder.getFromLocation(lat, lon, 1)
230-
if (!addresses.isNullOrEmpty()) {
231-
locationName = addresses[0].subLocality ?: addresses[0].locality ?: ""
232-
}
233-
}
234-
} catch (e: Exception) {
235-
e.printStackTrace()
236-
}
141+
val geoUrl = "https://geocoding-api.open-meteo.com/v1/reverse?latitude=$lat&longitude=$lon"
142+
val geoResponse = URL(geoUrl).readText()
143+
val geoJson = JSONObject(geoResponse)
144+
val cityName = geoJson.optJSONArray("results")
145+
?.optJSONObject(0)?.optString("name") ?: "Your location"
237146

238147
val response = URL(
239-
"https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon&current_weather=true&timezone=auto"
148+
"https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon&current_weather=true"
240149
).readText()
241-
242150
val current = JSONObject(response).getJSONObject("current_weather")
243151
val temp = current.getDouble("temperature").roundToInt()
244152
val code = current.getInt("weathercode")
245153

246-
withContext(Dispatchers.Main) {
247-
saveCache(now, lat, lon, temp, code, locationName)
248-
refreshGreetingText()
249-
}
154+
val condition = getLocalizedCondition(code)
155+
val emoji = getWeatherEmoji(code)
156+
val prefix = context.getString(R.string.weather_today)
157+
val weatherText = "$prefix $condition $emoji, $temp°C ($cityName)"
250158

159+
lastWeatherText = weatherText
160+
lastWeatherTime = now
161+
withContext(Dispatchers.Main) { updateGreeting(weatherText) }
251162
} catch (e: Exception) {
252163
e.printStackTrace()
253-
if (cachedCode != -1) {
254-
withContext(Dispatchers.Main) { refreshGreetingText() }
255-
}
256164
}
257165
}
258166
}
259167

260168
private fun fetchWeatherByCity(city: String) {
261169
coroutineScope.launch(Dispatchers.IO) {
262170
try {
263-
val geoResponse = URL("https://geocoding-api.open-meteo.com/v1/search?name=$city&count=1").readText()
171+
val encoded = URLEncoder.encode(city, "UTF-8")
172+
val geoResponse = URL("https://geocoding-api.open-meteo.com/v1/search?name=$encoded").readText()
264173
val geoJson = JSONObject(geoResponse)
265174
val results = geoJson.optJSONArray("results") ?: return@launch
266-
267-
if (results.length() > 0) {
268-
val first = results.getJSONObject(0)
269-
val lat = first.getDouble("latitude")
270-
val lon = first.getDouble("longitude")
271-
fetchWeatherByCoords(lat, lon)
272-
}
175+
if (results.length() == 0) return@launch
176+
177+
val first = results.getJSONObject(0)
178+
val lat = first.getDouble("latitude")
179+
val lon = first.getDouble("longitude")
180+
val cityName = first.optString("name", city)
181+
182+
val response = URL(
183+
"https://api.open-meteo.com/v1/forecast?latitude=$lat&longitude=$lon&current_weather=true"
184+
).readText()
185+
val current = JSONObject(response).getJSONObject("current_weather")
186+
val temp = current.getDouble("temperature").roundToInt()
187+
val code = current.getInt("weathercode")
188+
189+
val condition = getLocalizedCondition(code)
190+
val emoji = getWeatherEmoji(code)
191+
val prefix = context.getString(R.string.weather_today)
192+
val weatherText = "$prefix $condition $emoji, $temp°C — $cityName"
193+
194+
lastWeatherText = weatherText
195+
lastWeatherTime = System.currentTimeMillis()
196+
withContext(Dispatchers.Main) { updateGreeting(weatherText) }
273197
} catch (e: Exception) {
274198
e.printStackTrace()
275199
}
@@ -314,4 +238,4 @@ class Greetings @JvmOverloads constructor(
314238
}
315239
return context.getString(resId)
316240
}
317-
}
241+
}

0 commit comments

Comments
 (0)