Skip to content

Commit

Permalink
WEATHER MVI in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
myofficework000 committed Jun 29, 2023
1 parent 89205d3 commit 1ab5e7e
Show file tree
Hide file tree
Showing 20 changed files with 722 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.abanoub.weather.data.location

import android.Manifest
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationManager
import androidx.core.content.ContextCompat
import com.abanoub.weather.domain.location.LocationTracker
import com.google.android.gms.location.FusedLocationProviderClient
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject
import kotlin.coroutines.resume

@OptIn(ExperimentalCoroutinesApi::class)
class DefaultLocationTracker @Inject constructor(
private val locationClient: FusedLocationProviderClient,
private val application: Application
) : LocationTracker {

override suspend fun getCurrentLocation(): Location? {
val hasAccessFineLocationPermission = ContextCompat.checkSelfPermission(
application,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED

val hasAccessCoarseLocationPermission = ContextCompat.checkSelfPermission(
application,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED

val locationManager =
application.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ||
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)

if (!hasAccessFineLocationPermission || !hasAccessCoarseLocationPermission || !isGpsEnabled)
return null

return suspendCancellableCoroutine { continuation ->
locationClient.lastLocation.apply {
if (isComplete) {
if (isSuccessful) {
continuation.resume(result)
} else {
continuation.resume(null)
}
return@suspendCancellableCoroutine
}
addOnSuccessListener {
continuation.resume(it)
}
addOnFailureListener {
continuation.resume(null)
}
addOnCanceledListener {
continuation.cancel()
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.abanoub.weather.data.mappers

import android.os.Build
import androidx.annotation.RequiresApi
import com.abanoub.weather.data.remote.WeatherDataDto
import com.abanoub.weather.data.remote.WeatherDto
import com.abanoub.weather.domain.weather.WeatherData
import com.abanoub.weather.domain.weather.WeatherInfo
import com.abanoub.weather.domain.weather.WeatherType
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

private data class IndexedWeatherData(
val index: Int,
val data: WeatherData
)

@RequiresApi(Build.VERSION_CODES.O)
fun WeatherDataDto.toWeatherDataMap(): Map<Int, List<WeatherData>> {
return time.mapIndexed { index, time ->
val temperature = temperatures[index]
val weatherCode = weatherCodes[index]
val windSpeed = windSpeeds[index]
val pressure = pressures[index]
val humidity = humidities[index]
IndexedWeatherData(
index = index,
data = WeatherData(
time = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME),
temperatureCelsius = temperature,
pressure = pressure,
windSpeed = windSpeed,
humidity = humidity,
weatherType = WeatherType.fromWMO(weatherCode)
)
)
}.groupBy {
it.index / 24
}.mapValues {
it.value.map { it.data }
}
}

@RequiresApi(Build.VERSION_CODES.O)
fun WeatherDto.toWeatherInfo(): WeatherInfo {
val weatherDataMap = weatherData.toWeatherDataMap()
val now = LocalDateTime.now()
val currentWeatherData = weatherDataMap[0]?.find {
val hour = if (now.minute < 30) now.hour else now.hour + 1
it.time.hour == hour
}
return WeatherInfo(
weatherDataPerDay = weatherDataMap,
currentWeatherData = currentWeatherData
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.abanoub.weather.data.remote

import retrofit2.http.GET
import retrofit2.http.Query

interface WeatherApi {

@GET("v1/forecast?hourly=temperature_2m,weathercode,relativehumidity_2m,windspeed_10m,pressure_msl")
suspend fun getWeather(
@Query("latitude") latitude: Double,
@Query("longitude") longitude: Double,
): WeatherDto
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.abanoub.weather.data.remote

import com.squareup.moshi.Json

data class WeatherDataDto(
val time: List<String>,
@field:Json(name = "temperature_2m")
val temperatures: List<Double>,
@field:Json(name = "weathercode")
val weatherCodes: List<Int>,
@field:Json(name = "pressure_msl")
val pressures: List<Double>,
@field:Json(name = "windspeed_10m")
val windSpeeds: List<Double>,
@field:Json(name = "relativehumidity_2m")
val humidities: List<Double>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.abanoub.weather.data.remote

import com.squareup.moshi.Json

data class WeatherDto(
@field:Json(name = "hourly")
val weatherData: WeatherDataDto
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.abanoub.weather.data.repository

import android.os.Build
import androidx.annotation.RequiresApi
import com.abanoub.weather.data.mappers.toWeatherInfo
import com.abanoub.weather.data.remote.WeatherApi
import com.abanoub.weather.domain.repository.WeatherRepository
import com.abanoub.weather.domain.util.Resource
import com.abanoub.weather.domain.weather.WeatherInfo
import javax.inject.Inject

class WeatherRepositoryImpl @Inject constructor(
private val api: WeatherApi
) : WeatherRepository {

@RequiresApi(Build.VERSION_CODES.O)
override suspend fun getWeatherData(lat: Double, long: Double): Resource<WeatherInfo> {
return try {
Resource.Success(
data = api.getWeather(
latitude = lat,
longitude = long
).toWeatherInfo()
)
} catch (e: Exception) {
return Resource.Error(e.message ?: "Unknown Error")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.abanoub.weather.domain.location

import android.location.Location

interface LocationTracker {
suspend fun getCurrentLocation(): Location?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.abanoub.weather.domain.repository

import com.abanoub.weather.domain.util.Resource
import com.abanoub.weather.domain.weather.WeatherInfo

interface WeatherRepository {
suspend fun getWeatherData(lat: Double, long: Double): Resource<WeatherInfo>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.abanoub.weather.domain.util

sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class Success<T>(data: T?) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.abanoub.weather.domain.weather

import java.time.LocalDateTime

data class WeatherData(
val time: LocalDateTime,
val temperatureCelsius: Double,
val pressure: Double,
val windSpeed: Double,
val humidity: Double,
val weatherType: WeatherType
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.abanoub.weather.domain.weather

data class WeatherInfo(
val weatherDataPerDay: Map<Int, List<WeatherData>>,
val currentWeatherData: WeatherData?
)
Loading

0 comments on commit 1ab5e7e

Please sign in to comment.