Skip to content

Commit 80a445b

Browse files
committed
- Added comments for few important methods like mapping weather forecast api responses to domain and then ui model for better understanding.
1 parent 4871fa2 commit 80a445b

4 files changed

Lines changed: 96 additions & 3 deletions

File tree

app/src/main/java/com/asad/weatherapp/data/mapper/WeatherForecastMapper.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,20 @@ import javax.inject.Inject
1313

1414
class WeatherForecastMapper @Inject constructor() :
1515
ResponseMapper<List<Forecast>, List<WeatherForecastModel>> {
16-
16+
/**
17+
* Maps a list of Forecast API models to the domain-specific WeatherForecastModel.
18+
*
19+
* @param input List<Forecast> - Raw API data.
20+
* @return List<WeatherForecastModel> - Transformed list suitable for UI consumption.
21+
*/
1722
override fun map(input: List<Forecast>): List<WeatherForecastModel> = input.toDomainModel()
1823

24+
/**
25+
* Extension function that converts a list of Forecast API objects into domain models.
26+
* Groups forecasts by date and extracts a midday representative item for each day.
27+
*
28+
* @return List<WeatherForecastModel> - Grouped by day, each with a list of time slots.
29+
*/
1930
private fun List<Forecast>.toDomainModel(): List<WeatherForecastModel> {
2031
return this
2132
.groupBy { it.dtTxt.substring(0, 10) } // group by day
@@ -44,13 +55,25 @@ class WeatherForecastMapper @Inject constructor() :
4455
}
4556
}
4657

58+
/**
59+
* Converts raw weather type string from API to domain-specific WeatherType enum.
60+
*
61+
* @param type String - e.g., "Rain", "Clouds", "Clear"
62+
* @return WeatherType - Enum used in domain/UI layer.
63+
*/
4764
private fun mapToWeatherType(type: String): WeatherType =
4865
when {
4966
type.equals(RAIN, ignoreCase = true) -> WeatherType.RAINY
5067
type.equals(CLOUDS, ignoreCase = true) -> WeatherType.CLOUDY
5168
else -> WeatherType.SUNNY
5269
}
5370

