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 News screen #16

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 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
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
48 changes: 45 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import java.util.Properties

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

android {
Expand All @@ -19,6 +23,20 @@ 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 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_API_ID", value = alpacaApiId)
buildConfigField(type = "String", name = "ALPACA_API_SECRET", value = alpacaApiSecret)
}

buildTypes {
Expand All @@ -31,6 +49,7 @@ android {
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
Expand All @@ -39,9 +58,10 @@ android {
}
buildFeatures {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.10"
kotlinCompilerExtensionVersion = "1.5.12"
}
packaging {
resources {
Expand All @@ -52,8 +72,10 @@ 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.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
Expand All @@ -72,7 +94,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 +109,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
14 changes: 13 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,17 @@
package dev.pinkroom.marketsight

import android.app.Application
import coil.ImageLoader
import coil.ImageLoaderFactory
import coil.util.DebugLogger
import dagger.hilt.android.HiltAndroidApp

class MarketSightApp: Application()
@HiltAndroidApp
class MarketSightApp: Application(), ImageLoaderFactory{
override fun newImageLoader(): ImageLoader {
return ImageLoader(this).newBuilder()
.logger(DebugLogger())
.build()
}

}
14 changes: 14 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,14 @@
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 BUFFER_LIST = 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()
}
24 changes: 24 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,24 @@
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(jsonValue: String): ErrorMessageDto? {
val helperIdentifier = HelperIdentifierMessagesAlpacaService.Error
fromJson(jsonValue, Array<Any>::class.java).toList().forEach { data ->
toObject(value = data, helperIdentifier = helperIdentifier)?.let {
return it
}
}
return null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package dev.pinkroom.marketsight.common

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 Stocks: HelperIdentifierMessagesAlpacaService<Any>(identifier = "q")
data object Crypto: HelperIdentifierMessagesAlpacaService<Any>(identifier = "t")
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