Skip to content

Commit

Permalink
Merge pull request #1 from LPeteR90/develop
Browse files Browse the repository at this point in the history
0.0.1
  • Loading branch information
greyhairredbear authored Oct 12, 2021
2 parents e1ea8d6 + 659985e commit a416123
Show file tree
Hide file tree
Showing 72 changed files with 1,104 additions and 301 deletions.
3 changes: 0 additions & 3 deletions .idea/.gitignore

This file was deleted.

6 changes: 0 additions & 6 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/codeStyles/codeStyleConfig.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion .idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .idea/deploymentTargetDropDown.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions .idea/inspectionProfiles/Project_Default.xml

This file was deleted.

11 changes: 3 additions & 8 deletions .idea/jarRepositories.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions .idea/kotlinScripting.xml

This file was deleted.

11 changes: 11 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# android-template
# RaCooin

Template for Android Projects.

# TODO

RaCooin (a mix of **Ra**te, **Coin** and the cute animal)
is a minimalistic app for tracking the current fiat money value of one's crypto currencies.

# TODO
- Add CI Workflow to publish to google play
- Add Jetpack Compose once stable
1 change: 1 addition & 0 deletions apiclient/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
32 changes: 32 additions & 0 deletions apiclient/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
plugins {
id(Plugins.LIB_JAVA)
id(Plugins.LIB_KOTLIN)
kotlin(Plugins.SERIALIZATION) version BuildPluginsVersions.KOTLIN
}

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

