Skip to content

Commit

Permalink
Fix blank widget issues
Browse files Browse the repository at this point in the history
  • Loading branch information
StylingAndroid committed Jan 23, 2021
1 parent 0d76376 commit 01191ec
Show file tree
Hide file tree
Showing 20 changed files with 142 additions and 64 deletions.
2 changes: 1 addition & 1 deletion LICENSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2020 Mark Allison
Copyright 2020-2021 Mark Allison

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package="com.stylingandroid.currencyconverter">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<application
android:name=".CurrencyConverterApplication"
Expand Down Expand Up @@ -37,6 +38,7 @@
android:name="android.appwidget.provider"
android:resource="@xml/currency_appwidget_info" />
</receiver>

</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@ package com.stylingandroid.currencyconverter.core

import com.stylingandroid.currencyconverter.domain.Balance
import com.stylingandroid.currencyconverter.domain.ExchangeRate
import java.time.Instant

interface CurrencyPersistence {

suspend fun saveExchangeRates(exchangeRate: ExchangeRate)

suspend fun loadExchangeRates(): ExchangeRate?

suspend fun purgeExchangeRates(expiry: Instant)
suspend fun purgeExchangeRates()

suspend fun saveBalance(balance: Balance)

suspend fun loadBalance(): Balance?

suspend fun purgeBalance(expiry: Instant)
suspend fun purgeBalance()
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import java.util.Currency

interface CurrencyProvider {

suspend fun exchangeRates(from: Currency, to: Currency): ExchangeRate
suspend fun exchangeRates(from: Currency, to: Currency): ExchangeRate?

suspend fun balance(currency: Currency): Balance
suspend fun balance(currency: Currency): Balance?
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import java.util.Currency

data class Balance(
val currency: Currency,
val timestamp: Instant,
override val timestamp: Instant,
val balance: BigDecimal
)
) : Timestamped(timestamp)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ data class ExchangeRate(
val from: Currency,
val to: Currency,
val date: LocalDateTime,
val timestamp: Instant,
override val timestamp: Instant,
val rate: BigDecimal
)
) : Timestamped(timestamp)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.stylingandroid.currencyconverter.domain

import java.time.Instant

