Merge branch 'widgerref' into modal

This commit is contained in:
Marcos Rodriguez Velez 2024-07-21 14:01:50 -04:00
commit a076b3ec39
No known key found for this signature in database
GPG key ID: 6030B2F48CCE86D7
6 changed files with 136 additions and 86 deletions

View file

@ -118,8 +118,8 @@ dependencies {
// The version of react-native is set by the React Native Gradle Plugin
implementation("com.facebook.react:react-android")
implementation files("../../node_modules/rn-ldk/android/libs/LDK-release.aar")
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.core:core-remoteviews:1.1.0'
implementation 'androidx.core:core-ktx'
implementation 'androidx.work:work-runtime-ktx:2.7.1'
if (hermesEnabled.toBoolean()) {
implementation("com.facebook.react:hermes-android")
@ -131,8 +131,6 @@ dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.work:work-runtime:2.9.0"
implementation "androidx.work:work-runtime-ktx:2.9.0"
}
apply plugin: 'com.google.gms.google-services' // Google Services plugin

View file

@ -27,8 +27,8 @@ class BitcoinPriceWidget : AppWidgetProvider() {
WorkManager.getInstance(context).cancelUniqueWork(WidgetUpdateWorker.WORK_NAME)
}
override fun onReceive(context: Context, intent: Intent) {
override fun onReceive(context: Context, intent:Intent) {
super.onReceive(context, intent)
Log.d("BitcoinPriceWidget", "onReceive called with action: ${intent.action}")
}
}
}
}

View file

