Skip to content

Commit

Permalink
Merge pull request #157 from ryanw-mobile/refactor/130-koin
Browse files Browse the repository at this point in the history
[refactor] (#130) introduce koin
  • Loading branch information
ryanw-mobile committed Mar 21, 2024
2 parents 7e2efbd + 3d71a59 commit 593099d
Show file tree
Hide file tree
Showing 115 changed files with 652 additions and 514 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Trying to reuse all my Android development knowledge as possible, otherwise nati

### Major libraries used

* [Koin](https://github.com/InsertKoinIO/koin) - Dependency injection
* [Ktor](https://ktor.io/) - HTTP Client (Option 1, default)
* [Retrofit2](https://square.github.io/retrofit/) - HTTP Client (Option 2)
* [Kotlin Serialization](https://kotlinlang.org/docs/serialization.html) - For JSON parsing. Replacing Moshi
Expand Down Expand Up @@ -96,7 +97,7 @@ Trying to reuse all my Android development knowledge as possible, otherwise nati

### Running the app

There is a plan to deprecate the configuration file automate the app build process. However, before that happened,
There is a plan to deprecate the configuration file automate the app build process. However, before that happened,
you still have to properly maintain your own configuration file to build and run the app.

To start the app from command line: `./gradlew runReleaseDistributable`
4 changes: 4 additions & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ kotlin {
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.serialization.kotlinx.json)

implementation(libs.koin.core)
implementation(libs.koin.test)
implementation(libs.koin.android)

api(libs.moko.mvvm.core)
api(libs.moko.mvvm.compose)
}
Expand Down
6 changes: 6 additions & 0 deletions composeApp/composeApp.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="CheckStyle-IDEA-Module" serialisationVersion="2">
<option name="activeLocationsIds" />
</component>
</module>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

package uk.ryanwong.gmap2ics.app.configs

import uk.ryanwong.gmap2ics.app.models.ActivityType
import uk.ryanwong.gmap2ics.domain.models.ActivityType

/***
* Configuration template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

package uk.ryanwong.gmap2ics.app.configs

import uk.ryanwong.gmap2ics.app.models.ActivityType
import uk.ryanwong.gmap2ics.domain.models.ActivityType

class DefaultConfig : Config {
override val jsonPath = "./src/commonMain/resources"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi
package uk.ryanwong.gmap2ics.data.datasources.googleapi

import uk.ryanwong.gmap2ics.app.models.timeline.PlaceDetails
import uk.ryanwong.gmap2ics.domain.models.timeline.PlaceDetails

/**
* The idea for setting up this data source, is to encapsulate the implementation details of the network library.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.ktor
package uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor

import uk.ryanwong.gmap2ics.data.models.places.PlaceDetails

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.ktor
package uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor

import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uk.ryanwong.gmap2ics.app.models.timeline.PlaceDetails
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GetPlaceDetailsAPIErrorException
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.except
import uk.ryanwong.gmap2ics.data.repository.impl.PlaceDetailsNotFoundException
import uk.ryanwong.gmap2ics.data.source.googleapi.GetPlaceDetailsAPIErrorException
import uk.ryanwong.gmap2ics.data.source.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.repositories.PlaceDetailsNotFoundException
import uk.ryanwong.gmap2ics.domain.models.timeline.PlaceDetails
import kotlin.coroutines.cancellation.CancellationException

class KtorGoogleApiDataSource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.ktor.impl
package uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor.impl

import io.ktor.client.HttpClient
import io.ktor.client.call.body
Expand All @@ -12,8 +12,8 @@ import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.serialization.kotlinx.json.json
import kotlinx.serialization.json.Json
import uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor.GoogleMapsApiClient
import uk.ryanwong.gmap2ics.data.models.places.PlaceDetails
import uk.ryanwong.gmap2ics.data.source.googleapi.ktor.GoogleMapsApiClient

class GoogleMapsApiClientImpl(engine: HttpClientEngine) : GoogleMapsApiClient {
private val baseUrl = "https://maps.googleapis.com"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.retrofit
package uk.ryanwong.gmap2ics.data.datasources.googleapi.retrofit

import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.retrofit
package uk.ryanwong.gmap2ics.data.datasources.googleapi.retrofit

import org.jetbrains.annotations.Nullable
import retrofit2.Response
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.googleapi.retrofit
package uk.ryanwong.gmap2ics.data.datasources.googleapi.retrofit

import uk.ryanwong.gmap2ics.app.models.timeline.PlaceDetails
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GetPlaceDetailsAPIErrorException
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.except
import uk.ryanwong.gmap2ics.data.repository.impl.PlaceDetailsNotFoundException
import uk.ryanwong.gmap2ics.data.source.googleapi.GetPlaceDetailsAPIErrorException
import uk.ryanwong.gmap2ics.data.source.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.repositories.PlaceDetailsNotFoundException
import uk.ryanwong.gmap2ics.domain.models.timeline.PlaceDetails
import kotlin.coroutines.cancellation.CancellationException

class RetrofitGoogleApiDataSource(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.local
package uk.ryanwong.gmap2ics.data.datasources.local

interface LocalDataSource {
suspend fun getFileList(absolutePath: String, extension: String): Result<List<String>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.source.local.impl
package uk.ryanwong.gmap2ics.data.datasources.local.impl

import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uk.ryanwong.gmap2ics.data.datasources.local.LocalDataSource
import uk.ryanwong.gmap2ics.data.except
import uk.ryanwong.gmap2ics.data.source.local.LocalDataSource
import java.io.File
import java.io.FileWriter
import java.nio.file.Files
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.repository.impl
package uk.ryanwong.gmap2ics.data.repositories

import uk.ryanwong.gmap2ics.app.models.VEvent
import uk.ryanwong.gmap2ics.data.repository.LocalFileRepository
import uk.ryanwong.gmap2ics.data.source.local.LocalDataSource
import uk.ryanwong.gmap2ics.data.source.local.impl.LocalDataSourceImpl
import uk.ryanwong.gmap2ics.data.datasources.local.LocalDataSource
import uk.ryanwong.gmap2ics.data.datasources.local.impl.LocalDataSourceImpl
import uk.ryanwong.gmap2ics.domain.models.VEvent
import uk.ryanwong.gmap2ics.domain.repositories.LocalFileRepository
import java.nio.file.Paths

class LocalFileRepositoryImpl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.repository.impl
package uk.ryanwong.gmap2ics.data.repositories

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import uk.ryanwong.gmap2ics.app.models.timeline.PlaceDetails
import uk.ryanwong.gmap2ics.data.repository.PlaceDetailsRepository
import uk.ryanwong.gmap2ics.data.source.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.domain.models.timeline.PlaceDetails
import uk.ryanwong.gmap2ics.domain.repositories.PlaceDetailsRepository

class PlaceDetailsRepositoryImpl(
private val networkDataSource: GoogleApiDataSource,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
* Copyright (c) 2022-2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.data.repository.impl
package uk.ryanwong.gmap2ics.data.repositories

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import uk.ryanwong.gmap2ics.app.models.timeline.Timeline
import uk.ryanwong.gmap2ics.app.utils.timezonemap.TimeZoneMapWrapper
import uk.ryanwong.gmap2ics.data.datasources.local.LocalDataSource
import uk.ryanwong.gmap2ics.data.datasources.local.impl.LocalDataSourceImpl
import uk.ryanwong.gmap2ics.data.except
import uk.ryanwong.gmap2ics.data.models.timeline.TimelineObjects
import uk.ryanwong.gmap2ics.data.repository.TimelineRepository
import uk.ryanwong.gmap2ics.data.source.local.LocalDataSource
import uk.ryanwong.gmap2ics.data.source.local.impl.LocalDataSourceImpl
import uk.ryanwong.gmap2ics.domain.models.timeline.Timeline
import uk.ryanwong.gmap2ics.domain.repositories.TimelineRepository
import kotlin.coroutines.cancellation.CancellationException

class TimelineRepositoryImpl(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.di

import org.koin.dsl.module
import uk.ryanwong.gmap2ics.app.utils.timezonemap.TimeZoneMapImpl
import uk.ryanwong.gmap2ics.app.utils.timezonemap.TimeZoneMapWrapper
import us.dustinj.timezonemap.TimeZoneMap

val appModule = module {
single<TimeZoneMapWrapper> {
TimeZoneMapImpl(timeZoneMap = TimeZoneMap.forEverywhere())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.di

import io.ktor.client.engine.cio.CIO
import org.koin.core.qualifier.named
import org.koin.dsl.module
import uk.ryanwong.gmap2ics.data.datasources.googleapi.GoogleApiDataSource
import uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor.GoogleMapsApiClient
import uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor.KtorGoogleApiDataSource
import uk.ryanwong.gmap2ics.data.datasources.googleapi.ktor.impl.GoogleMapsApiClientImpl
import uk.ryanwong.gmap2ics.data.datasources.googleapi.retrofit.RetrofitGoogleApiDataSource

val networkModule = module {
single { CIO.create() }
single<GoogleMapsApiClient> { GoogleMapsApiClientImpl(engine = get()) }
single<GoogleApiDataSource>(named("ktor")) { KtorGoogleApiDataSource(googleMapsApiClient = get()) }
single<GoogleApiDataSource>(named("retrofit")) { RetrofitGoogleApiDataSource() }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.di

import kotlinx.serialization.json.Json
import org.koin.core.qualifier.named
import org.koin.dsl.module
import uk.ryanwong.gmap2ics.app.configs.ProvideConfig
import uk.ryanwong.gmap2ics.data.repositories.LocalFileRepositoryImpl
import uk.ryanwong.gmap2ics.data.repositories.PlaceDetailsRepositoryImpl
import uk.ryanwong.gmap2ics.data.repositories.TimelineRepositoryImpl
import uk.ryanwong.gmap2ics.domain.repositories.LocalFileRepository
import uk.ryanwong.gmap2ics.domain.repositories.PlaceDetailsRepository
import uk.ryanwong.gmap2ics.domain.repositories.TimelineRepository

const val USE_KTOR = true

val repositoryModule = module {
single<PlaceDetailsRepository> {
PlaceDetailsRepositoryImpl(
networkDataSource = get(named(if (USE_KTOR) "ktor" else "retrofit")),
placesApiKey = ProvideConfig.getConfig().placesApiKey,
apiLanguageOverride = ProvideConfig.getConfig().apiLanguageOverride,
)
}

single<LocalFileRepository> {
LocalFileRepositoryImpl()
}

single<TimelineRepository> {
TimelineRepositoryImpl(
timeZoneMap = get(),
kotlinJson = Json { ignoreUnknownKeys = true },
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.di

import org.koin.dsl.module
import uk.ryanwong.gmap2ics.domain.usecases.GetActivitySegmentVEventUseCase
import uk.ryanwong.gmap2ics.domain.usecases.GetOutputFilenameUseCase
import uk.ryanwong.gmap2ics.domain.usecases.GetPlaceVisitVEventUseCase
import uk.ryanwong.gmap2ics.domain.usecases.VEventFromActivitySegmentUseCase
import uk.ryanwong.gmap2ics.usecases.GetActivitySegmentVEventUseCaseImpl
import uk.ryanwong.gmap2ics.usecases.GetOutputFilenameUseCaseImpl
import uk.ryanwong.gmap2ics.usecases.GetPlaceVisitVEventUseCaseImpl
import uk.ryanwong.gmap2ics.usecases.VEventFromActivitySegmentUseCaseImpl
import uk.ryanwong.gmap2ics.usecases.VEventFromChildVisitUseCaseImpl
import uk.ryanwong.gmap2ics.usecases.VEventFromPlaceVisitUseCaseImpl

val useCaseModule = module {
factory<GetPlaceVisitVEventUseCase> {
GetPlaceVisitVEventUseCaseImpl(
vEventFromChildVisitUseCase = VEventFromChildVisitUseCaseImpl(placeDetailsRepository = get()),
vEventFromPlaceVisitUseCase = VEventFromPlaceVisitUseCaseImpl(placeDetailsRepository = get()),
)
}

factory<GetOutputFilenameUseCase> {
GetOutputFilenameUseCaseImpl()
}

factory<VEventFromActivitySegmentUseCase> {
VEventFromActivitySegmentUseCaseImpl(placeDetailsRepository = get())
}

factory<GetActivitySegmentVEventUseCase> {
GetActivitySegmentVEventUseCaseImpl(vEventFromActivitySegmentUseCase = get())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright (c) 2024. Ryan Wong (hello@ryanwebmail.com)
*/

package uk.ryanwong.gmap2ics.di

import org.koin.dsl.module
import uk.ryanwong.gmap2ics.app.configs.ProvideConfig
import uk.ryanwong.gmap2ics.ui.viewmodels.MainScreenViewModel
import java.util.Locale
import java.util.ResourceBundle

val viewModelModule = module {
single {
MainScreenViewModel(
configFile = ProvideConfig.getConfig(),
resourceBundle = ResourceBundle.getBundle("resources", Locale.ENGLISH),
timelineRepository = get(),
localFileRepository = get(),
getActivitySegmentVEventUseCase = get(),
getOutputFilenameUseCase = get(),
getPlaceVisitVEventUseCase = get(),
)
}
}
Loading

0 comments on commit 593099d

Please sign in to comment.