71+
/**
72+
* Maps raw weather condition IDs from API to domain-specific WeatherCondition.
73+
*
74+
* @param weatherId Int - Numeric ID from OpenWeather API.
75+
* @return WeatherCondition - Enum representing weather condition in UI/domain.
76+
*/
5477
private fun mapWeatherIcon(weatherId: Int): WeatherCondition {
5578
return when (weatherId) {
5679
in 200..232 -> WeatherCondition.THUNDERSTORM

app/src/main/java/com/asad/weatherapp/data/repository/LocationCoordinatesRepositoryImpl.kt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,46 @@ import javax.inject.Inject
1414
import kotlin.coroutines.resume
1515

1616
class LocationCoordinatesRepositoryImpl @Inject constructor(
17-
@ApplicationContext context: Context
17+
@ApplicationContext context: Context,
1818
) : LocationCoordinatesRepository {
1919
private val fusedLocationClient by lazy {
2020
LocationServices.getFusedLocationProviderClient(
2121
context
2222
)
2323
}
2424

25+
/**
26+
* Suspend function to get the current device location as a [LocationCoordinates] object.
27+
*
28+
* @OptIn(ExperimentalCoroutinesApi::class) - We are using suspendCancellableCoroutine which is currently experimental.
29+
* @RequiresPermission - Ensures caller has ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION.
30+
*
31+
* @return Result<LocationCoordinates> - Returns success with latitude and longitude or failure if location is unavailable.
32+
*
33+
* Implementation details:
34+
* - Uses suspendCancellableCoroutine to bridge callback-based FusedLocationProviderClient API with coroutines.
35+
* - On success:
36+
* - If location is not null, resume coroutine with Result.success(location).
37+
* - If location is null, resume coroutine with Result.failure(ErrorHandler.UnknownError).
38+
* - On failure:
39+
* - Wrap the exception in ErrorHandler.UnknownError and resume the coroutine with failure.
40+
* - The suspendCancellableCoroutine ensures proper cancellation handling if the coroutine is cancelled before completion.
41+
*/
2542
@OptIn(ExperimentalCoroutinesApi::class)
2643
@RequiresPermission(allOf = [Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION])
2744
override suspend fun getCurrentLocation(): Result<LocationCoordinates> =
2845
suspendCancellableCoroutine { continuation ->
2946
fusedLocationClient.lastLocation
3047
.addOnSuccessListener { location ->
3148
if (location != null) {
32-
continuation.resume(Result.success(LocationCoordinates(location.latitude, location.longitude)))
49+
continuation.resume(
50+
Result.success(
51+
LocationCoordinates(
52+
location.latitude,
53+
location.longitude
54+
)
55+
)
56+
)
3357
} else {
3458
continuation.resume(Result.failure(ErrorHandler.UnknownError(Exception())))
3559
}

app/src/main/java/com/asad/weatherapp/ui/forecast/mapper/WeatherForecastUiMapper.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@ class WeatherForecastUiMapper @Inject constructor() :
2020
}
2121
}
2222

23+
/**
24+
* Maps a list of domain [WeatherForecastModel] objects into a [WeatherForecastUIModel] suitable
25+
* for display in the UI.
26+
*
27+
* @param input List of WeatherForecastModel domain objects.
28+
* @return WeatherForecastUIModel containing mapped UI items and the current background.
29+
*
30+
* Implementation details:
31+
* - For each day, finds the closest time slot to the current time.
32+
* - Maps each time slot to a [WeatherForecastItemUIModel] containing day name, temperature, and weather icon.
33+
* - Determines the overall background based on the first available weather type.
34+
*/
2335
override fun map(input: List<WeatherForecastModel>): WeatherForecastUIModel {
2436
val data = input.map { dto ->
2537
val currentSlot = closestTimeSlotToCurrentTime(dto.timeSlots)

app/src/main/java/com/asad/weatherapp/utils/CommonUtils.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,51 @@ import java.time.ZoneId
77
import java.time.format.TextStyle
88
import java.util.Locale
99

10+
/**
11+
* Returns the full day name (e.g., "Monday", "Tuesday") for a given timestamp in seconds.
12+
*
13+
* @param timestampSeconds The timestamp in seconds since epoch.
14+
* @return Day name as a String based on the device's default locale.
15+
*
16+
* Implementation details:
17+
* - Converts epoch seconds to an Instant.
18+
* - Converts the Instant to local date-time using the system default timezone.
19+
* - Extracts the day of week and returns its full display name according to the default locale.
20+
*/
1021
fun getDayName(timestampSeconds: Long): String {
1122
return Instant.ofEpochSecond(timestampSeconds)
1223
.atZone(ZoneId.systemDefault())
1324
.dayOfWeek
1425
.getDisplayName(TextStyle.FULL, Locale.getDefault())
1526
}
1627

28+
/**
29+
* Finds the closest weather time slot to the current system time.
30+
*
31+
* @param slots List of [WeatherForecastTimeSlotModel] representing forecasted times.
32+
* @return The [WeatherForecastTimeSlotModel] that is closest to the current hour.
33+
*
34+
* Implementation details:
35+
* - Gets the current system hour using LocalTime.now().
36+
* - Finds the time slot with the minimum difference between its hour and the current hour.
37+
* - If the slots list is empty or minByOrNull returns null, it falls back to the first slot.
38+
*/
1739
fun closestTimeSlotToCurrentTime(slots: List<WeatherForecastTimeSlotModel>): WeatherForecastTimeSlotModel {
1840
val currentHour = LocalTime.now().hour
1941
return slots.minByOrNull { hourDiff(it, currentHour) } ?: slots.first()
2042
}
2143

44+
/**
45+
* Calculates the absolute difference in hours between a time slot and a reference hour.
46+
*
47+
* @param slot The [WeatherForecastTimeSlotModel] containing a time string in "HH:mm:ss" format.
48+
* @param hour The reference hour (0-23) to compare against.
49+
* @return Absolute difference in hours as an Int.
50+
*
51+
* Implementation details:
52+
* - Extracts the hour part from the slot's timeSlot string by taking the substring of first 2 characters.
53+
* - Converts it to Int.
54+
* - Calculates the absolute difference between the slot hour and the reference hour.
55+
*/
2256
private fun hourDiff(slot: WeatherForecastTimeSlotModel, hour: Int): Int =
2357
kotlin.math.abs(slot.timeSlot.substring(0, 2).toInt() - hour)

0 commit comments

Comments
 (0)