@ -46,47 +46,76 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
val sharedPref = applicationContext.getSharedPreferences("widget_prefs", Context.MODE_PRIVATE)
val preferredCurrency = sharedPref.getString("preferredCurrency", "USD")
val preferredCurrencyLocale = sharedPref.getString("preferredCurrencyLocale", "en-US")
val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
val price = fetchPrice()
val previousPrice = sharedPref.getString("previous_price", null)
// Log fetched data
Log.d(TAG, "Fetch completed with price: $price at $currentTime. Previous price: $previousPrice")
// Update views
val currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()).apply {
maximumFractionDigits = 0
}
views.setTextViewText(R.id.price_value, currencyFormat.format(price.toDouble()))
views.setTextViewText(R.id.last_updated_time, currentTime)
views.setViewVisibility(R.id.last_updated_label, View.VISIBLE)
views.setViewVisibility(R.id.last_updated_time, View.VISIBLE)
if (previousPrice != null) {
views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE)
views.setTextViewText(R.id.previous_price, currencyFormat.format(previousPrice.toDouble()))
if (price.toDouble() > previousPrice.toDouble()) {
views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float)
} else {
views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float)
}
} else {
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
}
// Show loading indicator
views.setViewVisibility(R.id.loading_indicator, View.VISIBLE)
views.setViewVisibility(R.id.price_value, View.GONE)
views.setViewVisibility(R.id.last_updated_label, View.GONE)
views.setViewVisibility(R.id.last_updated_time, View.GONE)
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
appWidgetManager.updateAppWidget(appWidgetIds, views)
savePrice(sharedPref, price)
val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
fetchPrice(preferredCurrency) { fetchedPrice, error ->
if (error != null) {
Log.e(TAG, "Error fetching price: $error")
views.setViewVisibility(R.id.loading_indicator, View.GONE)
views.setTextViewText(R.id.price_value, "Error fetching data")
views.setViewVisibility(R.id.price_value, View.VISIBLE)
} else {
val previousPrice = sharedPref.getString("previous_price", null)
val currentPrice = fetchedPrice?.toDouble()?.let { it.toInt() } // Remove cents
if (currentPrice == previousPrice?.toDouble()?.let { it.toInt() }) {
views.setTextViewText(R.id.last_updated_time, currentTime)
} else {
Log.d(TAG, "Fetch completed with price: $fetchedPrice at $currentTime. Previous price: $previousPrice")
val currencyFormat = NumberFormat.getCurrencyInstance(Locale.forLanguageTag(preferredCurrencyLocale!!)).apply {
maximumFractionDigits = 0
}
views.setViewVisibility(R.id.loading_indicator, View.GONE)
views.setTextViewText(R.id.price_value, currencyFormat.format(currentPrice))
views.setTextViewText(R.id.last_updated_time, currentTime)
views.setViewVisibility(R.id.price_value, View.VISIBLE)
views.setViewVisibility(R.id.last_updated_label, View.VISIBLE)
views.setViewVisibility(R.id.last_updated_time, View.VISIBLE)
if (previousPrice != null) {
views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE)
views.setTextViewText(R.id.previous_price, currencyFormat.format(previousPrice.toDouble().toInt()))
if (currentPrice!! > previousPrice.toDouble().toInt()) {
views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float)
} else {
views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float)
}
} else {
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
}
savePrice(sharedPref, fetchedPrice!!)
}
}
appWidgetManager.updateAppWidget(appWidgetIds, views)
}
return Result.success()
}
private fun fetchPrice(): String {
val urlString = "https://api.kraken.com/0/public/Ticker?pair=XXBTZUSD"
private fun fetchPrice(currency: String?, callback: (String?, String?) -> Unit) {
val fiatUnitsJson = applicationContext.assets.open("fiatUnits.json").bufferedReader().use { it.readText() }
val json = JSONObject(fiatUnitsJson)
val currencyInfo = json.getJSONObject(currency ?: "USD")
val source = currencyInfo.getString("source")
val endPointKey = currencyInfo.getString("endPointKey")
val urlString = buildURLString(source, endPointKey)
Log.d(TAG, "Fetching price from URL: $urlString")
val url = URL(urlString)
val urlConnection = url.openConnection() as HttpURLConnection
return try {
try {
val reader = InputStreamReader(urlConnection.inputStream)
val jsonResponse = StringBuilder()
val buffer = CharArray(1024)
@ -94,13 +123,45 @@ class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Wor
while (reader.read(buffer).also { read = it } != -1) {
jsonResponse.append(buffer, 0, read)
}
val json = JSONObject(jsonResponse.toString())
json.getJSONObject("result").getJSONObject("XXBTZUSD").getJSONArray("c").getString(0)
val responseJson = JSONObject(jsonResponse.toString())
val price = parseJSONBasedOnSource(responseJson, source, endPointKey)
callback(price, null)
} catch (e: Exception) {
Log.e(TAG, "Error fetching price", e)
callback(null, e.message)
} finally {
urlConnection.disconnect()
}
}
private fun buildURLString(source: String, endPointKey: String): String {
return when (source) {
"Yadio" -> "https://api.yadio.io/json/$endPointKey"
"YadioConvert" -> "https://api.yadio.io/convert/1/BTC/$endPointKey"
"Exir" -> "https://api.exir.io/v1/ticker?symbol=btc-irt"
"wazirx" -> "https://api.wazirx.com/api/v2/tickers/btcinr"
"Bitstamp" -> "https://www.bitstamp.net/api/v2/ticker/btc${endPointKey.lowercase()}"
"Coinbase" -> "https://api.coinbase.com/v2/prices/BTC-${endPointKey.uppercase()}/buy"
"CoinGecko" -> "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=${endPointKey.lowercase()}"
"BNR" -> "https://www.bnr.ro/nbrfxrates.xml"
"Kraken" -> "https://api.kraken.com/0/public/Ticker?pair=XXBTZ${endPointKey.uppercase()}"
else -> "https://api.coindesk.com/v1/bpi/currentprice/$endPointKey.json"
}
}
private fun parseJSONBasedOnSource(json: JSONObject, source: String, endPointKey: String): String {
return when (source) {
"Kraken" -> json.getJSONObject("result").getJSONObject("XXBTZ${endPointKey.uppercase()}").getJSONArray("c").getString(0)
"CoinGecko" -> json.getJSONObject("bitcoin").getString(endPointKey.lowercase())
"Coinbase" -> json.getJSONObject("data").getString("amount")
"Bitstamp" -> json.getString("last")
"wazirx" -> json.getJSONObject("ticker").getString("buy")
"Exir" -> json.getString("last")
"Yadio", "YadioConvert" -> json.getJSONObject(endPointKey).getString("price")
else -> throw IllegalArgumentException("Unsupported source: $source")
}
}
private fun savePrice(sharedPref: SharedPreferences, price: String) {
sharedPref.edit().putString("previous_price", price).apply()
}

View file

@ -8,6 +8,13 @@
android:background="@drawable/widget_background"
android:gravity="end">
<ProgressBar
android:id="@+id/loading_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -21,7 +28,8 @@
android:text="Last Updated"
android:textSize="12sp"
android:textStyle="bold"
android:layout_marginEnd="8dp"/>
android:layout_marginEnd="8dp"
android:visibility="gone"/>
<TextView
android:id="@+id/last_updated_time"
@ -31,7 +39,8 @@
android:text=""
android:textSize="12sp"
android:layout_marginEnd="8dp"
android:layout_marginTop="2dp"/>
android:layout_marginTop="2dp"
android:visibility="gone"/>
</LinearLayout>
<LinearLayout
@ -49,14 +58,16 @@
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"/>
android:layout_marginBottom="8dp"
android:visibility="gone"/>
<LinearLayout
android:id="@+id/price_arrow_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
android:gravity="end"
android:visibility="gone">
<ImageView
android:id="@+id/price_arrow"
@ -84,12 +95,5 @@
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"/>
</LinearLayout>
</LinearLayout>
<ProgressBar
android:id="@+id/loading_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>

View file

@ -3,5 +3,5 @@
<color name="background_color">#FFFFFF</color>
<color name="widget_background">#FFFFFF</color>
<color name="text_primary">#0C234F</color>
<color name="text_secondary">#D3D3D3</color>
<color name="text_secondary">#949494</color>
</resources>

View file

@ -5,8 +5,8 @@ buildscript {
minSdkVersion = 24
supportLibVersion = "28.0.0"
buildToolsVersion = "34.0.0"
compileSdkVersion = 34
targetSdkVersion = 34
compileSdkVersion = 33
targetSdkVersion = 33
googlePlayServicesVersion = "16.+"
googlePlayServicesIidVersion = "16.0.1"
firebaseVersion = "17.3.4"
@ -15,18 +15,19 @@ buildscript {
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
ndkVersion = "23.1.7779620"
kotlin_version = '1.9.25'
kotlinVersion = '1.9.25'
kotlinVersion = '1.9.23'
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:8.2.1")
classpath("com.android.tools.build:gradle")
classpath("com.bugsnag:bugsnag-android-gradle-plugin:5.+")
classpath 'com.google.gms:google-services:4.4.2' // Google Services plugin
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("com.facebook.react:react-native-gradle-plugin")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
}
}
@ -36,13 +37,13 @@ allprojects {
url("$rootDir/../node_modules/detox/Detox-android")
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
mavenCentral {
// We don't want to fetch react-native from Maven Central as there are
// older versions over there.
content {
excludeGroup "com.facebook.react"
}
}
mavenLocal()
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
@ -53,32 +54,17 @@ allprojects {
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
maven { url 'https://www.jitpack.io' }
maven { url 'https://www.jitpack.io' }
}
}
subprojects {
afterEvaluate { project ->
if (project.plugins.hasPlugin("com.android.application") || project.plugins.hasPlugin("com.android.library")) {
project.android {
// Enable buildConfig feature
buildFeatures {
buildConfig = true
}
// Set namespace if not already set
if (!namespace) {
def manifestFile = android.sourceSets.main.manifest.srcFile
if (manifestFile.exists()) {
def manifestContent = new XmlParser().parse(manifestFile)
def packageName = manifestContent.@package
if (packageName) {
namespace = packageName
}
}
}
afterEvaluate {project ->
if (project.hasProperty("android")) {
android {
buildToolsVersion "34.0.0"
compileSdkVersion 34
defaultConfig {
compileSdkVersion 33
defaultConfig {
minSdkVersion 24
}
}
@ -90,5 +76,6 @@ subprojects {
kotlinOptions.jvmTarget = sourceCompatibility
}
}
}
}