Skip to content

Commit 858c38b

Browse files
Refactor: Add manual location and temperature unit settings for weather
This commit introduces the ability for users to manually set their location for weather information and choose their preferred temperature unit (Celsius/Fahrenheit). - Added `gpsLocation` boolean to `Prefs` to toggle between GPS and manual location. - Added `tempUnit` (Celsius/Fahrenheit) to `Prefs`. - Implemented `saveLocation`, `loadLocation`, and `loadLocationName` in `Prefs` to handle manual location data. - Updated `WeatherHelper` to use GPS or saved manual location based on `prefs.gpsLocation`. - Modified `WeatherReceiver` to fetch weather based on the selected temperature unit and added location search functionality using the Open-Meteo Geocoding API. - Added UI elements in `SettingsFragment` for: - Toggling GPS location. - Selecting temperature unit. - Navigating to a new `LocationSearchFragment` to set manual location if GPS is disabled. - Created `LocationSearchFragment` with a `ListView` to display location search results and an `EditText` for user input. - Added new string resources for the new settings and temperature units. - Updated `nav_graph.xml` to include navigation to `LocationSearchFragment`. - Moved `WeatherHelper` to the `helper` package. - Updated weather API endpoint in `WeatherReceiver` to include temperature unit. - Adjusted JSON parsing in `WeatherReceiver` for the updated API response. Signed-off-by: CreativeCodeCat <[email protected]>
1 parent 0ab929e commit 858c38b

File tree

10 files changed

+420
-71
lines changed

10 files changed

+420
-71
lines changed

app/src/main/java/com/github/droidworksstudio/mlauncher/data/Constants.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,27 @@ object Constants {
471471
}
472472
}
473473

474+
enum class TempUnits : EnumOption {
475+
Celsius,
476+
Fahrenheit;
477+
478+
fun getString(): String {
479+
return when (this) {
480+
Celsius -> getLocalizedString(R.string.celsius)
481+
Fahrenheit -> getLocalizedString(R.string.fahrenheit)
482+
}
483+
}
484+
485+
// Keep this for Composable usage
486+
@Composable
487+
override fun string(): String {
488+
return when (this) {
489+
Celsius -> getLocalizedString(R.string.celsius)
490+
Fahrenheit -> getLocalizedString(R.string.fahrenheit)
491+
}
492+
}
493+
}
494+
474495