dependencies {
implementation(project(mapOf("path" to ":core")))

implementation(Core.KOTLINX_COROUTINES) // TODO: remove?

implementation(Utils.KOTLIN_SERIALIZATION)

implementation(Server.KTOR_CLIENT_CORE)
implementation(Server.KTOR_CLIENT_ANDROID)
implementation(Server.KTOR_CLIENT_SERIALIZATION)
implementation(Server.KTOR_CLIENT_LOGGING)
implementation(Server.LOGBACK)

testImplementation(Testing.KOTEST_RUNNER)
testImplementation(Testing.KOTEST_JUNIT_RUNNER)
testImplementation(Testing.KOTEST_ASSERTIONS)
testImplementation(Testing.KOTEST_EXTENSIONS_ARROW)
testImplementation(Testing.KOTEST_PROPERTIES)
testImplementation(Testing.MOCKK)
testImplementation(Testing.KOTLINX_COROUTINES_TEST)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.greyhairredbear.racooin.apiclient

import arrow.core.Either
import com.greyhairredbear.racooin.core.interfaces.ApiClient
import com.greyhairredbear.racooin.core.model.ApiCallFailed
import com.greyhairredbear.racooin.core.model.ApiClientError
import com.greyhairredbear.racooin.core.model.CryptoCurrency
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.core.model.FiatBalance
import com.greyhairredbear.racooin.core.model.FiatCurrency
import io.ktor.client.HttpClient
import io.ktor.client.features.defaultRequest
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.DEFAULT
import io.ktor.client.features.logging.LogLevel
import io.ktor.client.features.logging.Logger
import io.ktor.client.features.logging.Logging
import io.ktor.client.request.accept
import io.ktor.client.request.get
import io.ktor.client.request.host
import io.ktor.client.request.parameter
import io.ktor.http.ContentType
import io.ktor.http.URLProtocol
import kotlinx.serialization.Serializable

@Serializable
data class CurrencyRatesResponse(
val bitcoin: FiatBalanceResponse,
val ethereum: FiatBalanceResponse,
val dogecoin: FiatBalanceResponse,
val litecoin: FiatBalanceResponse,
val ripple: FiatBalanceResponse,
) {
fun toCurrencyRates(): List<CryptoCurrencyRate> =
mapOf(
CryptoCurrency.BITCOIN to bitcoin,
CryptoCurrency.ETHEREUM to ethereum,
CryptoCurrency.DOGECOIN to dogecoin,
CryptoCurrency.LITECOIN to litecoin,
CryptoCurrency.RIPPLE to ripple,
).flatMap {
listOf(
CryptoCurrencyRate(it.key, FiatBalance(FiatCurrency.EURO, it.value.eur)),
CryptoCurrencyRate(it.key, FiatBalance(FiatCurrency.US_DOLLAR, it.value.usd)),
)
}
}

@Serializable
data class FiatBalanceResponse(val eur: Double, val usd: Double)

// TODO extract client setup etc
class CoingeckoApiClient : ApiClient {
private val client = HttpClient {

install(Logging) {
logger = Logger.DEFAULT
level = LogLevel.ALL
}

install(JsonFeature) {
serializer = KotlinxSerializer()
}

defaultRequest {
host = "api.coingecko.com/api/v3/simple"
url {
protocol = URLProtocol.HTTPS
}
accept(ContentType.Application.Json)
}

}

override suspend fun fetchCurrencyRates(): Either<ApiClientError, List<CryptoCurrencyRate>> =
Either.catch {
val result = client.get<CurrencyRatesResponse> {
url { encodedPath = "/price" }
parameter("ids", "ethereum,bitcoin,dogecoin,litecoin,ripple")
parameter("vs_currencies", "eur,usd")
}
result.toCurrencyRates()
}.mapLeft { ApiCallFailed }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.greyhairredbear.racooin.apiclient

import com.greyhairredbear.racooin.core.model.CryptoCurrency
import com.greyhairredbear.racooin.core.model.CryptoCurrencyRate
import com.greyhairredbear.racooin.core.model.FiatCurrency
import io.kotest.assertions.arrow.core.shouldBeRight
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldContainAll
import io.kotest.matchers.collections.shouldHaveSize

val sut = CoingeckoApiClient()

class ApiClientTest : FunSpec({
test("should fetch currencies from api") {
val result = sut.fetchCurrencyRates()
.shouldBeRight()

result.shouldHaveSize(10)
result.shouldContainAllCurrencyCombinations()
}
})

private fun List<CryptoCurrencyRate>.shouldContainAllCurrencyCombinations() {
val expectedCombinations = CryptoCurrency.values()
.flatMap { crypto -> FiatCurrency.values().map { crypto to it } }

currencyCombinations().shouldContainAll(expectedCombinations)
}

private fun List<CryptoCurrencyRate>.currencyCombinations() =
map { it.cryptoCurrency to it.fiatBalance.fiatCurrency }
56 changes: 46 additions & 10 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import com.google.protobuf.gradle.generateProtoTasks
import com.google.protobuf.gradle.plugins
import com.google.protobuf.gradle.protobuf
import com.google.protobuf.gradle.protoc

plugins {
kotlin(Plugins.KAPT)
id(Plugins.ANDROID_APPLICATION)
id(Plugins.KOTLIN_ANDROID)
id(Plugins.PROTOBUF) version BuildPluginsVersions.PROTOBUF
id(Plugins.HILT)
}

val installGitHooks by rootProject.tasks.existing
Expand Down Expand Up @@ -39,7 +47,6 @@ android {
}

composeOptions {
kotlinCompilerVersion = BuildPluginsVersions.KOTLIN
kotlinCompilerExtensionVersion = Versions.COMPOSE_VERSION
}

Expand All @@ -50,26 +57,32 @@ android {
}

dependencies {
implementation(Core.STD_LIB)
implementation(Core.KOTLINX_COROUTINES)
implementation(project(mapOf("path" to ":core")))
implementation(project(mapOf("path" to ":apiclient")))

implementation(Server.KTOR_CLIENT_CORE)
implementation(Server.KTOR_CLIENT_ANDROID)
implementation(Server.KTOR_CLIENT_SERIALIATION)
implementation(Core.KOTLINX_COROUTINES)

implementation(Compose.COMPOSE_UI)
implementation(Compose.COMPOSE_MATERIAL)
implementation(Compose.COMPOSE_UI_TOOLING_PREVIEW)
implementation(Compose.COMPOSE_FOUNDATION)
implementation(Android.ANDROIDX_LIFECYCLE_VIEWMODEL_COMPOSE)

implementation(Protobuf.PROTOBUF_JAVA_LITE)
implementation(Android.ANDROIDX_DATASTORE)

implementation(SupportLibs.ANDROIDX_APPCOMPAT)
implementation(SupportLibs.ANDROIDX_CORE_KTX)
implementation(SupportLibs.ANDROIDX_ACTIVITY)
implementation(Android.ANDROIDX_APPCOMPAT)
implementation(Android.ANDROIDX_CORE_KTX)
implementation(Android.ANDROIDX_ACTIVITY)
implementation (GoogleLibs.ANDROID_MATERIAL)

implementation(Android.HILT_ANDROID)
kapt(Android.HILT_ANDROID_COMPILER)

testImplementation(Testing.KOTEST_RUNNER)
testImplementation(Testing.KOTEST_JUNIT_RUNNER)
testImplementation(Testing.KOTEST_ASSERTIONS)
testImplementation(Testing.KOTEST_EXTENSIONS_ARROW)
testImplementation(Testing.KOTEST_PROPERTIES)
testImplementation(Testing.MOCKK)
testImplementation(Testing.KOTLINX_COROUTINES_TEST)
Expand All @@ -80,4 +93,27 @@ dependencies {
androidTestImplementation(AndroidTesting.ESPRESSO_CORE)

debugImplementation(Compose.COMPOSE_UI_TOOLING)
}
}

kapt {
correctErrorTypes = true
}

protobuf {
protoc {
artifact = Protobuf.PROTOBUF_PROTOC
}

// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().forEach { task ->
task.plugins {
create("java") {
option("lite")
}
}
}
}
}
Loading

0 comments on commit a416123

Please # to comment.