diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..94085ad
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+WeatherApp
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index fb7f4a8..b589d56 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..1b1b82d
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 4e3844e..ecd50f0 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -1,18 +1,19 @@
+
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..c224ad5
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 12b852b..1039c01 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..16660f1
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 1bb6d8d..0000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,82 +0,0 @@
-plugins {
- id 'com.android.application'
- id 'org.jetbrains.kotlin.android'
- id 'kotlin-kapt'
- id 'dagger.hilt.android.plugin'
-}
-
-android {
- compileSdk 32
-
- defaultConfig {
- applicationId "com.plcoding.weatherapp"
- minSdk 21
- targetSdk 32
- versionCode 1
- versionName "1.0"
-
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- vectorDrawables {
- useSupportLibrary true
- }
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
- }
- }
- compileOptions {
- coreLibraryDesugaringEnabled true
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = '1.8'
- }
- buildFeatures {
- compose true
- }
- composeOptions {
- kotlinCompilerExtensionVersion compose_version
- }
- packagingOptions {
- resources {
- excludes += '/META-INF/{AL2.0,LGPL2.1}'
- }
- }
-}
-
-dependencies {
-
- implementation 'androidx.core:core-ktx:1.7.0'
- implementation "androidx.compose.ui:ui:$compose_version"
- implementation "androidx.compose.material:material:$compose_version"
- implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
- implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
- implementation 'androidx.activity:activity-compose:1.3.1'
- testImplementation 'junit:junit:4.13.2'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
- androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
- debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
- coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
-
- //Dagger - Hilt
- implementation "com.google.dagger:hilt-android:2.40.5"
- kapt "com.google.dagger:hilt-android-compiler:2.40.5"
- implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
- kapt "androidx.hilt:hilt-compiler:1.0.0"
- implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
-
- // Location Services
- implementation 'com.google.android.gms:play-services-location:20.0.0'
-
- // Retrofit
- implementation 'com.squareup.retrofit2:retrofit:2.9.0'
- implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
- implementation "com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3"
-
- implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"
-}
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..98211cf
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,75 @@
+plugins {
+ alias(libs.plugins.android.application)
+ alias(libs.plugins.kotlin.android)
+ alias(libs.plugins.kotlin.compose)
+ id("com.google.devtools.ksp")
+ id("com.google.dagger.hilt.android")
+}
+
+android {
+ namespace = "com.plcoding.weatherapp"
+ compileSdk = 35
+
+ defaultConfig {
+ applicationId = "com.plcoding.weatherapp"
+ minSdk = 26
+ targetSdk = 34
+ versionCode = 1
+ versionName = "1.0"
+
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ isMinifyEnabled = false
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
+ }
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+ kotlinOptions {
+ jvmTarget = "11"
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+
+ implementation(libs.androidx.core.ktx)
+ implementation(libs.androidx.lifecycle.runtime.ktx)
+ implementation(libs.androidx.activity.compose)
+ implementation(platform(libs.androidx.compose.bom))
+ implementation(libs.androidx.ui)
+ implementation(libs.androidx.ui.graphics)
+ implementation(libs.androidx.ui.tooling.preview)
+ implementation(libs.androidx.material3)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.androidx.junit)
+ androidTestImplementation(libs.androidx.espresso.core)
+ androidTestImplementation(platform(libs.androidx.compose.bom))
+ androidTestImplementation(libs.androidx.ui.test.junit4)
+ debugImplementation(libs.androidx.ui.tooling)
+ debugImplementation(libs.androidx.ui.test.manifest)
+
+ // GMS Location
+ implementation("com.google.android.gms:play-services-location:21.3.0")
+
+ // Hilt
+ implementation("com.google.dagger:hilt-android:2.51.1")
+ ksp("com.google.dagger:hilt-android-compiler:2.51.1")
+
+ // Retrofit
+ implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
+ implementation("com.squareup.retrofit2:converter-gson:2.9.0")
+ implementation("com.squareup.okhttp3:okhttp:4.11.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.11.0")
+}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3f09106..a3850d3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -15,7 +15,7 @@
android:supportsRtl="true"
android:theme="@style/Theme.WeatherApp">
diff --git a/app/src/main/java/com/plcoding/weatherapp/MainActivity.kt b/app/src/main/java/com/plcoding/weatherapp/MainActivity.kt
new file mode 100644
index 0000000..804c89f
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/MainActivity.kt
@@ -0,0 +1,49 @@
+package com.plcoding.weatherapp
+
+import android.Manifest
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.activity.viewModels
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Scaffold
+import androidx.compose.ui.Modifier
+import com.plcoding.weatherapp.ui.screen.WeatherScreen
+import com.plcoding.weatherapp.ui.screen.WeatherViewModel
+import com.plcoding.weatherapp.ui.theme.WeatherAppTheme
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class MainActivity : ComponentActivity() {
+
+ private val viewModel: WeatherViewModel by viewModels()
+ private lateinit var permissionLauncher: ActivityResultLauncher>
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ permissionLauncher = registerForActivityResult(
+ ActivityResultContracts.RequestMultiplePermissions()
+ ) {
+ viewModel.loadWeatherInfo()
+ }
+ permissionLauncher.launch(arrayOf(
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ ))
+ setContent {
+ WeatherAppTheme {
+ Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
+ WeatherScreen(
+ modifier = Modifier.fillMaxSize().padding(innerPadding),
+ viewModel = viewModel
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/location/DefaultLocationTracker.kt b/app/src/main/java/com/plcoding/weatherapp/data/location/DefaultLocationTracker.kt
index a53d06b..3fd3e78 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/location/DefaultLocationTracker.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/location/DefaultLocationTracker.kt
@@ -9,12 +9,10 @@ import android.location.LocationManager
import androidx.core.content.ContextCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.plcoding.weatherapp.domain.location.LocationTracker
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import javax.inject.Inject
import kotlin.coroutines.resume
-@ExperimentalCoroutinesApi
class DefaultLocationTracker @Inject constructor(
private val locationClient: FusedLocationProviderClient,
private val application: Application
@@ -31,8 +29,7 @@ class DefaultLocationTracker @Inject constructor(
) == PackageManager.PERMISSION_GRANTED
val locationManager = application.getSystemService(Context.LOCATION_SERVICE) as LocationManager
- val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) ||
- locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
+ val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) || locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
if(!hasAccessCoarseLocationPermission || !hasAccessFineLocationPermission || !isGpsEnabled) {
return null
}
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/mappers/WeatherMappers.kt b/app/src/main/java/com/plcoding/weatherapp/data/mapper/WeatherMapper.kt
similarity index 74%
rename from app/src/main/java/com/plcoding/weatherapp/data/mappers/WeatherMappers.kt
rename to app/src/main/java/com/plcoding/weatherapp/data/mapper/WeatherMapper.kt
index cc18e49..fec1685 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/mappers/WeatherMappers.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/mapper/WeatherMapper.kt
@@ -1,10 +1,10 @@
-package com.plcoding.weatherapp.data.mappers
+package com.plcoding.weatherapp.data.mapper
-import com.plcoding.weatherapp.data.remote.WeatherDataDto
-import com.plcoding.weatherapp.data.remote.WeatherDto
-import com.plcoding.weatherapp.domain.weather.WeatherData
-import com.plcoding.weatherapp.domain.weather.WeatherInfo
-import com.plcoding.weatherapp.domain.weather.WeatherType
+import com.plcoding.weatherapp.data.remote.WeatherDTO
+import com.plcoding.weatherapp.data.remote.WeatherDataDTO
+import com.plcoding.weatherapp.domain.model.WeatherData
+import com.plcoding.weatherapp.domain.model.WeatherInfo
+import com.plcoding.weatherapp.domain.model.WeatherType
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
@@ -13,7 +13,7 @@ private data class IndexedWeatherData(
val data: WeatherData
)
-fun WeatherDataDto.toWeatherDataMap(): Map> {
+fun WeatherDataDTO.toWeatherDataMap(): Map> {
return time.mapIndexed { index, time ->
val temperature = temperatures[index]
val weatherCode = weatherCodes[index]
@@ -38,7 +38,7 @@ fun WeatherDataDto.toWeatherDataMap(): Map> {
}
}
-fun WeatherDto.toWeatherInfo(): WeatherInfo {
+fun WeatherDTO.toWeatherInfo(): WeatherInfo {
val weatherDataMap = weatherData.toWeatherDataMap()
val now = LocalDateTime.now()
val currentWeatherData = weatherDataMap[0]?.find {
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherApi.kt b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherAPIService.kt
similarity index 86%
rename from app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherApi.kt
rename to app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherAPIService.kt
index 67ac577..dcbbff2 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherApi.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherAPIService.kt
@@ -3,11 +3,12 @@ package com.plcoding.weatherapp.data.remote
import retrofit2.http.GET
import retrofit2.http.Query
-interface WeatherApi {
+interface WeatherAPIService {
@GET("v1/forecast?hourly=temperature_2m,weathercode,relativehumidity_2m,windspeed_10m,pressure_msl")
suspend fun getWeatherData(
@Query("latitude") lat: Double,
@Query("longitude") long: Double
- ): WeatherDto
+ ): WeatherDTO
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDataDto.kt b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDataDto.kt
index aa25dfc..b01a7cb 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDataDto.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDataDto.kt
@@ -2,7 +2,7 @@ package com.plcoding.weatherapp.data.remote
import com.squareup.moshi.Json
-data class WeatherDataDto(
+data class WeatherDataDTO(
val time: List,
@field:Json(name = "temperature_2m")
val temperatures: List,
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDto.kt b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDto.kt
index e647e50..02755ae 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDto.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/remote/WeatherDto.kt
@@ -2,7 +2,7 @@ package com.plcoding.weatherapp.data.remote
import com.squareup.moshi.Json
-data class WeatherDto(
+data class WeatherDTO(
@field:Json(name = "hourly")
- val weatherData: WeatherDataDto
-)
+ val weatherData: WeatherDataDTO
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/data/repository/WeatherRepositoryImpl.kt b/app/src/main/java/com/plcoding/weatherapp/data/repository/WeatherRepositoryImpl.kt
index 49879dd..b514959 100644
--- a/app/src/main/java/com/plcoding/weatherapp/data/repository/WeatherRepositoryImpl.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/data/repository/WeatherRepositoryImpl.kt
@@ -1,16 +1,17 @@
package com.plcoding.weatherapp.data.repository
-import com.plcoding.weatherapp.data.mappers.toWeatherInfo
-import com.plcoding.weatherapp.data.remote.WeatherApi
+import com.plcoding.weatherapp.data.mapper.toWeatherInfo
+import com.plcoding.weatherapp.data.remote.WeatherAPIService
+import com.plcoding.weatherapp.domain.model.WeatherInfo
import com.plcoding.weatherapp.domain.repository.WeatherRepository
-import com.plcoding.weatherapp.domain.util.Resource
-import com.plcoding.weatherapp.domain.weather.WeatherInfo
+import com.plcoding.weatherapp.util.Resource
import javax.inject.Inject
class WeatherRepositoryImpl @Inject constructor(
- private val api: WeatherApi
+ private val api: WeatherAPIService
): WeatherRepository {
+
override suspend fun getWeatherData(lat: Double, long: Double): Resource {
return try {
Resource.Success(
diff --git a/app/src/main/java/com/plcoding/weatherapp/di/AppModule.kt b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationProviderModule.kt
similarity index 53%
rename from app/src/main/java/com/plcoding/weatherapp/di/AppModule.kt
rename to app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationProviderModule.kt
index 6662377..80540de 100644
--- a/app/src/main/java/com/plcoding/weatherapp/di/AppModule.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationProviderModule.kt
@@ -1,31 +1,17 @@
-package com.plcoding.weatherapp.di
+package com.plcoding.weatherapp.dependencyinjection
import android.app.Application
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
-import com.plcoding.weatherapp.data.remote.WeatherApi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
-import retrofit2.Retrofit
-import retrofit2.converter.moshi.MoshiConverterFactory
-import retrofit2.create
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
-object AppModule {
-
- @Provides
- @Singleton
- fun provideWeatherApi(): WeatherApi {
- return Retrofit.Builder()
- .baseUrl("https://api.open-meteo.com/")
- .addConverterFactory(MoshiConverterFactory.create())
- .build()
- .create()
- }
+object LocationProviderModule {
@Provides
@Singleton
diff --git a/app/src/main/java/com/plcoding/weatherapp/di/LocationModule.kt b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationTrackerModule.kt
similarity index 75%
rename from app/src/main/java/com/plcoding/weatherapp/di/LocationModule.kt
rename to app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationTrackerModule.kt
index 77a203a..7e9ecf3 100644
--- a/app/src/main/java/com/plcoding/weatherapp/di/LocationModule.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/LocationTrackerModule.kt
@@ -1,4 +1,4 @@
-package com.plcoding.weatherapp.di
+package com.plcoding.weatherapp.dependencyinjection
import com.plcoding.weatherapp.data.location.DefaultLocationTracker
import com.plcoding.weatherapp.domain.location.LocationTracker
@@ -6,15 +6,14 @@ import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Singleton
-@ExperimentalCoroutinesApi
@Module
@InstallIn(SingletonComponent::class)
-abstract class LocationModule {
+abstract class LocationTrackerModule {
@Binds
@Singleton
abstract fun bindLocationTracker(defaultLocationTracker: DefaultLocationTracker): LocationTracker
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/di/RepositoryModule.kt b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RepositoryModule.kt
similarity index 52%
rename from app/src/main/java/com/plcoding/weatherapp/di/RepositoryModule.kt
rename to app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RepositoryModule.kt
index 2dcbff7..1803887 100644
--- a/app/src/main/java/com/plcoding/weatherapp/di/RepositoryModule.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RepositoryModule.kt
@@ -1,24 +1,18 @@
-package com.plcoding.weatherapp.di
+package com.plcoding.weatherapp.dependencyinjection
-import com.plcoding.weatherapp.data.location.DefaultLocationTracker
import com.plcoding.weatherapp.data.repository.WeatherRepositoryImpl
-import com.plcoding.weatherapp.domain.location.LocationTracker
import com.plcoding.weatherapp.domain.repository.WeatherRepository
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import javax.inject.Singleton
-@ExperimentalCoroutinesApi
@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryModule {
@Binds
@Singleton
- abstract fun bindWeatherRepository(
- weatherRepositoryImpl: WeatherRepositoryImpl
- ): WeatherRepository
+ abstract fun bindWeatherRepository(weatherRepositoryImpl: WeatherRepositoryImpl): WeatherRepository
}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RetrofitModule.kt b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RetrofitModule.kt
new file mode 100644
index 0000000..ccbe681
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/dependencyinjection/RetrofitModule.kt
@@ -0,0 +1,25 @@
+package com.plcoding.weatherapp.dependencyinjection
+
+import com.plcoding.weatherapp.data.remote.WeatherAPIService
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.components.SingletonComponent
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+import javax.inject.Singleton
+
+@Module
+@InstallIn(SingletonComponent::class)
+object RetrofitModule {
+
+ @Provides
+ @Singleton
+ fun provideWeatherApi(): WeatherAPIService {
+ return Retrofit.Builder()
+ .baseUrl("https://api.open-meteo.com/")
+ .addConverterFactory(MoshiConverterFactory.create())
+ .build()
+ .create(WeatherAPIService::class.java)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherData.kt b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherData.kt
similarity index 82%
rename from app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherData.kt
rename to app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherData.kt
index af5377c..e38e301 100644
--- a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherData.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherData.kt
@@ -1,4 +1,4 @@
-package com.plcoding.weatherapp.domain.weather
+package com.plcoding.weatherapp.domain.model
import java.time.LocalDateTime
@@ -9,4 +9,4 @@ data class WeatherData(
val windSpeed: Double,
val humidity: Double,
val weatherType: WeatherType
-)
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherInfo.kt b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherInfo.kt
similarity index 71%
rename from app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherInfo.kt
rename to app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherInfo.kt
index ff7e9fc..71bac0d 100644
--- a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherInfo.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherInfo.kt
@@ -1,6 +1,6 @@
-package com.plcoding.weatherapp.domain.weather
+package com.plcoding.weatherapp.domain.model
data class WeatherInfo(
val weatherDataPerDay: Map>,
val currentWeatherData: WeatherData?
-)
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherType.kt b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherType.kt
similarity index 74%
rename from app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherType.kt
rename to app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherType.kt
index d39edec..835685a 100644
--- a/app/src/main/java/com/plcoding/weatherapp/domain/weather/WeatherType.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/domain/model/WeatherType.kt
@@ -1,4 +1,4 @@
-package com.plcoding.weatherapp.domain.weather
+package com.plcoding.weatherapp.domain.model
import androidx.annotation.DrawableRes
import com.plcoding.weatherapp.R
@@ -7,111 +7,111 @@ sealed class WeatherType(
val weatherDesc: String,
@DrawableRes val iconRes: Int
) {
- object ClearSky : WeatherType(
+ data object ClearSky : WeatherType(
weatherDesc = "Clear sky",
iconRes = R.drawable.ic_sunny
)
- object MainlyClear : WeatherType(
+ data object MainlyClear : WeatherType(
weatherDesc = "Mainly clear",
iconRes = R.drawable.ic_cloudy
)
- object PartlyCloudy : WeatherType(
+ data object PartlyCloudy : WeatherType(
weatherDesc = "Partly cloudy",
iconRes = R.drawable.ic_cloudy
)
- object Overcast : WeatherType(
+ data object Overcast : WeatherType(
weatherDesc = "Overcast",
iconRes = R.drawable.ic_cloudy
)
- object Foggy : WeatherType(
+ data object Foggy : WeatherType(
weatherDesc = "Foggy",
iconRes = R.drawable.ic_very_cloudy
)
- object DepositingRimeFog : WeatherType(
+ data object DepositingRimeFog : WeatherType(
weatherDesc = "Depositing rime fog",
iconRes = R.drawable.ic_very_cloudy
)
- object LightDrizzle : WeatherType(
+ data object LightDrizzle : WeatherType(
weatherDesc = "Light drizzle",
iconRes = R.drawable.ic_rainshower
)
- object ModerateDrizzle : WeatherType(
+ data object ModerateDrizzle : WeatherType(
weatherDesc = "Moderate drizzle",
iconRes = R.drawable.ic_rainshower
)
- object DenseDrizzle : WeatherType(
+ data object DenseDrizzle : WeatherType(
weatherDesc = "Dense drizzle",
iconRes = R.drawable.ic_rainshower
)
- object LightFreezingDrizzle : WeatherType(
+ data object LightFreezingDrizzle : WeatherType(
weatherDesc = "Slight freezing drizzle",
iconRes = R.drawable.ic_snowyrainy
)
- object DenseFreezingDrizzle : WeatherType(
+ data object DenseFreezingDrizzle : WeatherType(
weatherDesc = "Dense freezing drizzle",
iconRes = R.drawable.ic_snowyrainy
)
- object SlightRain : WeatherType(
+ data object SlightRain : WeatherType(
weatherDesc = "Slight rain",
iconRes = R.drawable.ic_rainy
)
- object ModerateRain : WeatherType(
+ data object ModerateRain : WeatherType(
weatherDesc = "Rainy",
iconRes = R.drawable.ic_rainy
)
- object HeavyRain : WeatherType(
+ data object HeavyRain : WeatherType(
weatherDesc = "Heavy rain",
iconRes = R.drawable.ic_rainy
)
- object HeavyFreezingRain: WeatherType(
+ data object HeavyFreezingRain: WeatherType(
weatherDesc = "Heavy freezing rain",
iconRes = R.drawable.ic_snowyrainy
)
- object SlightSnowFall: WeatherType(
+ data object SlightSnowFall: WeatherType(
weatherDesc = "Slight snow fall",
iconRes = R.drawable.ic_snowy
)
- object ModerateSnowFall: WeatherType(
+ data object ModerateSnowFall: WeatherType(
weatherDesc = "Moderate snow fall",
iconRes = R.drawable.ic_heavysnow
)
- object HeavySnowFall: WeatherType(
+ data object HeavySnowFall: WeatherType(
weatherDesc = "Heavy snow fall",
iconRes = R.drawable.ic_heavysnow
)
- object SnowGrains: WeatherType(
+ data object SnowGrains: WeatherType(
weatherDesc = "Snow grains",
iconRes = R.drawable.ic_heavysnow
)
- object SlightRainShowers: WeatherType(
+ data object SlightRainShowers: WeatherType(
weatherDesc = "Slight rain showers",
iconRes = R.drawable.ic_rainshower
)
- object ModerateRainShowers: WeatherType(
+ data object ModerateRainShowers: WeatherType(
weatherDesc = "Moderate rain showers",
iconRes = R.drawable.ic_rainshower
)
- object ViolentRainShowers: WeatherType(
+ data object ViolentRainShowers: WeatherType(
weatherDesc = "Violent rain showers",
iconRes = R.drawable.ic_rainshower
)
- object SlightSnowShowers: WeatherType(
+ data object SlightSnowShowers: WeatherType(
weatherDesc = "Light snow showers",
iconRes = R.drawable.ic_snowy
)
- object HeavySnowShowers: WeatherType(
+ data object HeavySnowShowers: WeatherType(
weatherDesc = "Heavy snow showers",
iconRes = R.drawable.ic_snowy
)
- object ModerateThunderstorm: WeatherType(
+ data object ModerateThunderstorm: WeatherType(
weatherDesc = "Moderate thunderstorm",
iconRes = R.drawable.ic_thunder
)
- object SlightHailThunderstorm: WeatherType(
+ data object SlightHailThunderstorm: WeatherType(
weatherDesc = "Thunderstorm with slight hail",
iconRes = R.drawable.ic_rainythunder
)
- object HeavyHailThunderstorm: WeatherType(
+ data object HeavyHailThunderstorm: WeatherType(
weatherDesc = "Thunderstorm with heavy hail",
iconRes = R.drawable.ic_rainythunder
)
diff --git a/app/src/main/java/com/plcoding/weatherapp/domain/repository/WeatherRepository.kt b/app/src/main/java/com/plcoding/weatherapp/domain/repository/WeatherRepository.kt
index ade5766..9f073ef 100644
--- a/app/src/main/java/com/plcoding/weatherapp/domain/repository/WeatherRepository.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/domain/repository/WeatherRepository.kt
@@ -1,7 +1,7 @@
package com.plcoding.weatherapp.domain.repository
-import com.plcoding.weatherapp.domain.util.Resource
-import com.plcoding.weatherapp.domain.weather.WeatherInfo
+import com.plcoding.weatherapp.domain.model.WeatherInfo
+import com.plcoding.weatherapp.util.Resource
interface WeatherRepository {
suspend fun getWeatherData(lat: Double, long: Double): Resource
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/MainActivity.kt b/app/src/main/java/com/plcoding/weatherapp/presentation/MainActivity.kt
deleted file mode 100644
index 3445490..0000000
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/MainActivity.kt
+++ /dev/null
@@ -1,75 +0,0 @@
-package com.plcoding.weatherapp.presentation
-
-import android.Manifest
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.activity.result.ActivityResultLauncher
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.activity.viewModels
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
-import androidx.compose.material.CircularProgressIndicator
-import androidx.compose.material.Text
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.unit.dp
-import com.plcoding.weatherapp.presentation.ui.theme.DarkBlue
-import com.plcoding.weatherapp.presentation.ui.theme.DeepBlue
-import com.plcoding.weatherapp.presentation.ui.theme.WeatherAppTheme
-import dagger.hilt.android.AndroidEntryPoint
-
-@AndroidEntryPoint
-class MainActivity : ComponentActivity() {
-
- private val viewModel: WeatherViewModel by viewModels()
- private lateinit var permissionLauncher: ActivityResultLauncher>
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- permissionLauncher = registerForActivityResult(
- ActivityResultContracts.RequestMultiplePermissions()
- ) {
- viewModel.loadWeatherInfo()
- }
- permissionLauncher.launch(arrayOf(
- Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- ))
- setContent {
- WeatherAppTheme {
- Box(
- modifier = Modifier.fillMaxSize()
- ) {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .background(DarkBlue)
- ) {
- WeatherCard(
- state = viewModel.state,
- backgroundColor = DeepBlue
- )
- Spacer(modifier = Modifier.height(16.dp))
- WeatherForecast(state = viewModel.state)
- }
- if(viewModel.state.isLoading) {
- CircularProgressIndicator(
- modifier = Modifier.align(Alignment.Center)
- )
- }
- viewModel.state.error?.let { error ->
- Text(
- text = error,
- color = Color.Red,
- textAlign = TextAlign.Center,
- modifier = Modifier.align(Alignment.Center)
- )
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherState.kt b/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherState.kt
deleted file mode 100644
index 67bf723..0000000
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherState.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.plcoding.weatherapp.presentation
-
-import com.plcoding.weatherapp.domain.weather.WeatherInfo
-
-data class WeatherState(
- val weatherInfo: WeatherInfo? = null,
- val isLoading: Boolean = false,
- val error: String? = null
-)
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Color.kt b/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Color.kt
deleted file mode 100644
index 29be551..0000000
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Color.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.plcoding.weatherapp.presentation.ui.theme
-
-import androidx.compose.ui.graphics.Color
-
-val DarkBlue = Color(0xFF1B3B5A)
-val DeepBlue = Color(0xFF102840)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Shape.kt b/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Shape.kt
deleted file mode 100644
index 92ffb9f..0000000
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Shape.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.plcoding.weatherapp.presentation.ui.theme
-
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Shapes
-import androidx.compose.ui.unit.dp
-
-val Shapes = Shapes(
- small = RoundedCornerShape(4.dp),
- medium = RoundedCornerShape(4.dp),
- large = RoundedCornerShape(0.dp)
-)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Theme.kt b/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Theme.kt
deleted file mode 100644
index ba1d274..0000000
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Theme.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.plcoding.weatherapp.presentation.ui.theme
-
-import androidx.compose.material.MaterialTheme
-import androidx.compose.runtime.Composable
-
-@Composable
-fun WeatherAppTheme(content: @Composable () -> Unit) {
- MaterialTheme(
- typography = Typography,
- shapes = Shapes,
- content = content
- )
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/HourlyWeatherDisplay.kt b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentHourlyWeather.kt
similarity index 83%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/HourlyWeatherDisplay.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentHourlyWeather.kt
index 4e2cd57..215fa2d 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/HourlyWeatherDisplay.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentHourlyWeather.kt
@@ -1,10 +1,10 @@
-package com.plcoding.weatherapp.presentation
+package com.plcoding.weatherapp.ui.component
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
-import androidx.compose.material.Text
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
@@ -13,19 +13,18 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import com.plcoding.weatherapp.domain.weather.WeatherData
+import com.plcoding.weatherapp.domain.model.WeatherData
import java.time.format.DateTimeFormatter
+
@Composable
-fun HourlyWeatherDisplay(
+fun ComponentHourlyWeather(
weatherData: WeatherData,
modifier: Modifier = Modifier,
textColor: Color = Color.White
) {
val formattedTime = remember(weatherData) {
- weatherData.time.format(
- DateTimeFormatter.ofPattern("HH:mm")
- )
+ weatherData.time.format(DateTimeFormatter.ofPattern("HH:mm"))
}
Column(
modifier = modifier,
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherCard.kt b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeather.kt
similarity index 80%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/WeatherCard.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeather.kt
index 1940d36..dffd6d5 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherCard.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeather.kt
@@ -1,13 +1,21 @@
-package com.plcoding.weatherapp.presentation
+package com.plcoding.weatherapp.ui.component
import androidx.compose.foundation.Image
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.Card
-import androidx.compose.material.Text
+import androidx.compose.material3.Card
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.painterResource
@@ -16,20 +24,19 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.plcoding.weatherapp.R
+import com.plcoding.weatherapp.ui.screen.state.WeatherUIState
import java.time.format.DateTimeFormatter
import kotlin.math.roundToInt
+
@Composable
fun WeatherCard(
- state: WeatherState,
- backgroundColor: Color,
- modifier: Modifier = Modifier
+ state: WeatherUIState
) {
state.weatherInfo?.currentWeatherData?.let { data ->
Card(
- backgroundColor = backgroundColor,
shape = RoundedCornerShape(10.dp),
- modifier = modifier.padding(16.dp)
+ modifier = Modifier.padding(16.dp).clip(RoundedCornerShape(10.dp))
) {
Column(
modifier = Modifier
@@ -39,9 +46,7 @@ fun WeatherCard(
) {
Text(
text = "Today ${
- data.time.format(
- DateTimeFormatter.ofPattern("HH:mm")
- )
+ data.time.format(DateTimeFormatter.ofPattern("HH:mm"))
}",
modifier = Modifier.align(Alignment.End),
color = Color.White
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherDataDisplay.kt b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherData.kt
similarity index 86%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/WeatherDataDisplay.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherData.kt
index fc3077a..509cc58 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherDataDisplay.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherData.kt
@@ -1,17 +1,16 @@
-package com.plcoding.weatherapp.presentation
+package com.plcoding.weatherapp.ui.component
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.material.Icon
-import androidx.compose.material.Text
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherForecast.kt b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherForecast.kt
similarity index 67%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/WeatherForecast.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherForecast.kt
index a75d391..11307ba 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherForecast.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/component/ComponentWeatherForecast.kt
@@ -1,23 +1,27 @@
-package com.plcoding.weatherapp.presentation
+package com.plcoding.weatherapp.ui.component
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.Text
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import com.plcoding.weatherapp.ui.screen.state.WeatherUIState
@Composable
fun WeatherForecast(
- state: WeatherState,
- modifier: Modifier = Modifier
+ state: WeatherUIState
) {
state.weatherInfo?.weatherDataPerDay?.get(0)?.let { data ->
Column(
- modifier = modifier
+ modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
@@ -29,7 +33,7 @@ fun WeatherForecast(
Spacer(modifier = Modifier.height(16.dp))
LazyRow(content = {
items(data) { weatherData ->
- HourlyWeatherDisplay(
+ ComponentHourlyWeather(
weatherData = weatherData,
modifier = Modifier
.height(100.dp)
diff --git a/app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherScreen.kt b/app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherScreen.kt
new file mode 100644
index 0000000..82f18fd
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherScreen.kt
@@ -0,0 +1,66 @@
+package com.plcoding.weatherapp.ui.screen
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.material3.Button
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import com.plcoding.weatherapp.ui.component.WeatherCard
+import com.plcoding.weatherapp.ui.component.WeatherForecast
+
+@Composable
+fun WeatherScreen(
+ modifier: Modifier = Modifier,
+ viewModel: WeatherViewModel
+) {
+ Column(
+ modifier = modifier
+ ) {
+ WeatherCard(state = viewModel.state)
+ Spacer(modifier = Modifier.height(16.dp))
+ WeatherForecast(state = viewModel.state)
+ }
+ if (viewModel.state.isLoading) {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator()
+ }
+ }
+ viewModel.state.error?.let { error ->
+ Column (
+ modifier = Modifier.fillMaxSize(),
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = error,
+ color = Color.Red,
+ textAlign = TextAlign.Center
+ )
+ Button(
+ onClick = {
+ viewModel.loadWeatherInfo()
+ }
+ ) {
+ Text(
+ text = "Try Again!",
+ color = Color.Black,
+ textAlign = TextAlign.Center
+ )
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherViewModel.kt b/app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherViewModel.kt
similarity index 78%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/WeatherViewModel.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherViewModel.kt
index e35faee..3bef83d 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/WeatherViewModel.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/screen/WeatherViewModel.kt
@@ -1,4 +1,4 @@
-package com.plcoding.weatherapp.presentation
+package com.plcoding.weatherapp.ui.screen
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -7,7 +7,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.plcoding.weatherapp.domain.location.LocationTracker
import com.plcoding.weatherapp.domain.repository.WeatherRepository
-import com.plcoding.weatherapp.domain.util.Resource
+import com.plcoding.weatherapp.ui.screen.state.WeatherUIState
+import com.plcoding.weatherapp.util.Resource
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -18,7 +19,7 @@ class WeatherViewModel @Inject constructor(
private val locationTracker: LocationTracker
): ViewModel() {
- var state by mutableStateOf(WeatherState())
+ var state by mutableStateOf(WeatherUIState())
private set
fun loadWeatherInfo() {
@@ -43,8 +44,16 @@ class WeatherViewModel @Inject constructor(
error = result.message
)
}
+
+ is Resource.Loading -> {
+ state = state.copy(
+ weatherInfo = null,
+ isLoading = true,
+ error = null
+ )
+ }
}
- } ?: kotlin.run {
+ } ?: run {
state = state.copy(
isLoading = false,
error = "Couldn't retrieve location. Make sure to grant permission and enable GPS."
diff --git a/app/src/main/java/com/plcoding/weatherapp/ui/screen/state/WeatherUIState.kt b/app/src/main/java/com/plcoding/weatherapp/ui/screen/state/WeatherUIState.kt
new file mode 100644
index 0000000..9abbdcc
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/screen/state/WeatherUIState.kt
@@ -0,0 +1,9 @@
+package com.plcoding.weatherapp.ui.screen.state
+
+import com.plcoding.weatherapp.domain.model.WeatherInfo
+
+data class WeatherUIState(
+ val weatherInfo: WeatherInfo? = null,
+ val isLoading: Boolean = false,
+ val error: String? = null
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/ui/theme/Color.kt b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Color.kt
new file mode 100644
index 0000000..34920c5
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Color.kt
@@ -0,0 +1,11 @@
+package com.plcoding.weatherapp.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Purple80 = Color(0xFFD0BCFF)
+val PurpleGrey80 = Color(0xFFCCC2DC)
+val Pink80 = Color(0xFFEFB8C8)
+
+val Purple40 = Color(0xFF6650a4)
+val PurpleGrey40 = Color(0xFF625b71)
+val Pink40 = Color(0xFF7D5260)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/ui/theme/Theme.kt b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Theme.kt
new file mode 100644
index 0000000..d9e704a
--- /dev/null
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Theme.kt
@@ -0,0 +1,57 @@
+package com.plcoding.weatherapp.ui.theme
+
+import android.os.Build
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.darkColorScheme
+import androidx.compose.material3.dynamicDarkColorScheme
+import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+
+private val DarkColorScheme = darkColorScheme(
+ primary = Purple80,
+ secondary = PurpleGrey80,
+ tertiary = Pink80
+)
+
+private val LightColorScheme = lightColorScheme(
+ primary = Purple40,
+ secondary = PurpleGrey40,
+ tertiary = Pink40
+
+ /* Other default colors to override
+ background = Color(0xFFFFFBFE),
+ surface = Color(0xFFFFFBFE),
+ onPrimary = Color.White,
+ onSecondary = Color.White,
+ onTertiary = Color.White,
+ onBackground = Color(0xFF1C1B1F),
+ onSurface = Color(0xFF1C1B1F),
+ */
+)
+
+@Composable
+fun WeatherAppTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ // Dynamic color is available on Android 12+
+ dynamicColor: Boolean = true,
+ content: @Composable () -> Unit
+) {
+ val colorScheme = when {
+ dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ val context = LocalContext.current
+ if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
+ }
+
+ darkTheme -> DarkColorScheme
+ else -> LightColorScheme
+ }
+
+ MaterialTheme(
+ colorScheme = colorScheme,
+ typography = Typography,
+ content = content
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Type.kt b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Type.kt
similarity index 53%
rename from app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Type.kt
rename to app/src/main/java/com/plcoding/weatherapp/ui/theme/Type.kt
index 2d80614..b4d3694 100644
--- a/app/src/main/java/com/plcoding/weatherapp/presentation/ui/theme/Type.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/ui/theme/Type.kt
@@ -1,6 +1,6 @@
-package com.plcoding.weatherapp.presentation.ui.theme
+package com.plcoding.weatherapp.ui.theme
-import androidx.compose.material.Typography
+import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
@@ -8,21 +8,27 @@ import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
- body1 = TextStyle(
+ bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
- fontSize = 16.sp
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ letterSpacing = 0.5.sp
)
/* Other default text styles to override
- button = TextStyle(
+ titleLarge = TextStyle(
fontFamily = FontFamily.Default,
- fontWeight = FontWeight.W500,
- fontSize = 14.sp
+ fontWeight = FontWeight.Normal,
+ fontSize = 22.sp,
+ lineHeight = 28.sp,
+ letterSpacing = 0.sp
),
- caption = TextStyle(
+ labelSmall = TextStyle(
fontFamily = FontFamily.Default,
- fontWeight = FontWeight.Normal,
- fontSize = 12.sp
+ fontWeight = FontWeight.Medium,
+ fontSize = 11.sp,
+ lineHeight = 16.sp,
+ letterSpacing = 0.5.sp
)
*/
)
\ No newline at end of file
diff --git a/app/src/main/java/com/plcoding/weatherapp/domain/util/Resource.kt b/app/src/main/java/com/plcoding/weatherapp/util/Resource.kt
similarity index 74%
rename from app/src/main/java/com/plcoding/weatherapp/domain/util/Resource.kt
rename to app/src/main/java/com/plcoding/weatherapp/util/Resource.kt
index 21dae92..9ce1d00 100644
--- a/app/src/main/java/com/plcoding/weatherapp/domain/util/Resource.kt
+++ b/app/src/main/java/com/plcoding/weatherapp/util/Resource.kt
@@ -1,6 +1,7 @@
-package com.plcoding.weatherapp.domain.util
+package com.plcoding.weatherapp.util
sealed class Resource(val data: T? = null, val message: String? = null) {
class Success(data: T?): Resource(data)
class Error(message: String, data: T? = null): Resource(data, message)
+ class Loading: Resource()
}
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index ba794a9..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-buildscript {
- ext {
- compose_version = '1.1.1'
- }
- dependencies {
- classpath "com.google.dagger:hilt-android-gradle-plugin:2.40.5"
- }
-}// Top-level build file where you can add configuration options common to all sub-projects/modules.
-plugins {
- id 'com.android.application' version '7.1.0' apply false
- id 'com.android.library' version '7.1.0' apply false
- id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
-}
-
-task clean(type: Delete) {
- delete rootProject.buildDir
-}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..3542cad
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,10 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ alias(libs.plugins.android.application) apply false
+ alias(libs.plugins.kotlin.android) apply false
+ alias(libs.plugins.kotlin.compose) apply false
+ // Ksp
+ id("com.google.devtools.ksp") version "2.0.21-1.0.27" apply false
+ // Hilt
+ id("com.google.dagger.hilt.android") version "2.51.1" apply false
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index cd0519b..20e2a01 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -8,11 +8,11 @@
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# This option should only be used with decoupled projects. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app"s APK
+# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..0aa7ea2
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,32 @@
+[versions]
+agp = "8.7.3"
+kotlin = "2.0.21"
+coreKtx = "1.15.0"
+junit = "4.13.2"
+junitVersion = "1.2.1"
+espressoCore = "3.6.1"
+lifecycleRuntimeKtx = "2.8.7"
+activityCompose = "1.9.3"
+composeBom = "2024.04.01"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c15336a..dce2419 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Jun 26 11:26:31 CEST 2022
+#Wed Dec 11 18:58:46 TRT 2024
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
-zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/settings.gradle b/settings.gradle.kts
similarity index 55%
rename from settings.gradle
rename to settings.gradle.kts
index dfe4890..5908742 100644
--- a/settings.gradle
+++ b/settings.gradle.kts
@@ -1,8 +1,14 @@
pluginManagement {
repositories {
- gradlePluginPortal()
- google()
+ google {
+ content {
+ includeGroupByRegex("com\\.android.*")
+ includeGroupByRegex("com\\.google.*")
+ includeGroupByRegex("androidx.*")
+ }
+ }
mavenCentral()
+ gradlePluginPortal()
}
}
dependencyResolutionManagement {
@@ -12,5 +18,7 @@ dependencyResolutionManagement {
mavenCentral()
}
}
+
rootProject.name = "WeatherApp"
-include ':app'
+include(":app")
+
\ No newline at end of file