open class Timestamped(
open val timestamp: Instant
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,32 @@ class TransferwiseProvider @Inject constructor(
private val service: TransferwiseApiService
) : CurrencyProvider {

override suspend fun exchangeRates(from: Currency, to: Currency): ExchangeRate =
service.rates(from.currencyCode, to.currencyCode).first().toDomainObject()
@Suppress("TooGenericExceptionCaught")
override suspend fun exchangeRates(from: Currency, to: Currency): ExchangeRate? =
try {
service.rates(from.currencyCode, to.currencyCode).first().toDomainObject()
} catch (throwable: Throwable) {
null
}

override suspend fun balance(currency: Currency): Balance {
return service.balances(BuildConfig.TRANSFERWISE_PROFILE_ID)
.first()
.balances
.filter {
it.currency == currency.currencyCode
}
.map {
Balance(
currency = currency,
timestamp = Instant.now(),
balance = it.amount.value
)
}
.first()
}
@Suppress("TooGenericExceptionCaught")
override suspend fun balance(currency: Currency): Balance? =
try {
service.balances(BuildConfig.TRANSFERWISE_PROFILE_ID)
.first()
.balances
.filter {
it.currency == currency.currencyCode
}
.map {
Balance(
currency = currency,
timestamp = Instant.now(),
balance = it.amount.value
)
}
.first()
} catch (throwable: Throwable) {
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ interface CurrencyDao {
@Query("DELETE FROM ExchangeRateEntity")
fun deleteAllExchangeRates()

@Query("DELETE FROM ExchangeRateEntity WHERE timestamp <= :expiry")
fun purgeExchangeRates(expiry: Long)

@Query("SELECT * from BalanceEntity")
fun getBalances(): List<BalanceEntity>

Expand All @@ -28,7 +25,4 @@ interface CurrencyDao {

@Query("DELETE FROM BalanceEntity")
fun deleteAllEBalances()

@Query("DELETE FROM BalanceEntity WHERE timestamp <= :expiry")
fun purgeBalance(expiry: Long)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.stylingandroid.currencyconverter.persistence
import com.stylingandroid.currencyconverter.core.CurrencyPersistence
import com.stylingandroid.currencyconverter.domain.Balance
import com.stylingandroid.currencyconverter.domain.ExchangeRate
import java.time.Instant
import javax.inject.Inject

class RoomCurrencyPersistence @Inject constructor(
Expand All @@ -21,8 +20,8 @@ class RoomCurrencyPersistence @Inject constructor(
override suspend fun loadExchangeRates(): ExchangeRate? =
dao.getExchangeRates().firstOrNull()?.toDomainObject()

override suspend fun purgeExchangeRates(expiry: Instant) {
dao.purgeExchangeRates(expiry.toEpochMilli())
override suspend fun purgeExchangeRates() {
dao.deleteAllExchangeRates()
}

override suspend fun saveBalance(balance: Balance) {
Expand All @@ -33,7 +32,7 @@ class RoomCurrencyPersistence @Inject constructor(
override suspend fun loadBalance(): Balance? =
dao.getBalances().firstOrNull()?.toDomainObject()

override suspend fun purgeBalance(expiry: Instant) {
dao.purgeBalance(expiry.toEpochMilli())
override suspend fun purgeBalance() {
dao.deleteAllEBalances()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import com.stylingandroid.currencyconverter.core.CurrencyPersistence
import com.stylingandroid.currencyconverter.core.CurrencyProvider
import com.stylingandroid.currencyconverter.domain.Balance
import com.stylingandroid.currencyconverter.domain.ExchangeRate
import com.stylingandroid.currencyconverter.domain.Timestamped
import java.time.Duration
import java.time.Instant
import java.util.Currency
import javax.inject.Inject
import javax.inject.Named
import timber.log.Timber

class CurrencyRepository @Inject constructor(
private val persistence: CurrencyPersistence,
Expand All @@ -18,17 +20,50 @@ class CurrencyRepository @Inject constructor(
@Named(ApplicationModule.TO_CURRENCY) private val to: Currency
) {

suspend fun exchangeRates(validity: Duration): ExchangeRate {
persistence.purgeExchangeRates(Instant.now().minus(validity))
return persistence.loadExchangeRates() ?: provider.exchangeRates(from, to).also {
persistence.saveExchangeRates(it)
suspend fun exchangeRates(validity: Duration): ExchangeRate? {
val persisted = persistence.loadExchangeRates()
return loadAndPurge(
value = persisted,
validity = validity,
purger = persistence::purgeExchangeRates,
saver = persistence::saveExchangeRates
) {
provider.exchangeRates(from, to)
}
}

suspend fun balance(validity: Duration): Balance {
persistence.purgeBalance(Instant.now().minus(validity))
return persistence.loadBalance() ?: provider.balance(from).also {
persistence.saveBalance(it)
suspend fun balance(validity: Duration): Balance? {
val persisted = persistence.loadBalance()
return loadAndPurge(
value = persisted,
validity = validity,
purger = persistence::purgeBalance,
saver = persistence::saveBalance
) {
provider.balance(from)
}
}

private suspend fun <T : Timestamped> loadAndPurge(
value: T?,
validity: Duration,
purger: suspend () -> Unit,
saver: suspend (T) -> Unit,
retriever: suspend () -> T?
): T? {
return if (value?.timestamp?.isAfter(Instant.now().minus(validity)) == true) {
value
} else {
retriever()?.also { newValue ->
purger()

saver(newValue)
} ?: run {
Timber
.tag("CurrencyRepository")
.d("Retrieve failed, so using expired, persisted value")
value
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,29 @@ package com.stylingandroid.currencyconverter.ui

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.stylingandroid.currencyconverter.R
import com.stylingandroid.currencyconverter.repository.CurrencyRepository
import com.stylingandroid.currencyconverter.databinding.ActivityMainBinding
import com.stylingandroid.currencyconverter.update.UpdateScheduler
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import timber.log.Timber

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

@Inject
lateinit var repository: CurrencyRepository
lateinit var updateScheduler: UpdateScheduler

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}

companion object {
const val INTERVAL = 15L
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

binding.updateButton.setOnClickListener {
Timber.d("Button clicked")
updateScheduler.enqueueOneTimeWorker()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ class CurrencyAppWidgetProvider : AppWidgetProvider() {
balance = intent.getStringExtra("BALANCE")
converted = intent.getStringExtra("CONVERTED")
super.onReceive(context, intent)
if (intent.action == AppWidgetManager.ACTION_APPWIDGET_UPDATE &&
(balance == null || converted == null)
) {
updateScheduler.enqueueOneTimeWorker()
}
}

override fun onUpdate(context: Context, widgetManager: AppWidgetManager, widgetIds: IntArray) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.stylingandroid.currencyconverter.ui.appwidget
package com.stylingandroid.currencyconverter.update

import android.content.Context
import androidx.work.WorkManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,19 @@ class CurrencyWorker @WorkerInject constructor(
@Suppress("TooGenericExceptionCaught")
override suspend fun doWork(): Result =
try {
val result = repository.exchangeRates(ratesValidity)
val rate = repository.exchangeRates(ratesValidity)
val balance = repository.balance(balanceValidity)
val targetBalance = result.rate.multiply(balance.balance)
Timber.d("Worker ran: $result $balance $targetBalance")
val balanceString = balance?.balance?.let { balanceValue ->
rate?.from?.let { fromCurrency ->
currencyFormat(fromCurrency).format(balanceValue)
}
}
val convertedString = balance?.balance?.let { balanceValue ->
rate?.let { rateValue ->
currencyFormat(rateValue.to).format(rateValue.rate.multiply(balanceValue))
}
}
Timber.d("Worker ran: $rate $balance $convertedString")
appContext.sendBroadcast(
Intent(appContext, CurrencyAppWidgetProvider::class.java).apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
Expand All @@ -45,8 +54,9 @@ class CurrencyWorker @WorkerInject constructor(
ComponentName(appContext, CurrencyAppWidgetProvider::class.java)
)
)
putExtra("BALANCE", currencyFormat(result.from).format(balance.balance))
putExtra("CONVERTED", currencyFormat(result.to).format(targetBalance))

putExtra("BALANCE", balanceString)
putExtra("CONVERTED", convertedString)
}
)
Result.success()
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,14 @@
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.button.MaterialButton
android:id="@+id/update_button"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/update"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
1 change: 1 addition & 0 deletions app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<!-- Primary brand color. -->
<item name="colorPrimary">@color/saGreenLight</item>
<item name="colorPrimaryDark">@color/saGreenDark</item>
<item name="colorAccent">@color/saGreenDark</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Status bar color. -->
<!-- Customize your theme here. -->
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<resources>
<string name="app_name">CurrencyConverter</string>
<string name="update">Update</string>
</resources>
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const val kotlinVersion = "1.4.30-M1"
const val hiltVersion = "2.30.1-alpha"
const val kotlinVersion = "1.4.30-RC"
const val hiltVersion = "2.31.2-alpha"
const val wireVersion = "3.5.0"

object BuildPlugins {
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Sat Aug 29 15:36:56 BST 2020
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.1-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

0 comments on commit 01191ec

Please sign in to comment.