475496
enum class FontFamily : EnumOption {
476497
System,

app/src/main/java/com/github/droidworksstudio/mlauncher/data/Prefs.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import com.github.droidworksstudio.mlauncher.data.Constants.Gravity
1313
import com.github.droidworksstudio.mlauncher.helper.emptyString
1414
import com.github.droidworksstudio.mlauncher.helper.getUserHandleFromString
1515
import com.github.droidworksstudio.mlauncher.helper.isSystemInDarkMode
16+
import com.github.droidworksstudio.mlauncher.helper.receivers.LocationResult
1617
import com.squareup.moshi.JsonAdapter
1718
import com.squareup.moshi.Moshi
1819
import com.squareup.moshi.Types
@@ -52,6 +53,7 @@ private const val NAVIGATION_BAR = "NAVIGATION_BAR"
5253
private const val SHOW_BATTERY = "SHOW_BATTERY"
5354
private const val SHOW_BATTERY_ICON = "SHOW_BATTERY_ICON"
5455
private const val SHOW_WEATHER = "SHOW_WEATHER"
56+
private const val GPS_LOCATION = "GPS_LOCATION"
5557
private const val SHOW_AZSIDEBAR = "SHOW_AZSIDEBAR"
5658
private const val SHOW_DATE = "SHOW_DATE"
5759
private const val HOME_LOCKED = "HOME_LOCKED"
@@ -97,6 +99,7 @@ private const val APP_USAGE_STATS = "APP_USAGE_STATS"
9799
private const val APP_OPACITY = "APP_OPACITY"
98100
private const val APP_LANGUAGE = "APP_LANGUAGE"
99101
private const val APP_THEME = "APP_THEME"
102+
private const val TEMP_UNIT = "TEMP_UNIT"
100103
private const val SHORT_SWIPE_UP = "SHORT_SWIPE_UP"
101104
private const val SHORT_SWIPE_DOWN = "SHORT_SWIPE_DOWN"
102105
private const val SHORT_SWIPE_LEFT = "SHORT_SWIPE_LEFT"
@@ -137,6 +140,10 @@ private const val BUBBLE_CATEGORY_COLOR = "BUBBLE_CATEGORY_COLOR"
137140
private const val INPUT_MESSAGE_COLOR = "INPUT_MESSAGE_COLOR"
138141
private const val INPUT_MESSAGEHINT_COLOR = "INPUT_MESSAGEHINT_COLOR"
139142

143+
private const val WEATHER_LATITUDE = "WEATHER_LATITUDE"
144+
private const val WEATHER_LONGITUDE = "WEATHER_LONGITUDE"
145+
private const val WEATHER_LOCATION = "WEATHER_LOCATION"
146+
140147
private const val NOTES_MESSAGES = "NOTES_MESSAGES"
141148
private const val NOTES_CATEGORY = "NOTES_CATEGORY"
142149
private const val NOTES_PRIORITY = "NOTES_PRIORITY"
@@ -534,6 +541,10 @@ class Prefs(val context: Context) {
534541
get() = getSetting(SHOW_WEATHER, true)
535542
set(value) = prefsNormal.edit { putBoolean(SHOW_WEATHER, value) }
536543

544+
var gpsLocation: Boolean
545+
get() = getSetting(GPS_LOCATION, true)
546+
set(value) = prefsNormal.edit { putBoolean(GPS_LOCATION, value) }
547+
537548
var showBatteryIcon: Boolean
538549
get() = getSetting(SHOW_BATTERY_ICON, true)
539550
set(value) = prefsNormal.edit { putBoolean(SHOW_BATTERY_ICON, value) }
@@ -674,6 +685,12 @@ class Prefs(val context: Context) {
674685
}
675686
set(value) = prefsNormal.edit { putString(APP_THEME, value.name) }
676687

688+
var tempUnit: Constants.TempUnits
689+
get() {
690+
return getEnumSetting(TEMP_UNIT, Constants.TempUnits.Celsius)
691+
}
692+
set(value) = prefsNormal.edit { putString(TEMP_UNIT, value.name) }
693+
677694
var appLanguage: Constants.Language
678695
get() {
679696
return getEnumSetting(APP_LANGUAGE, Constants.Language.System)
@@ -962,6 +979,33 @@ class Prefs(val context: Context) {
962979
}
963980
}
964981

982+
/** 🔹 Save selected location into SharedPreferences */
983+
fun saveLocation(results: LocationResult) {
984+
results.let {
985+
prefsNormal.edit {
986+
putString(WEATHER_LOCATION, it.region)
987+
putFloat(WEATHER_LATITUDE, it.latitude.toFloat())
988+
putFloat(WEATHER_LONGITUDE, it.longitude.toFloat())
989+
}
990+
}
991+
}
992+
993+
/** 🔹 Load saved location */
994+
fun loadLocation(): Pair<Double, Double>? {
995+
val lat = prefsNormal.getFloat(WEATHER_LATITUDE, Float.NaN)
996+
val lon = prefsNormal.getFloat(WEATHER_LONGITUDE, Float.NaN)
997+
998+
return if (!lat.isNaN() && !lon.isNaN()) {
999+
Pair(lat.toDouble(), lon.toDouble())
1000+
} else {
1001+
null
1002+
}
1003+
}
1004+
1005+
fun loadLocationName(): String {
1006+
return prefsNormal.getString(WEATHER_LOCATION, "Select Location").toString()
1007+
}
1008+
9651009
fun remove(prefName: String) {
9661010
prefsNormal.edit { remove(prefName) }
9671011
}

app/src/main/java/com/github/droidworksstudio/mlauncher/helper/WeatherHelper.kt

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.github.droidworksstudio.mlauncher.weather
1+
package com.github.droidworksstudio.mlauncher.helper
22

33
import android.Manifest
44
import android.content.Context
@@ -12,6 +12,7 @@ import androidx.core.app.ActivityCompat
1212
import androidx.lifecycle.LifecycleOwner
1313
import androidx.lifecycle.lifecycleScope
1414
import com.github.droidworksstudio.common.AppLogger
15+
import com.github.droidworksstudio.mlauncher.data.Prefs
1516
import com.github.droidworksstudio.mlauncher.helper.receivers.WeatherReceiver
1617
import kotlinx.coroutines.launch
1718

@@ -21,64 +22,84 @@ class WeatherHelper(
2122
private val updateWeatherUi: (temperatureText: String) -> Unit
2223
) {
2324

25+
private val prefs = Prefs(context)
26+
2427
fun getWeather() {
25-
val locationManager =
26-
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
28+
val useGps = prefs.gpsLocation // or however you store it
2729

28-
val fineLocationGranted = ActivityCompat.checkSelfPermission(
29-
context, Manifest.permission.ACCESS_FINE_LOCATION
30-
) == PackageManager.PERMISSION_GRANTED
30+
if (useGps) {
31+
val locationManager =
32+
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
3133

32-
val coarseLocationGranted = ActivityCompat.checkSelfPermission(
33-
context, Manifest.permission.ACCESS_COARSE_LOCATION
34-
) == PackageManager.PERMISSION_GRANTED
34+
val fineLocationGranted = ActivityCompat.checkSelfPermission(
35+
context, Manifest.permission.ACCESS_FINE_LOCATION
36+
) == PackageManager.PERMISSION_GRANTED
3537

36-
if (!fineLocationGranted && !coarseLocationGranted) {
37-
AppLogger.w("WeatherReceiver", "Location permission not granted. Aborting.")
38-
return
39-
}
38+
val coarseLocationGranted = ActivityCompat.checkSelfPermission(
39+
context, Manifest.permission.ACCESS_COARSE_LOCATION
40+
) == PackageManager.PERMISSION_GRANTED
4041

41-
val provider = when {
42-
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) -> LocationManager.GPS_PROVIDER
43-
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> LocationManager.NETWORK_PROVIDER
44-
else -> {
45-
AppLogger.e("WeatherReceiver", "No location provider enabled.")
42+
if (!fineLocationGranted && !coarseLocationGranted) {
43+
AppLogger.w("WeatherReceiver", "Location permission not granted. Aborting.")
4644
return
4745
}
48-
}
4946

50-
// ✅ Try last known location first
51-
val lastKnown = locationManager.getLastKnownLocation(provider)
52-
if (lastKnown != null) {
53-
handleLocation(lastKnown)
54-
return
55-
}
47+
val provider = when {
48+
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) -> LocationManager.GPS_PROVIDER
49+
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) -> LocationManager.NETWORK_PROVIDER
50+
else -> {
51+
AppLogger.e("WeatherReceiver", "No location provider enabled.")
52+
return
53+
}
54+
}
5655

57-
// 🔁 Fallback: wait for real-time update
58-
val locationListener = object : LocationListener {
59-
override fun onLocationChanged(location: Location) {
60-
locationManager.removeUpdates(this)
61-
handleLocation(location)
56+
// ✅ Try last known location first
57+
val lastKnown = locationManager.getLastKnownLocation(provider)
58+
if (lastKnown != null) {
59+
handleLocation(lastKnown)
60+
return
6261
}
6362

64-
override fun onProviderDisabled(provider: String) {}
65-
override fun onProviderEnabled(provider: String) {}
63+
// 🔁 Fallback: wait for real-time update
64+
val locationListener = object : LocationListener {
65+
override fun onLocationChanged(location: Location) {
66+
locationManager.removeUpdates(this)
67+
handleLocation(location)
68+
}
6669

67-
@Deprecated("Deprecated in Java")
68-
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
70+
override fun onProviderDisabled(provider: String) {}
71+
override fun onProviderEnabled(provider: String) {}
72+
73+
@Deprecated("Deprecated in Java")
74+
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
75+
}
76+
}
77+
78+
try {
79+
locationManager.requestLocationUpdates(
80+
provider,
81+
30 * 60 * 1000L, // every 30 minutes
82+
100f, // or 100 meters
83+
locationListener,
84+
Looper.getMainLooper()
85+
)
86+
} catch (se: SecurityException) {
87+
se.printStackTrace()
88+
}
89+
} else {
90+
// 📍 Use saved custom location from prefs
91+
val savedLocation = prefs.loadLocation() // Pair<Double, Double>?
92+
if (savedLocation != null) {
93+
val (lat, lon) = savedLocation
94+
val fakeLocation = Location("prefs_location").apply {
95+
latitude = lat
96+
longitude = lon
97+
}
98+
handleLocation(fakeLocation)
99+
} else {
100+
AppLogger.e("WeatherReceiver", "No custom location saved in prefs.")
69101
}
70-
}
71102

72-
try {
73-
locationManager.requestLocationUpdates(
74-
provider,
75-
30 * 60 * 1000L, // every 30 minutes
76-
100f, // or 100 meters
77-
locationListener,
78-
Looper.getMainLooper()
79-
)
80-
} catch (se: SecurityException) {
81-
se.printStackTrace()
82103
}
83104
}
84105

@@ -87,10 +108,10 @@ class WeatherHelper(
87108
val lon = location.longitude
88109
AppLogger.d("WeatherReceiver", "Location: $lat, $lon")
89110

90-
val receiver = WeatherReceiver()
111+
val receiver = WeatherReceiver(context)
91112

92113
lifecycleOwner.lifecycleScope.launch {
93-
val weatherReceiver = receiver.getCurrentWeather(lat, lon)
114+
val weatherReceiver = receiver.loadWeatherForSavedLocation(lat, lon)
94115
val weatherType =
95116
receiver.getWeatherEmoji(weatherReceiver?.currentWeather?.weatherCode ?: -1)
96117

0 commit comments

Comments
 (0)