Skip to content

Commit

Permalink
23 create a use case to build the correct list of working hours for l…
Browse files Browse the repository at this point in the history
…ocation (#36)
  • Loading branch information
yuriisurzhykov authored May 28, 2024
2 parents 000a75d + 1fd0ef3 commit 835aad8
Show file tree
Hide file tree
Showing 54 changed files with 1,618 additions and 65 deletions.
27 changes: 27 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ import java.util.Properties
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.jetbrains.kotlin.android)
alias(libs.plugins.dagger.hilt.android)
alias(libs.plugins.kotlin.ksp)
}

ksp {
arg(RoomSchemaArgProvider(File(rootProject.projectDir, "room-schemas")))
}

hilt {
enableAggregatingTask = false
}

android {
Expand Down Expand Up @@ -69,6 +79,13 @@ android {

dependencies {

implementation(projects.uiFeatures.locationDetails)
implementation(projects.locationData.cache)
implementation(projects.locationDomain)
implementation(projects.locationData.repository)
implementation(projects.core)
implementation(projects.locationUikit)

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand All @@ -77,6 +94,16 @@ dependencies {
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.material3)

implementation(libs.retrofit)
implementation(libs.androidx.room.ktx)
implementation(libs.kotlinx.serialization.json)
implementation(libs.retrofit.converter.kotlinx.serialization)

implementation(libs.dagger.hilt.android)
ksp(libs.dagger.hilt.compiler)
ksp(libs.androidx.room.compiler)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
Expand Down
6 changes: 4 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".PursApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -13,9 +16,8 @@
android:theme="@style/Theme.PursTest"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".ui.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.PursTest">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.yuriisurzhykov.purs

import android.app.Application
import dagger.hilt.android.HiltAndroidApp

@HiltAndroidApp
class PursApplication : Application()
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.github.yuriisurzhykov.purs.data

import androidx.room.Database
import androidx.room.RoomDatabase
import com.github.yuriisurzhykov.purs.data.cache.LocationDao
import com.github.yuriisurzhykov.purs.data.cache.model.LocationCache
import com.github.yuriisurzhykov.purs.data.cache.model.WorkingHourCache

@Database(
entities = [LocationCache::class, WorkingHourCache::class],
version = 1,
exportSchema = true
)
abstract class PursDatabase : RoomDatabase() {

abstract fun locationDao(): LocationDao
}
202 changes: 202 additions & 0 deletions app/src/main/java/com/github/yuriisurzhykov/purs/di/PursAppModule.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package com.github.yuriisurzhykov.purs.di

import android.content.Context
import androidx.room.Room
import com.github.yuriisurzhykov.purs.core.MergeStrategy
import com.github.yuriisurzhykov.purs.core.RequestResponseMergeStrategy
import com.github.yuriisurzhykov.purs.core.RequestResult
import com.github.yuriisurzhykov.purs.data.PursDatabase
import com.github.yuriisurzhykov.purs.data.cache.GetLocationId
import com.github.yuriisurzhykov.purs.data.cache.LocationCacheDataSource
import com.github.yuriisurzhykov.purs.data.cache.LocationDao
import com.github.yuriisurzhykov.purs.data.cache.model.LocationWithWorkingHours
import com.github.yuriisurzhykov.purs.data.cloud.LocationCloudDataSource
import com.github.yuriisurzhykov.purs.data.cloud.LocationService
import com.github.yuriisurzhykov.purs.data.repository.LocationCloudMapper
import com.github.yuriisurzhykov.purs.data.repository.LocationCloudToCacheMapper
import com.github.yuriisurzhykov.purs.data.repository.LocationRepository
import com.github.yuriisurzhykov.purs.data.repository.LocationWorkingHoursCloudMapper
import com.github.yuriisurzhykov.purs.domain.chain.AddMissingDaysUseCase
import com.github.yuriisurzhykov.purs.domain.chain.MergeCrossDayTimeSlotsUseCase
import com.github.yuriisurzhykov.purs.domain.chain.MergeTimeSlotsUseCase
import com.github.yuriisurzhykov.purs.domain.chain.SortWorkingDaysUseCase
import com.github.yuriisurzhykov.purs.domain.chain.WorkingHourChainProcessor
import com.github.yuriisurzhykov.purs.domain.mapper.LocationCacheToDomainMapper
import com.github.yuriisurzhykov.purs.domain.mapper.StringToDayOfWeekMapper
import com.github.yuriisurzhykov.purs.domain.mapper.StringToLocalTimeMapper
import com.github.yuriisurzhykov.purs.domain.mapper.WorkingHourCacheToDomainMapper
import com.github.yuriisurzhykov.purs.domain.usecase.BuildCurrentLocationStatusUseCase
import com.github.yuriisurzhykov.purs.domain.usecase.BuildWorkingHoursListUseCase
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.OkHttpClient
import retrofit2.Converter
import retrofit2.Retrofit
import java.util.concurrent.TimeUnit
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object PursAppModule {

@Provides
@Singleton
fun provideDatabase(
@ApplicationContext context: Context
): PursDatabase = Room.databaseBuilder(context, PursDatabase::class.java, "purs.db").build()

@Provides
@Singleton
fun provideLocationsDao(database: PursDatabase): LocationDao = database.locationDao()

@Provides
@Singleton
fun provideRetrofit(
okHttpClient: OkHttpClient,
converterFactory: Converter.Factory
): Retrofit =
Retrofit.Builder()
.client(okHttpClient)
.addConverterFactory(converterFactory)
.baseUrl("https://purs-demo-bucket-test.s3.us-west-2.amazonaws.com")
.build()

@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build()

@Provides
@Singleton
fun provideConverterFactory(): Converter.Factory =
Json.asConverterFactory("application/json; charset=UTF8".toMediaType())


@Provides
@Singleton
fun provideBuildWorkingHoursListUseCase(
repository: LocationRepository,
mapper: LocationCacheToDomainMapper,
processor: WorkingHourChainProcessor,
buildLocationStatus: BuildCurrentLocationStatusUseCase
): BuildWorkingHoursListUseCase = BuildWorkingHoursListUseCase.Base(
repository,
mapper,
processor,
buildLocationStatus
)

@Provides
@Singleton
fun provideWorkingHourChainProcessor(
mergeCrossDayTimeSlots: MergeCrossDayTimeSlotsUseCase,
addMissingDaysUseCase: AddMissingDaysUseCase,
sortWorkingDaysUseCase: SortWorkingDaysUseCase
): WorkingHourChainProcessor = WorkingHourChainProcessor.Base(
mergeCrossDayTimeSlots,
addMissingDaysUseCase,
sortWorkingDaysUseCase
)

@Provides
@Singleton
fun provideMergeTimeSlotsUseCase(): MergeTimeSlotsUseCase = MergeTimeSlotsUseCase.Base()

@Provides
@Singleton
fun provideAddMissingDaysUseCase(): AddMissingDaysUseCase = AddMissingDaysUseCase.Base()

@Provides
@Singleton
fun provideMergeCrossDayTimeSlotsUseCase(sortWorkingDaysUseCase: SortWorkingDaysUseCase): MergeCrossDayTimeSlotsUseCase =
MergeCrossDayTimeSlotsUseCase.Base(sortWorkingDaysUseCase)

@Provides
@Singleton
fun provideSortMissingDaysUseCase(): SortWorkingDaysUseCase = SortWorkingDaysUseCase.Base()

@Provides
@Singleton
fun provideLocationCacheToDomainMapper(
mapper: WorkingHourCacheToDomainMapper
): LocationCacheToDomainMapper =
LocationCacheToDomainMapper.Base(mapper)

@Provides
@Singleton
fun provideBuildCurrentLocationStatusUseCase(): BuildCurrentLocationStatusUseCase =
BuildCurrentLocationStatusUseCase.Base()

@Provides
@Singleton
fun provideWorkingHourCacheToDomainMapper(
mapper: StringToLocalTimeMapper,
mergeTimeSlotsUseCase: MergeTimeSlotsUseCase,
stringToDayMapper: StringToDayOfWeekMapper
): WorkingHourCacheToDomainMapper =
WorkingHourCacheToDomainMapper.Base(mapper, mergeTimeSlotsUseCase, stringToDayMapper)

@Provides
@Singleton
fun provideStringToDayOfWeekMapper(): StringToDayOfWeekMapper = StringToDayOfWeekMapper.Base()

@Provides
@Singleton
fun provideStringToLocalTimeMapper(): StringToLocalTimeMapper = StringToLocalTimeMapper.Base()

@Provides
@Singleton
fun provideLocationRepository(
cloudDataSource: LocationCloudDataSource,
cacheDataSource: LocationCacheDataSource,
sourceMergeStrategy: MergeStrategy<RequestResult<LocationWithWorkingHours>>,
mapper: LocationCloudToCacheMapper
): LocationRepository = LocationRepository.Base(
cloudDataSource,
cacheDataSource,
sourceMergeStrategy,
mapper
)

@Provides
@Singleton
fun provideLocationCloudToCacheMapper(
cloudMapper: LocationCloudMapper,
workingHoursCloudMapper: LocationWorkingHoursCloudMapper
): LocationCloudToCacheMapper =
LocationCloudToCacheMapper.Base(cloudMapper, workingHoursCloudMapper)

@Provides
@Singleton
fun provideLocationCloudDataSource(locationService: LocationService): LocationCloudDataSource =
LocationCloudDataSource.Base(locationService)

@Provides
@Singleton
fun provideLocationCacheDataSource(
locationDao: LocationDao,
getLocationId: GetLocationId
): LocationCacheDataSource = LocationCacheDataSource.Base(locationDao, getLocationId)

@Provides
@Singleton
fun provideGetLocationId(): GetLocationId = GetLocationId.Const(1)

@Provides
@Singleton
fun provideMergeStrategy(): MergeStrategy<RequestResult<LocationWithWorkingHours>> =
RequestResponseMergeStrategy()

@Provides
@Singleton
fun provideLocationService(retrofit: Retrofit): LocationService =
retrofit.create(LocationService::class.java)
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.yuriisurzhykov.purs
package com.github.yuriisurzhykov.purs.ui

import android.os.Bundle
import androidx.activity.ComponentActivity
Expand All @@ -7,41 +7,22 @@ import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.github.yuriisurzhykov.purs.ui.theme.PursTestTheme
import com.github.yuriisurzhykov.purs.location.details.LocationDetails
import com.github.yuriisurzhykov.purs.location.uikit.theme.PursTestTheme
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PursTestTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
LocationDetails(modifier = Modifier.padding(innerPadding))
}
}
}
}
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
PursTestTheme {
Greeting("Android")
}
}
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/ProjectProperties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object ProjectProperties {
val javaTargetCompatibility = JavaVersion.VERSION_19

const val kotlinJvmTarget = "19"
const val minSdkVersion = 24
const val minSdkVersion = 26
const val targetSdkVersion = 34
const val compileSdkVersion = 34
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.github.yuriisurzhykov.purs.core

interface MergeStrategy<E> {
// right is cache, left is cloud
fun merge(right: E, left: E): E
}

internal class RequestResponseMergeStrategy<T : Any> : MergeStrategy<RequestResult<T>> {
class RequestResponseMergeStrategy<T : Any> : MergeStrategy<RequestResult<T>> {

// right is cache, left is cloud
override fun merge(
right: RequestResult<T>,
left: RequestResult<T>
Expand Down
4 changes: 0 additions & 4 deletions location-data/cache/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ plugins {
alias(libs.plugins.kotlin.ksp)
}

ksp {
arg(RoomSchemaArgProvider(File(rootProject.projectDir, "room-schemas")))
}

android {
namespace = "com.github.yuriisurzhykov.purs.data.cache"
compileSdk = ProjectProperties.compileSdkVersion
Expand Down
Loading

0 comments on commit 835aad8

Please sign in to comment.