Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/detail screen #19

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8d43411
base navigation
LucasPrioste92 Apr 5, 2024
673aeb2
Merge branch 'feature/splash-screen' into feature/navigation
LucasPrioste92 Apr 5, 2024
907f2ce
update colors background and add scaffold to support bottom bar
LucasPrioste92 Apr 8, 2024
14c67a3
bottom bar
LucasPrioste92 Apr 8, 2024
388f4f2
route with arg symbolId on Detail screen
LucasPrioste92 Apr 8, 2024
398453d
initial setup alpaca API
LucasPrioste92 Apr 8, 2024
3f5ed1f
update dimensions
LucasPrioste92 Apr 8, 2024
51bce36
Merge branch 'feature/navigation' into feature/alpaca-api
LucasPrioste92 Apr 8, 2024
31d797e
update alpaca news service
LucasPrioste92 Apr 10, 2024
739ee01
- refactor alpaca news ws service
LucasPrioste92 Apr 11, 2024
201b4b2
- alpaca news API
LucasPrioste92 Apr 11, 2024
b539b0c
- alpaca news API
LucasPrioste92 Apr 11, 2024
2798774
- setup Unit Test libs
LucasPrioste92 Apr 11, 2024
d26ca4f
- faker dependency
LucasPrioste92 Apr 12, 2024
2b44d41
- unit test (NewsRepository)
LucasPrioste92 Apr 12, 2024
734e555
- unit test (ChangeFilterNews UseCase)
LucasPrioste92 Apr 12, 2024
e487af3
- main carousel news
LucasPrioste92 Apr 17, 2024
84360dd
- main carousel news add anim
LucasPrioste92 Apr 18, 2024
aac9174
- show all news
LucasPrioste92 Apr 19, 2024
0b8c085
- real time news
LucasPrioste92 Apr 22, 2024
80c43b2
- pagination news
LucasPrioste92 Apr 22, 2024
dd409ca
- date picker ui
LucasPrioste92 Apr 24, 2024
3f639fa
- logic to apply filters on VM
LucasPrioste92 Apr 26, 2024
9a765df
- unit test
LucasPrioste92 Apr 29, 2024
fa5c993
- update libs
LucasPrioste92 Apr 29, 2024
232d958
- update symbol crypto
LucasPrioste92 Apr 29, 2024
660586c
- assets API
LucasPrioste92 May 2, 2024
9d0225c
- refactor alpaca data API
LucasPrioste92 May 2, 2024
92f6b79
- trades, quotes, bars info
LucasPrioste92 May 2, 2024
faaf727
- use case to get realtime tardes, quotes and bar
LucasPrioste92 May 6, 2024
0616d85
- unit test MarketRemoteDataSource and AssetRemoteDataSource
LucasPrioste92 May 6, 2024
b6235b6
- unit test AssetsRepository
LucasPrioste92 May 6, 2024
107ed0d
- search input assets
LucasPrioste92 May 8, 2024
5f26ec9
- handle error assets list
LucasPrioste92 May 13, 2024
cda4490
- ui test HomeViewModel
LucasPrioste92 May 13, 2024
378bf20
- header detail screen
LucasPrioste92 May 13, 2024
972655e
- base chart
LucasPrioste92 May 15, 2024
c12e93a
- new version chart
LucasPrioste92 May 20, 2024
40285a7
- update chart
LucasPrioste92 May 21, 2024
9c475d8
- update chart
LucasPrioste92 May 23, 2024
14785b8
- get real time quotes
LucasPrioste92 May 24, 2024
2b2ab02
- get trades
LucasPrioste92 May 28, 2024
2312385
- enabled pull to refresh
LucasPrioste92 May 28, 2024
68d5979
- chart just consume horizontal dragging
LucasPrioste92 May 28, 2024
5253271
- unit test DetailViewModel
LucasPrioste92 May 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import java.util.Properties

plugins {
kotlin("kapt")
alias(libs.plugins.androidApplication)
alias(libs.plugins.jetbrainsKotlinAndroid)
alias(libs.plugins.hiltAndroid)
alias(libs.plugins.jsonSerialization)
id("kotlin-parcelize")
}

