Skip to content

Commit

Permalink
add widget and fix some stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
iakanoe committed Nov 2, 2023
1 parent fd0d2c6 commit 56cf02a
Show file tree
Hide file tree
Showing 16 changed files with 312 additions and 72 deletions.
9 changes: 9 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

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

5 changes: 4 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ dependencies {
implementation("com.google.dagger:hilt-android:2.48")
kapt("com.google.dagger:hilt-android-compiler:2.48")

implementation("androidx.hilt:hilt-navigation-compose:1.1.0-rc01")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")

implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
Expand All @@ -85,4 +85,7 @@ dependencies {
implementation("androidx.compose.material3:material3")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")

implementation("androidx.glance:glance-appwidget:1.0.0")
implementation("androidx.glance:glance-material3:1.0.0")
}
13 changes: 12 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
android:theme="@style/Theme.Dolarcito"
tools:targetApi="31">

<receiver
android:name=".widget.ExchangeRatesWidgetReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>

<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/exchange_rates_widget_info" />
</receiver>

<activity
android:name=".MainActivity"
android:exported="true"
Expand All @@ -25,7 +37,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@ package io.iakanoe.github.dolarcito.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.scopes.ActivityRetainedScoped
import dagger.hilt.components.SingletonComponent
import io.iakanoe.github.dolarcito.data.ExchangeRateRepository
import io.iakanoe.github.dolarcito.data.SettingsRepository
import io.iakanoe.github.dolarcito.gateway.DolarcitoApiGateway
import io.iakanoe.github.dolarcito.gateway.SettingsGateway

@Module
@InstallIn(ActivityRetainedComponent::class)
@InstallIn(SingletonComponent::class)
object RepositoryModule {

@Provides
@ActivityRetainedScoped
fun providesExchangeRateRepository(
dolarcitoApiGateway: DolarcitoApiGateway
) = ExchangeRateRepository(dolarcitoApiGateway)

@Provides
@ActivityRetainedScoped
fun providesSettingsRepository(
settingsGateway: SettingsGateway
) = SettingsRepository(settingsGateway)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,18 @@ package io.iakanoe.github.dolarcito.di
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.scopes.ActivityRetainedScoped
import dagger.hilt.components.SingletonComponent
import io.iakanoe.github.dolarcito.data.ExchangeRateRepository
import io.iakanoe.github.dolarcito.data.SettingsRepository
import io.iakanoe.github.dolarcito.domain.GetOrderedExchangeRatesUseCase
import io.iakanoe.github.dolarcito.domain.GetSettingsUseCase
import io.iakanoe.github.dolarcito.domain.SaveNewSettingsUseCase

@Module
@InstallIn(ActivityRetainedComponent::class)
@InstallIn(SingletonComponent::class)
object UseCaseModule {

@Provides
@ActivityRetainedScoped
fun provideGetOrderedExchangeRatesUseCase(
settingsRepository: SettingsRepository,
exchangeRateRepository: ExchangeRateRepository
Expand All @@ -26,13 +24,11 @@ object UseCaseModule {
)

@Provides
@ActivityRetainedScoped
fun provideGetSettingsUseCase(
settingsRepository: SettingsRepository
) = GetSettingsUseCase(settingsRepository)

@Provides
@ActivityRetainedScoped
fun provideSaveNewSettingsUseCase(
settingsRepository: SettingsRepository
) = SaveNewSettingsUseCase(settingsRepository)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,40 +1,37 @@
package io.iakanoe.github.dolarcito.domain

import android.util.Log
import io.iakanoe.github.dolarcito.data.ExchangeRateRepository
import io.iakanoe.github.dolarcito.data.SettingsRepository
import io.iakanoe.github.dolarcito.model.ExchangeRate
import io.iakanoe.github.dolarcito.model.ExchangeRateOrder
import io.iakanoe.github.dolarcito.model.Settings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach

class GetOrderedExchangeRatesUseCase(
private val settingsRepository: SettingsRepository,
private val exchangeRateRepository: ExchangeRateRepository
) {
suspend fun execute(): Flow<ExchangeRateOrder> {
val existingRates = exchangeRateRepository.getExchangeRates()
fun execute(): Flow<ExchangeRateOrder> {
val existingRates = flow { emit(exchangeRateRepository.getExchangeRates()) }

val actualSettings = settingsRepository.getSettings()
.map { settings ->
.combine(existingRates) { a, b -> a to b }
.map { (settings, existingRates) ->
val updated = settings.update(existingRates.map { it.name })

Log.d("UseCase", "map\nsettings=$settings\nupdated=$updated")
if (settings != updated) settingsRepository.setSettings(updated)
updated
updated to existingRates
}
.onEach { Log.d("onEach", "ONEACH 1 $it") }
.map { settings ->
.map { (settings, existingRates) ->
ExchangeRateOrder(
showing = settings.showingRatesNames
.map { exchangeRateByName(existingRates, it) },
hidden = settings.hiddenRatesNames
.map { exchangeRateByName(existingRates, it) }
)
}
.onEach { Log.d("onEach", "ONEACH $it") }

return actualSettings
}
Expand Down
28 changes: 28 additions & 0 deletions app/src/main/java/io/iakanoe/github/dolarcito/ui/common/Compose.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.iakanoe.github.dolarcito.ui.common

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import java.text.DecimalFormat
import java.util.Calendar

val Int.minutesText
get() = when (this) {
0 -> "hace menos de un minuto"
1 -> "hace un minuto"
else -> "hace $this minutos"
}

val Float.priceText: String
get() = DecimalFormat("$#.##")
.format(this)

val Long.largeNumberText: String
get() = DecimalFormat("#,###")
.format(this)

val currentTime = flow {
while (true) {
delay(1000)
emit(Calendar.getInstance().timeInMillis)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.WindowCompat
import androidx.glance.GlanceTheme
import androidx.glance.material3.ColorProviders

private val dolarcitoDarkColorScheme = darkColorScheme(
primary = Purple80,
Expand Down Expand Up @@ -57,4 +59,15 @@ fun DolarcitoTheme(
typography = Typography,
content = content
)
}

@Composable
fun DolarcitoGlanceTheme(content: @Composable () -> Unit) {
GlanceTheme(
colors = ColorProviders(
light = dolarcitoLightColorScheme,
dark = dolarcitoDarkColorScheme
),
content = content
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import io.iakanoe.github.dolarcito.model.ExchangeRate
import io.iakanoe.github.dolarcito.ui.TopAppBarState
import java.text.DecimalFormat
import io.iakanoe.github.dolarcito.ui.common.currentTime
import io.iakanoe.github.dolarcito.ui.common.largeNumberText
import io.iakanoe.github.dolarcito.ui.common.minutesText
import io.iakanoe.github.dolarcito.ui.common.priceText
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
Expand All @@ -51,21 +54,6 @@ import java.util.Calendar
import kotlin.math.floor
import kotlin.math.sign

val Int.minutesText
get() = when (this) {
0 -> "hace menos de un minuto"
1 -> "hace un minuto"
else -> "hace $this minutos"
}

val Float.priceText: String
get() = DecimalFormat("$#.##")
.format(this)

val Long.largeNumberText: String
get() = DecimalFormat("#,###")
.format(this)

@Composable
fun ExchangeRatesScreen(
onNavigateToSettings: () -> Unit,
Expand All @@ -74,13 +62,15 @@ fun ExchangeRatesScreen(
) {
val viewState by viewModel.viewState.collectAsState()

val now by currentTime.collectAsState(initial = Calendar.getInstance().timeInMillis)

val lastUpdated by remember {
derivedStateOf {
viewState.let {
if (it is ExchangeRatesViewState.Loaded) {
val millis = Calendar.getInstance().timeInMillis - it.updatedTime
val millis = now - it.updatedTime
val minutes = floor(millis / 60000f)
minutes.toInt()
minutes.toInt().coerceAtLeast(0)
} else null
}
}
Expand Down Expand Up @@ -197,9 +187,11 @@ fun ExchangeRateCard(exchangeRate: ExchangeRate) {
else -> MaterialTheme.colorScheme.outline
}

val now by currentTime.collectAsState(initial = Calendar.getInstance().timeInMillis)

val lastUpdated by remember {
derivedStateOf {
val millis = Calendar.getInstance().timeInMillis - exchangeRate.timestamp
val millis = now - exchangeRate.timestamp
val minutes = floor(millis / 60000f)
minutes.toInt()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package io.iakanoe.github.dolarcito.ui.rates

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import io.iakanoe.github.dolarcito.domain.GetOrderedExchangeRatesUseCase
import io.iakanoe.github.dolarcito.model.ExchangeRate
import io.iakanoe.github.dolarcito.model.ExchangeRateOrder
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
import java.util.Calendar
import javax.inject.Inject
Expand All @@ -31,32 +33,28 @@ class ExchangeRatesViewModel @Inject constructor(
}

fun update() {
waitingJob?.cancel()
viewModelScope.launch {
waitingJob?.cancel()
_viewState.value = ExchangeRatesViewState.Loading

val order = runCatching { getOrderedExchangeRatesUseCase.execute() }
.onFailure {
getOrderedExchangeRatesUseCase.execute()
.map<ExchangeRateOrder, ExchangeRatesViewState> {
ExchangeRatesViewState.Loaded(
exchangeRates = it.showing,
hiddenExchangeRates = it.hidden,
updatedTime = Calendar.getInstance().timeInMillis
)
}
.onStart { emit(ExchangeRatesViewState.Loading) }
.catch {
it.printStackTrace()
_viewState.value = ExchangeRatesViewState.Error
emit(ExchangeRatesViewState.Error)
}
.getOrDefault(emptyFlow())
.firstOrNull()

order?.let {
Log.d("ExchangeRates", "loaded: \nshowing=${it.showing}\nhidden=${it.hidden}")

_viewState.value = ExchangeRatesViewState.Loaded(
exchangeRates = it.showing,
hiddenExchangeRates = it.hidden,
updatedTime = Calendar.getInstance().timeInMillis
)
}

waitingJob = launch {
delay(5 * 60000L)
update()
}
.onCompletion {
waitingJob = launch {
delay(5 * 60000L)
update()
}
}
.collect { _viewState.emit(it) }
}
}
}
Expand Down
Loading

0 comments on commit 56cf02a

Please sign in to comment.