android {
Expand All @@ -19,6 +24,22 @@ android {
vectorDrawables {
useSupportLibrary = true
}

val keystoreFile = project.rootProject.file("local.properties")
val properties = Properties()
properties.load(keystoreFile.inputStream())

val alpacaStreamUrl = properties.getProperty("ALPACA_STREAM_URL") ?: ""
val alpacaDataUrl = properties.getProperty("ALPACA_DATA_URL") ?: ""
val alpacaPaperUrl = properties.getProperty("ALPACA_PAPER_URL") ?: ""
val alpacaApiId = properties.getProperty("ALPACA_API_ID") ?: ""
val alpacaApiSecret = properties.getProperty("ALPACA_API_SECRET") ?: ""

buildConfigField(type = "String", name = "ALPACA_STREAM_URL", value = alpacaStreamUrl)
buildConfigField(type = "String", name = "ALPACA_DATA_URL", value = alpacaDataUrl)
buildConfigField(type = "String", name = "ALPACA_PAPER_URL", value = alpacaPaperUrl)
buildConfigField(type = "String", name = "ALPACA_API_ID", value = alpacaApiId)
buildConfigField(type = "String", name = "ALPACA_API_SECRET", value = alpacaApiSecret)
}

buildTypes {
Expand All @@ -31,6 +52,7 @@ android {
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
Expand All @@ -39,9 +61,10 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.10"
kotlinCompilerExtensionVersion = "1.5.12"
}
packaging {
resources {
Expand All @@ -52,8 +75,11 @@ android {

dependencies {
// CORE
coreLibraryDesugaring(libs.androidx.desugar)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.process)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
Expand All @@ -72,7 +98,12 @@ dependencies {
implementation(libs.okhttp.interceptor)

// SCARLET
implementation(libs.scarlet)
implementation(libs.scarlet.core)
implementation(libs.scarlet.stream)
implementation(libs.scarlet.gson)
implementation(libs.scarlet.okhttp)
implementation(libs.scarlet.lifecycle)
implementation(libs.scarlet.moshi)

// DAGGER HILT
kapt(libs.hilt.compiler)
Expand All @@ -82,9 +113,24 @@ dependencies {
// SPLASH
implementation(libs.splash)

// PAGING
implementation(libs.paging.runtime)
implementation(libs.paging.compose)

// COIL
implementation(libs.coil)

// JSON
implementation(libs.json)

// UNIT TEST
testImplementation(libs.junit)
testImplementation(libs.junit.api)
testImplementation(libs.mockk.android)
testImplementation(libs.mockk.agent)
testImplementation(libs.coroutine.test)
testImplementation(libs.assertk)
testImplementation(libs.faker)
testImplementation(libs.turbine)

// END-TO-END TEST
androidTestImplementation(libs.androidx.junit)
Expand Down
5 changes: 4 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
<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:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
Expand All @@ -10,11 +12,12 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.MarketSight.Splash"
tools:targetApi="34"
android:name=".MarketSightApp">
<activity
android:name=".ui.MainActivity"
android:name=".presentation.MainActivity"
android:exported="true"
android:theme="@style/Theme.MarketSight.Splash">
<intent-filter>
Expand Down
19 changes: 18 additions & 1 deletion app/src/main/java/dev/pinkroom/marketsight/MarketSightApp.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
package dev.pinkroom.marketsight

import android.app.Application
import androidx.lifecycle.ProcessLifecycleOwner
import dagger.hilt.android.HiltAndroidApp
import dev.pinkroom.marketsight.common.WebSocketLifecycle
import javax.inject.Inject

class MarketSightApp: Application()
@HiltAndroidApp
class MarketSightApp: Application() {
@Inject
internal lateinit var lifecycle: WebSocketLifecycle

override fun onCreate() {
super.onCreate()
initWebSocketLifecycleObserver()
}

private fun initWebSocketLifecycleObserver() {
ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycle)
}
}
18 changes: 18 additions & 0 deletions app/src/main/java/dev/pinkroom/marketsight/common/Constants.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.pinkroom.marketsight.common

object Constants {
// ANIM TIME
const val ANIM_TIME_CAROUSEL = 700

// LIMIT
const val MAX_ITEMS_CAROUSEL = 5
const val LIMIT_NEWS = 20
const val DEFAULT_LIMIT_ASSET = 1000
const val DEFAULT_LIMIT_QUOTES_ASSET = 30
const val DEFAULT_LIMIT_TRADES_ASSET = 30
const val BUFFER_LIST = 5
const val LIMIT_Y_INFO_CHART = 5

// TEXT
const val ALL_SYMBOLS = "All"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.pinkroom.marketsight.common

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers

interface DispatcherProvider {
val Main: CoroutineDispatcher
val IO: CoroutineDispatcher
val Default: CoroutineDispatcher
}
class DefaultDispatchers: DispatcherProvider {
override val Main: CoroutineDispatcher
get() = Dispatchers.Main
override val IO: CoroutineDispatcher
get() = Dispatchers.IO
override val Default: CoroutineDispatcher
get() = Dispatchers.Default
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.pinkroom.marketsight.common

import com.tinder.scarlet.Stream
import com.tinder.scarlet.StreamAdapter
import com.tinder.scarlet.utils.getRawType
import kotlinx.coroutines.flow.Flow
import java.lang.reflect.Type
import kotlinx.coroutines.reactive.asFlow

class FlowStreamAdapterFactory : StreamAdapter.Factory {

override fun create(type: Type): StreamAdapter<Any, Any> {
return when (type.getRawType()) {
Flow::class.java -> FlowStreamAdapter()
else -> throw IllegalArgumentException()
}
}
}

private class FlowStreamAdapter<T> : StreamAdapter<T, Flow<T>> where T : Any {
override fun adapt(stream: Stream<T>) = stream.asFlow()
}
22 changes: 22 additions & 0 deletions app/src/main/java/dev/pinkroom/marketsight/common/GsonUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.pinkroom.marketsight.common

import com.google.gson.Gson
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_news_service.ErrorMessageDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_news_service.TypeMessageDto

fun <T> Gson.toJsonValue(value: Any?, classOfT: Class<T>): T = fromJson(toJson(value), classOfT)

fun <T> Gson.toObject(value: Any, helperIdentifier: HelperIdentifierMessagesAlpacaService<T>): T? {
val typeMessage = toJsonValue(value, TypeMessageDto::class.java)
return if (typeMessage.value == helperIdentifier.identifier && helperIdentifier.classOfT != null){
toJsonValue(value, helperIdentifier.classOfT)
} else null
}

fun Gson.verifyIfIsError(value: Any): ErrorMessageDto? {
val helperIdentifier = HelperIdentifierMessagesAlpacaService.Error
toObject(value = value, helperIdentifier = helperIdentifier)?.let {
return it
}
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package dev.pinkroom.marketsight.common

import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_api.BarAssetDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_api.QuoteAssetDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_api.TradeAssetDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_news_service.ErrorMessageDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_news_service.NewsMessageDto
import dev.pinkroom.marketsight.data.remote.model.dto.alpaca_news_service.SubscriptionMessageDto

sealed class HelperIdentifierMessagesAlpacaService<T>(
val identifier: String,
val classOfT: Class<T>? = null,
){
data object News: HelperIdentifierMessagesAlpacaService<NewsMessageDto>(identifier = "n", classOfT = NewsMessageDto::class.java)
data object Trades: HelperIdentifierMessagesAlpacaService<TradeAssetDto>(identifier = "t", classOfT = TradeAssetDto::class.java)
data object Quotes: HelperIdentifierMessagesAlpacaService<QuoteAssetDto>(identifier = "q", classOfT = QuoteAssetDto::class.java)
data object Bars: HelperIdentifierMessagesAlpacaService<BarAssetDto>(identifier = "b", classOfT = BarAssetDto::class.java)
data object Success: HelperIdentifierMessagesAlpacaService<Any>(identifier = "success")
data object Error: HelperIdentifierMessagesAlpacaService<ErrorMessageDto>(identifier = "error", classOfT = ErrorMessageDto::class.java)
data object Subscription: HelperIdentifierMessagesAlpacaService<SubscriptionMessageDto>(identifier = "subscription", classOfT = SubscriptionMessageDto::class.java)
}
12 changes: 12 additions & 0 deletions app/src/main/java/dev/pinkroom/marketsight/common/Resource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.pinkroom.marketsight.common

import dev.pinkroom.marketsight.domain.model.common.ErrorMessage

sealed class Resource<T>{
data class Success<T>(val data: T): Resource<T>()
data class Error<T>(
val message: String? = null,
val errorInfo: ErrorMessage? = null,
val data: T? = null
): Resource<T>()
}
Loading