From 29aa934f8d7ee8d437a9d936dd35f25b6d4257ca Mon Sep 17 00:00:00 2001 From: Marcos Rodriguez Velez Date: Sat, 29 Jun 2024 17:42:14 -0400 Subject: [PATCH] wip --- android/app/build.gradle | 14 + android/app/src/main/AndroidManifest.xml | 5 +- android/app/src/main/assets/fiatUnits.json | 408 ++++++++++++++++++ .../bluewallet/BitcoinPriceWidget.java | 221 ++-------- .../io/bluewallet/bluewallet/Constants.java | 5 - .../bluewallet/MainApplication.java | 97 ++--- .../io/bluewallet/bluewallet/MarketAPI.java | 29 +- .../bluewallet/UpdateWidgetWorker.java | 149 ------- .../bluewallet/WidgetUpdateManager.java | 81 ++++ .../bluewallet/WidgetUpdateWorker.java | 156 +++++++ .../io/bluewallet/bluewallet/WidgetUtils.java | 19 + .../app/src/main/res/layout/widget_layout.xml | 29 +- 12 files changed, 805 insertions(+), 408 deletions(-) create mode 100644 android/app/src/main/assets/fiatUnits.json delete mode 100644 android/app/src/main/java/io/bluewallet/bluewallet/Constants.java delete mode 100644 android/app/src/main/java/io/bluewallet/bluewallet/UpdateWidgetWorker.java create mode 100644 android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java create mode 100644 android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java create mode 100644 android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java diff --git a/android/app/build.gradle b/android/app/build.gradle index 100c8631c..11d82526f 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -84,6 +84,12 @@ android { testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } + sourceSets { + main { + assets.srcDirs = ['src/main/assets', 'src/main/res/assets'] + } + } + buildTypes { release { // Caution! In production, you need to generate your own keystore file. @@ -95,6 +101,13 @@ android { } } +task copyFiatUnits(type: Copy) { + from '../../models/fiatUnits.json' + into 'src/main/assets' +} + +preBuild.dependsOn(copyFiatUnits) + dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") @@ -110,6 +123,7 @@ 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.7.1" } apply plugin: 'com.google.gms.google-services' // Google Services plugin diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b07ba48de..2fe078f1b 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + - @@ -70,7 +72,6 @@ android:resource="@xml/bitcoin_price_widget_info" /> - diff --git a/android/app/src/main/assets/fiatUnits.json b/android/app/src/main/assets/fiatUnits.json new file mode 100644 index 000000000..c14837051 --- /dev/null +++ b/android/app/src/main/assets/fiatUnits.json @@ -0,0 +1,408 @@ +{ + "USD": { + "endPointKey": "USD", + "locale": "en-US", + "source": "Kraken", + "symbol": "$", + "country": "United States (US Dollar)" + }, + "AED": { + "endPointKey": "AED", + "locale": "ar-AE", + "source": "CoinGecko", + "symbol": "د.إ.", + "country": "United Arab Emirates (UAE Dirham)" + }, + "ANG": { + "endPointKey": "ANG", + "locale": "en-SX", + "source": "CoinDesk", + "symbol": "ƒ", + "country": "Sint Maarten (Netherlands Antillean Guilder)" + }, + "ARS": { + "endPointKey": "ARS", + "locale": "es-AR", + "source": "Yadio", + "symbol": "$", + "country": "Argentina (Argentine Peso)" + }, + "AUD": { + "endPointKey": "AUD", + "locale": "en-AU", + "source": "CoinGecko", + "symbol": "$", + "country": "Australia (Australian Dollar)" + }, + "AWG": { + "endPointKey": "AWG", + "locale": "nl-AW", + "source": "CoinDesk", + "symbol": "ƒ", + "country": "Aruba (Aruban Florin)" + }, + "BHD": { + "endPointKey": "BHD", + "locale": "ar-BH", + "source": "CoinGecko", + "symbol": "د.ب.", + "country": "Bahrain (Bahraini Dinar)" + }, + "BRL": { + "endPointKey": "BRL", + "locale": "pt-BR", + "source": "CoinGecko", + "symbol": "R$", + "country": "Brazil (Brazilian Real)" + }, + "CAD": { + "endPointKey": "CAD", + "locale": "en-CA", + "source": "CoinGecko", + "symbol": "$", + "country": "Canada (Canadian Dollar)" + }, + "CHF": { + "endPointKey": "CHF", + "locale": "de-CH", + "source": "CoinGecko", + "symbol": "CHF", + "country": "Switzerland (Swiss Franc)" + }, + "CLP": { + "endPointKey": "CLP", + "locale": "es-CL", + "source": "Yadio", + "symbol": "$", + "country": "Chile (Chilean Peso)" + }, + "CNY": { + "endPointKey": "CNY", + "locale": "zh-CN", + "source": "Coinbase", + "symbol": "¥", + "country": "China (Chinese Yuan)" + }, + "COP": { + "endPointKey": "COP", + "locale": "es-CO", + "source": "CoinDesk", + "symbol": "$", + "country": "Colombia (Colombian Peso)" + }, + "CZK": { + "endPointKey": "CZK", + "locale": "cs-CZ", + "source": "CoinGecko", + "symbol": "Kč", + "country": "Czech Republic (Czech Koruna)" + }, + "DKK": { + "endPointKey": "DKK", + "locale": "da-DK", + "source": "CoinGecko", + "symbol": "kr", + "country": "Denmark (Danish Krone)" + }, + "EUR": { + "endPointKey": "EUR", + "locale": "en-IE", + "source": "Kraken", + "symbol": "€", + "country": "European Union (Euro)" + }, + "GBP": { + "endPointKey": "GBP", + "locale": "en-GB", + "source": "Kraken", + "symbol": "£", + "country": "United Kingdom (British Pound)" + }, + "HRK": { + "endPointKey": "HRK", + "locale": "hr-HR", + "source": "CoinDesk", + "symbol": "HRK", + "country": "Croatia (Croatian Kuna)" + }, + "HUF": { + "endPointKey": "HUF", + "locale": "hu-HU", + "source": "CoinGecko", + "symbol": "Ft", + "country": "Hungary (Hungarian Forint)" + }, + "IDR": { + "endPointKey": "IDR", + "locale": "id-ID", + "source": "CoinGecko", + "symbol": "Rp", + "country": "Indonesia (Indonesian Rupiah)" + }, + "ILS": { + "endPointKey": "ILS", + "locale": "he-IL", + "source": "CoinGecko", + "symbol": "₪", + "country": "Israel (Israeli New Shekel)" + }, + "INR": { + "endPointKey": "INR", + "locale": "hi-IN", + "source": "wazirx", + "symbol": "₹", + "country": "India (Indian Rupee)" + }, + "IRR": { + "endPointKey": "IRR", + "locale": "fa-IR", + "source": "Exir", + "symbol": "﷼", + "country": "Iran (Iranian Rial)" + }, + "IRT": { + "endPointKey": "IRT", + "locale": "fa-IR", + "source": "Exir", + "symbol": "تومان", + "country": "Iran (Iranian Toman)" + }, + "ISK": { + "endPointKey": "ISK", + "locale": "is-IS", + "source": "CoinDesk", + "symbol": "kr", + "country": "Iceland (Icelandic Króna)" + }, + "JPY": { + "endPointKey": "JPY", + "locale": "ja-JP", + "source": "CoinGecko", + "symbol": "¥", + "country": "Japan (Japanese Yen)" + }, + "KES": { + "endPointKey": "KES", + "locale": "en-KE", + "source": "CoinDesk", + "symbol": "Ksh", + "country": "Kenya (Kenyan Shilling)" + }, + "KRW": { + "endPointKey": "KRW", + "locale": "ko-KR", + "source": "CoinGecko", + "symbol": "₩", + "country": "South Korea (South Korean Won)" + }, + "KWD": { + "endPointKey": "KWD", + "locale": "ar-KW", + "source": "CoinGecko", + "symbol": "د.ك.", + "country": "Kuwait (Kuwaiti Dinar)" + }, + "LBP": { + "endPointKey": "LBP", + "locale": "ar-LB", + "source": "YadioConvert", + "symbol": "ل.ل.", + "country": "Lebanon (Lebanese Pound)" + }, + "LKR": { + "endPointKey": "LKR", + "locale": "si-LK", + "source": "CoinGecko", + "symbol": "රු.", + "country": "Sri Lanka (Sri Lankan Rupee)" + }, + "MXN": { + "endPointKey": "MXN", + "locale": "es-MX", + "source": "CoinGecko", + "symbol": "$", + "country": "Mexico (Mexican Peso)" + }, + "MYR": { + "endPointKey": "MYR", + "locale": "ms-MY", + "source": "CoinGecko", + "symbol": "RM", + "country": "Malaysia (Malaysian Ringgit)" + }, + "MZN": { + "endPointKey": "MZN", + "locale": "seh-MZ", + "source": "CoinDesk", + "symbol": "MTn", + "country": "Mozambique (Mozambican Metical)" + }, + "NGN": { + "endPointKey": "NGN", + "locale": "en-NG", + "source": "CoinGecko", + "symbol": "₦", + "country": "Nigeria (Nigerian Naira)" + }, + "NOK": { + "endPointKey": "NOK", + "locale": "nb-NO", + "source": "CoinGecko", + "symbol": "kr", + "country": "Norway (Norwegian Krone)" + }, + "NZD": { + "endPointKey": "NZD", + "locale": "en-NZ", + "source": "CoinGecko", + "symbol": "$", + "country": "New Zealand (New Zealand Dollar)" + }, + "OMR": { + "endPointKey": "OMR", + "locale": "ar-OM", + "source": "CoinDesk", + "symbol": "ر.ع.", + "country": "Oman (Omani Rial)" + }, + "PHP": { + "endPointKey": "PHP", + "locale": "en-PH", + "source": "CoinGecko", + "symbol": "₱", + "country": "Philippines (Philippine Peso)" + }, + "PLN": { + "endPointKey": "PLN", + "locale": "pl-PL", + "source": "CoinGecko", + "symbol": "zł", + "country": "Poland (Polish Zloty)" + }, + "QAR": { + "endPointKey": "QAR", + "locale": "ar-QA", + "source": "CoinDesk", + "symbol": "ر.ق.", + "country": "Qatar (Qatari Riyal)" + }, + "RON": { + "endPointKey": "RON", + "locale": "ro-RO", + "source": "BNR", + "symbol": "lei", + "country": "Romania (Romanian Leu)" + }, + "RUB": { + "endPointKey": "RUB", + "locale": "ru-RU", + "source": "CoinGecko", + "symbol": "₽", + "country": "Russia (Russian Ruble)" + }, + "SAR": { + "endPointKey": "SAR", + "locale": "ar-SA", + "source": "CoinGecko", + "symbol": "ر.س.", + "country": "Saudi Arabia (Saudi Riyal)" + }, + "SEK": { + "endPointKey": "SEK", + "locale": "sv-SE", + "source": "CoinGecko", + "symbol": "kr", + "country": "Sweden (Swedish Krona)" + }, + "SGD": { + "endPointKey": "SGD", + "locale": "zh-SG", + "source": "CoinGecko", + "symbol": "S$", + "country": "Singapore (Singapore Dollar)" + }, + "THB": { + "endPointKey": "THB", + "locale": "th-TH", + "source": "CoinGecko", + "symbol": "฿", + "country": "Thailand (Thai Baht)" + }, + "TRY": { + "endPointKey": "TRY", + "locale": "tr-TR", + "source": "CoinGecko", + "symbol": "₺", + "country": "Turkey (Turkish Lira)" + }, + "TWD": { + "endPointKey": "TWD", + "locale": "zh-Hant-TW", + "source": "CoinGecko", + "symbol": "NT$", + "country": "Taiwan (New Taiwan Dollar)" + }, + "TZS": { + "endPointKey": "TZS", + "locale": "en-TZ", + "source": "CoinDesk", + "symbol": "TSh", + "country": "Tanzania (Tanzanian Shilling)" + }, + "UAH": { + "endPointKey": "UAH", + "locale": "uk-UA", + "source": "CoinGecko", + "symbol": "₴", + "country": "Ukraine (Ukrainian Hryvnia)" + }, + "UGX": { + "endPointKey": "UGX", + "locale": "en-UG", + "source": "CoinDesk", + "symbol": "USh", + "country": "Uganda (Ugandan Shilling)" + }, + "UYU": { + "endPointKey": "UYU", + "locale": "es-UY", + "source": "CoinDesk", + "symbol": "$", + "country": "Uruguay (Uruguayan Peso)" + }, + "VEF": { + "endPointKey": "VEF", + "locale": "es-VE", + "source": "CoinGecko", + "symbol": "Bs.", + "country": "Venezuela (Venezuelan Bolívar Fuerte)" + }, + "VES": { + "endPointKey": "VES", + "locale": "es-VE", + "source": "Yadio", + "symbol": "Bs.", + "country": "Venezuela (Venezuelan Bolívar Soberano)" + }, + "XAF": { + "endPointKey": "XAF", + "locale": "fr-CF", + "source": "CoinDesk", + "symbol": "Fr", + "country": "Central African Republic (Central African Franc)" + }, + "ZAR": { + "endPointKey": "ZAR", + "locale": "en-ZA", + "source": "CoinGecko", + "symbol": "R", + "country": "South Africa (South African Rand)" + }, + "GHS": { + "endPointKey": "GHS", + "locale": "en-GH", + "source": "CoinDesk", + "symbol": "₵", + "country": "Ghana (Ghanaian Cedi)" + } +} diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/BitcoinPriceWidget.java b/android/app/src/main/java/io/bluewallet/bluewallet/BitcoinPriceWidget.java index 1160469f0..f9dbf9e7c 100644 --- a/android/app/src/main/java/io/bluewallet/bluewallet/BitcoinPriceWidget.java +++ b/android/app/src/main/java/io/bluewallet/bluewallet/BitcoinPriceWidget.java @@ -1,205 +1,78 @@ package io.bluewallet.bluewallet; -import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.util.Log; import android.view.View; import android.widget.RemoteViews; -import androidx.annotation.NonNull; -import androidx.work.Constraints; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import java.lang.ref.WeakReference; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - public class BitcoinPriceWidget extends AppWidgetProvider { private static final String TAG = "BitcoinPriceWidget"; - private static final String ACTION_UPDATE = "io.bluewallet.bluewallet.UPDATE"; private static final String PREFS_NAME = "BitcoinPriceWidgetPrefs"; private static final String PREF_PREFIX_KEY = "appwidget_"; - private static final int UPDATE_INTERVAL_MINUTES = 10; // Adjustable interval in minutes - private static ExecutorService executorService; - - @Override - public void onEnabled(Context context) { - super.onEnabled(context); - initializeExecutorService(); - } - - @Override - public void onDisabled(Context context) { - super.onDisabled(context); - if (executorService != null) { - executorService.shutdown(); - } - } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - initializeExecutorService(); - if (appWidgetIds.length > 0) { - for (int appWidgetId : appWidgetIds) { - Log.d(TAG, "Updating widget ID: " + appWidgetId); + Log.d(TAG, "Updating widget"); - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); - - // Set up the pending intent to open the app when the widget is clicked - Intent launchAppIntent = new Intent(context, MainActivity.class); - PendingIntent launchAppPendingIntent = PendingIntent.getActivity(context, 0, launchAppIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - views.setOnClickPendingIntent(R.id.widget_layout, launchAppPendingIntent); - - // Set the loading indicator visible initially - views.setViewVisibility(R.id.loading_indicator, View.VISIBLE); - views.setViewVisibility(R.id.price_value, View.GONE); - views.setViewVisibility(R.id.last_updated, View.GONE); - views.setViewVisibility(R.id.last_updated_time, View.GONE); - - appWidgetManager.updateAppWidget(appWidgetId, views); - - executorService.execute(new FetchBitcoinPriceTask(context, appWidgetManager, appWidgetId)); - } - - scheduleNextUpdate(context); + for (int appWidgetId : appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId); } + + WidgetUpdateWorker.scheduleWork(context); // Schedule to run periodically } - private void initializeExecutorService() { - if (executorService == null || executorService.isShutdown()) { - executorService = Executors.newSingleThreadExecutor(); - } - } + private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); + String currentPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_price", "N/A"); + String currentTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_time", "N/A"); + String previousPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_previous_price", "N/A"); + String previousTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_previous_time", "N/A"); - private void scheduleNextUpdate(Context context) { - OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(UpdateWidgetWorker.class) - .setInitialDelay(UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES) - .build(); - - WorkManager.getInstance(context).enqueue(workRequest); - } - - private static class FetchBitcoinPriceTask implements Runnable { - private final WeakReference contextRef; - private final AppWidgetManager appWidgetManager; - private final int appWidgetId; - - FetchBitcoinPriceTask(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { - this.contextRef = new WeakReference<>(context); - this.appWidgetManager = appWidgetManager; - this.appWidgetId = appWidgetId; - } - - @Override - public void run() { - Context context = contextRef.get(); - if (context == null) return; - - Log.d(TAG, "Starting to fetch Bitcoin price..."); - - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); - String preferredCurrency = prefs.getString("preferredCurrency", "USD"); - String price = MarketAPI.fetchPrice(context, preferredCurrency); - - if (price != null) { - updateWidgetWithPrice(context, price); - } else { - handleError(context); - } - } - - private void updateWidgetWithPrice(Context context, String price) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); - - // Fetch current and previous data - String prevPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_prev_price", "N/A"); - String prevTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_prev_time", "N/A"); - String currentPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_price", null); - String currentTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_time", null); - - SharedPreferences.Editor editor = prefs.edit(); - - String newTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date()); - Log.d(TAG, "Fetch completed with price: " + price + " at " + newTime + ". Previous price: " + prevPrice + " at " + prevTime); - NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.US); - views.setTextViewText(R.id.price_value, currencyFormat.format(Double.parseDouble(price))); - - if (currentPrice != null) { - double previousPrice = Double.parseDouble(currentPrice); - double newPrice = Double.parseDouble(price); - if (newPrice > previousPrice) { - views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float); - } else if (newPrice < previousPrice) { - views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float); - } else { - views.setImageViewResource(R.id.price_arrow, 0); - } - - if (newPrice != previousPrice) { - views.setTextViewText(R.id.previous_price, "from " + currencyFormat.format(previousPrice)); - views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE); - views.setViewVisibility(R.id.previous_price, View.VISIBLE); - } else { - views.setTextViewText(R.id.previous_price, ""); - views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - } - } else { - views.setImageViewResource(R.id.price_arrow, 0); - views.setTextViewText(R.id.previous_price, ""); - views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - } - - // Shift current to previous - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_prev_price", currentPrice); - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_prev_time", currentTime); - - // Set new current - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_current_price", price); - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_current_time", newTime); - editor.apply(); + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); + if (!currentPrice.equals("N/A")) { + views.setTextViewText(R.id.price_value, currentPrice); views.setTextViewText(R.id.last_updated, "Last Updated"); - views.setTextViewText(R.id.last_updated_time, newTime); - + views.setTextViewText(R.id.last_updated_time, currentTime); views.setViewVisibility(R.id.loading_indicator, View.GONE); - views.setViewVisibility(R.id.price_value, View.VISIBLE); - views.setViewVisibility(R.id.last_updated, View.VISIBLE); - views.setViewVisibility(R.id.last_updated_time, View.VISIBLE); - - appWidgetManager.updateAppWidget(appWidgetId, views); + } else { + views.setTextViewText(R.id.price_value, "Loading..."); + views.setViewVisibility(R.id.loading_indicator, View.VISIBLE); } - private void handleError(Context context) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); - String errorMessage = "Network Error"; - Log.e(TAG, errorMessage); - views.setTextViewText(R.id.price_value, errorMessage); - views.setTextViewText(R.id.last_updated, ""); - views.setTextViewText(R.id.last_updated_time, ""); - views.setImageViewResource(R.id.price_arrow, 0); - views.setTextViewText(R.id.previous_price, ""); + if (!previousPrice.equals("N/A") && !previousPrice.equals(currentPrice)) { + views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE); + views.setTextViewText(R.id.previous_price, "From " + previousPrice); + + try { + double current = Double.parseDouble(currentPrice.replaceAll("[^\\d.]", "")); + double previous = Double.parseDouble(previousPrice.replaceAll("[^\\d.]", "")); + if (current > previous) { + 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); + } + } catch (NumberFormatException e) { + Log.e(TAG, "Error parsing prices", e); + } + + } else { views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - views.setViewVisibility(R.id.loading_indicator, View.GONE); - - appWidgetManager.updateAppWidget(appWidgetId, views); - - Log.e(TAG, "Failed to fetch Bitcoin price"); } + + appWidgetManager.updateAppWidget(appWidgetId, views); } -} + + public static void savePreferences(Context context, int appWidgetId, String currentPrice, String currentTime, String previousPrice, String previousTime) { + SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit(); + prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_current_price", currentPrice); + prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_current_time", currentTime); + prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_previous_price", previousPrice); + prefs.putString(PREF_PREFIX_KEY + appWidgetId + "_previous_time", previousTime); + prefs.apply(); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/Constants.java b/android/app/src/main/java/io/bluewallet/bluewallet/Constants.java deleted file mode 100644 index 693b34878..000000000 --- a/android/app/src/main/java/io/bluewallet/bluewallet/Constants.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.bluewallet.bluewallet; - -public class Constants { - public static final int UPDATE_INTERVAL_MINUTES = 3; -} \ No newline at end of file diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java b/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java index 1c0886f73..789eb48c7 100644 --- a/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java +++ b/android/app/src/main/java/io/bluewallet/bluewallet/MainApplication.java @@ -23,64 +23,59 @@ import java.util.concurrent.TimeUnit; public class MainApplication extends Application implements ReactApplication { - private final ReactNativeHost mReactNativeHost = - new DefaultReactNativeHost(this) { - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } + private final ReactNativeHost mReactNativeHost = + new DefaultReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } - @Override - protected List getPackages() { - @SuppressWarnings("UnnecessaryLocalVariable") - List packages = new PackageList(this).getPackages(); - // Packages that cannot be autolinked yet can be added manually here, for example: - return packages; - } + @Override + protected List getPackages() { + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); + return packages; + } - @Override - protected String getJSMainModuleName() { - return "index"; - } + @Override + protected String getJSMainModuleName() { + return "index"; + } - @Override - protected boolean isNewArchEnabled() { - return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - } + @Override + protected boolean isNewArchEnabled() { + return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + } - @Override - protected Boolean isHermesEnabled() { - return BuildConfig.IS_HERMES_ENABLED; - } - }; + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED; + } + }; - @Override - public ReactNativeHost getReactNativeHost() { - return mReactNativeHost; - } - - @Override - public void onCreate() { - super.onCreate(); - I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance(); - sharedI18nUtilInstance.allowRTL(getApplicationContext(), true); - SoLoader.init(this, /* native exopackage */ false); - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - DefaultNewArchitectureEntryPoint.load(); + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; } - SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE); - // Retrieve the "donottrack" value. Default to "0" if not found. - String isDoNotTrackEnabled = sharedPref.getString("donottrack", "0"); + @Override + public void onCreate() { + super.onCreate(); + I18nUtil sharedI18nUtilInstance = I18nUtil.getInstance(); + sharedI18nUtilInstance.allowRTL(getApplicationContext(), true); + SoLoader.init(this, /* native exopackage */ false); - // Check if do not track is not enabled and initialize Bugsnag if so - if (!isDoNotTrackEnabled.equals("1")) { - // Initialize Bugsnag or your error tracking here - Bugsnag.start(this); + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + DefaultNewArchitectureEntryPoint.load(); + } + + SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE); + String isDoNotTrackEnabled = sharedPref.getString("donottrack", "0"); + + if (!isDoNotTrackEnabled.equals("1")) { + Bugsnag.start(this); + } + + WidgetUpdateWorker.scheduleWork(this); // Schedule to run every 10 minutes } - PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(UpdateWidgetWorker.class, 10, TimeUnit.MINUTES) - .build(); -WorkManager.getInstance(this).enqueueUniquePeriodicWork("UpdateWidgetWork", ExistingPeriodicWorkPolicy.REPLACE, workRequest); - } } diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java b/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java index c7c87a9f7..3a35aa2d8 100644 --- a/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java +++ b/android/app/src/main/java/io/bluewallet/bluewallet/MarketAPI.java @@ -1,6 +1,7 @@ package io.bluewallet.bluewallet; import android.content.Context; +import android.util.Log; import org.json.JSONArray; import org.json.JSONObject; @@ -12,24 +13,26 @@ import java.net.URL; public class MarketAPI { + private static final String TAG = "MarketAPI"; + private static final String HARD_CODED_JSON = "{\n" + + " \"USD\": {\n" + + " \"endPointKey\": \"USD\",\n" + + " \"locale\": \"en-US\",\n" + + " \"source\": \"Kraken\",\n" + + " \"symbol\": \"$\",\n" + + " \"country\": \"United States (US Dollar)\"\n" + + " }\n" + + "}"; + public static String fetchPrice(Context context, String currency) { try { - // Load the JSON file from assets - InputStreamReader is = new InputStreamReader(context.getAssets().open("fiatUnits.json")); - StringBuilder json = new StringBuilder(); - int data = is.read(); - while (data != -1) { - json.append((char) data); - data = is.read(); - } - is.close(); - - JSONObject jsonObject = new JSONObject(json.toString()); - JSONObject currencyInfo = jsonObject.getJSONObject(currency); + JSONObject json = new JSONObject(HARD_CODED_JSON); + JSONObject currencyInfo = json.getJSONObject(currency); String source = currencyInfo.getString("source"); String endPointKey = currencyInfo.getString("endPointKey"); String urlString = buildURLString(source, endPointKey); + Log.d(TAG, "Fetching from URL: " + urlString); URI uri = new URI(urlString); URL url = uri.toURL(); HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); @@ -116,4 +119,4 @@ public class MarketAPI { return null; } } -} \ No newline at end of file +} diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/UpdateWidgetWorker.java b/android/app/src/main/java/io/bluewallet/bluewallet/UpdateWidgetWorker.java deleted file mode 100644 index 272098960..000000000 --- a/android/app/src/main/java/io/bluewallet/bluewallet/UpdateWidgetWorker.java +++ /dev/null @@ -1,149 +0,0 @@ -package io.bluewallet.bluewallet; - -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Context; -import android.content.SharedPreferences; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; - -import androidx.annotation.NonNull; -import androidx.work.Worker; -import androidx.work.WorkerParameters; -import androidx.work.OneTimeWorkRequest; -import androidx.work.WorkManager; - -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.concurrent.TimeUnit; - -public class UpdateWidgetWorker extends Worker { - - private static final String TAG = "UpdateWidgetWorker"; - private static final String PREFS_NAME = "BitcoinPriceWidgetPrefs"; - private static final String PREF_PREFIX_KEY = "appwidget_"; - private static final int UPDATE_INTERVAL_MINUTES = 10; // Adjustable interval in minutes - - public UpdateWidgetWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - } - - @NonNull - @Override - public Result doWork() { - Context context = getApplicationContext(); - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - ComponentName widget = new ComponentName(context, BitcoinPriceWidget.class); - int[] appWidgetIds = appWidgetManager.getAppWidgetIds(widget); - - for (int appWidgetId : appWidgetIds) { - fetchAndUpdatePrice(context, appWidgetManager, appWidgetId); - } - - scheduleNextUpdate(context); // Schedule the next update - - return Result.success(); - } - - private void fetchAndUpdatePrice(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { - Log.d(TAG, "Fetching Bitcoin price..."); - - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); - String preferredCurrency = prefs.getString("preferredCurrency", "USD"); - String price = MarketAPI.fetchPrice(context, preferredCurrency); - - if (price != null) { - updateWidgetWithPrice(context, appWidgetManager, appWidgetId, price); - } else { - handleError(context, appWidgetManager, appWidgetId); - } - } - - private void updateWidgetWithPrice(Context context, AppWidgetManager appWidgetManager, int appWidgetId, String price) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); - - // Fetch current and previous data - String prevPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_prev_price", "N/A"); - String prevTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_prev_time", "N/A"); - String currentPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_price", null); - String currentTime = prefs.getString(PREF_PREFIX_KEY + appWidgetId + "_current_time", null); - - SharedPreferences.Editor editor = prefs.edit(); - - String newTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date()); - Log.d(TAG, "Fetch completed with price: " + price + " at " + newTime + ". Previous price: " + prevPrice + " at " + prevTime); - NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.US); - views.setTextViewText(R.id.price_value, currencyFormat.format(Double.parseDouble(price))); - - if (currentPrice != null) { - double previousPrice = Double.parseDouble(currentPrice); - double newPrice = Double.parseDouble(price); - if (newPrice > previousPrice) { - views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_up_float); - } else if (newPrice < previousPrice) { - views.setImageViewResource(R.id.price_arrow, android.R.drawable.arrow_down_float); - } else { - views.setImageViewResource(R.id.price_arrow, 0); - } - - if (newPrice != previousPrice) { - views.setTextViewText(R.id.previous_price, "from " + currencyFormat.format(previousPrice)); - views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE); - views.setViewVisibility(R.id.previous_price, View.VISIBLE); - } else { - views.setTextViewText(R.id.previous_price, ""); - views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - } - } else { - views.setImageViewResource(R.id.price_arrow, 0); - views.setTextViewText(R.id.previous_price, ""); - views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - } - - // Shift current to previous - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_prev_price", currentPrice); - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_prev_time", currentTime); - - // Set new current - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_current_price", price); - editor.putString(PREF_PREFIX_KEY + appWidgetId + "_current_time", newTime); - editor.apply(); - - views.setTextViewText(R.id.last_updated, "Last Updated"); - views.setTextViewText(R.id.last_updated_time, newTime); - - views.setViewVisibility(R.id.loading_indicator, View.GONE); - views.setViewVisibility(R.id.price_value, View.VISIBLE); - views.setViewVisibility(R.id.last_updated, View.VISIBLE); - views.setViewVisibility(R.id.last_updated_time, View.VISIBLE); - - appWidgetManager.updateAppWidget(appWidgetId, views); - } - - private void handleError(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); - String errorMessage = "Network Error"; - Log.e(TAG, errorMessage); - views.setTextViewText(R.id.price_value, errorMessage); - views.setTextViewText(R.id.last_updated, ""); - views.setTextViewText(R.id.last_updated_time, ""); - views.setImageViewResource(R.id.price_arrow, 0); - views.setTextViewText(R.id.previous_price, ""); - views.setViewVisibility(R.id.price_arrow_container, View.GONE); - views.setViewVisibility(R.id.previous_price, View.GONE); - appWidgetManager.updateAppWidget(appWidgetId, views); - } - - private void scheduleNextUpdate(Context context) { - OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(UpdateWidgetWorker.class) - .setInitialDelay(UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES) - .build(); - WorkManager.getInstance(context).enqueue(workRequest); - } -} \ No newline at end of file diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java new file mode 100644 index 000000000..50f0d104c --- /dev/null +++ b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateManager.java @@ -0,0 +1,81 @@ +package io.bluewallet.bluewallet; + +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; +import android.view.View; +import android.widget.RemoteViews; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import java.text.DateFormat; +import java.util.Date; +import java.util.Locale; + +public class WidgetUpdateManager extends Worker { + private static final String TAG = "WidgetUpdateManager"; + private static final String PREFS_NAME = "BitcoinPriceWidgetPrefs"; + private static final String PREF_PREFIX_KEY = "appwidget_"; + private static final String PREFS_LAST_UPDATE_TIME_KEY = "lastUpdateTime_"; + private static final String PREFS_LAST_PRICE_KEY = "lastPrice_"; + private static final String PREFS_PREV_PRICE_KEY = "prevPrice_"; + private static final String PREFS_PREV_UPDATE_TIME_KEY = "prevUpdateTime_"; + + public WidgetUpdateManager(Context context, WorkerParameters params) { + super(context, params); + } + + @Override + public Result doWork() { + Context context = getApplicationContext(); + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, BitcoinPriceWidget.class)); + + for (int appWidgetId : appWidgetIds) { + updateAppWidget(context, appWidgetManager, appWidgetId); + } + return Result.success(); + } + + public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { + SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0); + String previousPrice = prefs.getString(PREFS_PREV_PRICE_KEY + appWidgetId, "N/A"); + String lastPrice = prefs.getString(PREFS_LAST_PRICE_KEY + appWidgetId, "N/A"); + String lastUpdateTime = prefs.getString(PREFS_LAST_UPDATE_TIME_KEY + appWidgetId, "N/A"); + String previousUpdateTime = prefs.getString(PREFS_PREV_UPDATE_TIME_KEY + appWidgetId, "N/A"); + + Log.d(TAG, "Fetch completed with price: " + lastPrice + " at " + lastUpdateTime + ". Previous price: " + previousPrice + " at " + previousUpdateTime); + + String price = MarketAPI.fetchPrice(context, "USD"); + if (price != null) { + Log.d(TAG, "Fetch completed with price: " + price); + SharedPreferences.Editor editor = prefs.edit(); + editor.putString(PREFS_PREV_PRICE_KEY + appWidgetId, lastPrice); + editor.putString(PREFS_PREV_UPDATE_TIME_KEY + appWidgetId, lastUpdateTime); + editor.putString(PREFS_LAST_PRICE_KEY + appWidgetId, price); + editor.putString(PREFS_LAST_UPDATE_TIME_KEY + appWidgetId, DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date())); + editor.apply(); + + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); + views.setTextViewText(R.id.price_value, price); + views.setTextViewText(R.id.last_updated_time, DateFormat.getTimeInstance(DateFormat.SHORT).format(new Date())); + views.setViewVisibility(R.id.loading_indicator, View.GONE); + views.setViewVisibility(R.id.price_value, View.VISIBLE); + views.setViewVisibility(R.id.last_updated, View.VISIBLE); + views.setViewVisibility(R.id.last_updated_time, View.VISIBLE); + + if (!"N/A".equals(previousPrice)) { + views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE); + views.setViewVisibility(R.id.previous_price, View.VISIBLE); + } else { + views.setViewVisibility(R.id.price_arrow_container, View.GONE); + views.setViewVisibility(R.id.previous_price, View.GONE); + } + appWidgetManager.updateAppWidget(appWidgetId, views); + } else { + Log.e(TAG, "Failed to fetch Bitcoin price"); + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java new file mode 100644 index 000000000..872feb49b --- /dev/null +++ b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUpdateWorker.java @@ -0,0 +1,156 @@ +package io.bluewallet.bluewallet; + +import android.content.Context; +import android.content.SharedPreferences; +import android.util.Log; +import android.view.View; +import android.widget.RemoteViews; + +import androidx.annotation.NonNull; +import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import java.text.DateFormat; +import java.text.NumberFormat; +import java.util.Date; +import java.util.Locale; +import java.util.concurrent.TimeUnit; + +public class WidgetUpdateWorker extends Worker { + + private static final String TAG = "WidgetUpdateWorker"; + private static final String WORK_NAME = "WidgetUpdateWorker"; + private static final int UPDATE_INTERVAL_MINUTES = 10; // You can adjust this value as needed + + public WidgetUpdateWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + Context context = getApplicationContext(); + SharedPreferences prefs = context.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE); + + // Fetch the preferred currency from shared preferences + String preferredCurrency = prefs.getString("preferredCurrency", "USD"); + + Log.d(TAG, "Fetching price for currency: " + preferredCurrency); + + // Fetch the price using MarketAPI + String price = MarketAPI.fetchPrice(context, preferredCurrency); + String previousPrice = prefs.getString("previous_price", "N/A"); + String previousTime = prefs.getString("previous_time", "N/A"); + + Log.d(TAG, "Fetch completed with price: " + price + " at " + getCurrentTime() + ". Previous price: " + previousPrice + " at " + previousTime); + + // Update the widget with the fetched price + if (price != null) { + SharedPreferences.Editor editor = prefs.edit(); + editor.putString("previous_price", prefs.getString("current_price", "N/A")); + editor.putString("previous_time", prefs.getString("current_time", "N/A")); + editor.putString("current_price", price); + editor.putString("current_time", getCurrentTime()); + editor.apply(); + + updateWidget(context, price, preferredCurrency, previousPrice); + } else { + handleError(context); + return Result.retry(); + } + + Log.d(TAG, "Next fetch will occur at: " + getNextFetchTime()); + + return Result.success(); + } + + private void updateWidget(Context context, String price, String currency, String previousPrice) { + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); + + NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()); + currencyFormat.setCurrency(java.util.Currency.getInstance(currency)); + + views.setTextViewText(R.id.price_value, currencyFormat.format(Double.parseDouble(price))); + views.setTextViewText(R.id.last_updated, "Last Updated"); + views.setTextViewText(R.id.last_updated_time, getCurrentTime()); + + SharedPreferences prefs = context.getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE); + String currentStoredPrice = prefs.getString("current_price", null); + if (currentStoredPrice == null || currentStoredPrice.isEmpty()) { + views.setViewVisibility(R.id.loading_indicator, View.VISIBLE); + views.setViewVisibility(R.id.price_value, View.GONE); + views.setViewVisibility(R.id.last_updated, View.GONE); + views.setViewVisibility(R.id.last_updated_time, View.GONE); + } else { + views.setViewVisibility(R.id.loading_indicator, View.GONE); + views.setViewVisibility(R.id.price_value, View.VISIBLE); + views.setViewVisibility(R.id.last_updated, View.VISIBLE); + views.setViewVisibility(R.id.last_updated_time, View.VISIBLE); + } + + if (!previousPrice.equals("N/A") && !previousPrice.equals(price)) { + views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE); + views.setTextViewText(R.id.previous_price, "From " + currencyFormat.format(Double.parseDouble(previousPrice))); + + double current = Double.parseDouble(price); + double previous = Double.parseDouble(previousPrice); + if (current > previous) { + 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); + } + + // Update the widget + WidgetUtils.updateAppWidget(context, views); + } + + private void handleError(Context context) { + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout); + views.setTextViewText(R.id.price_value, "Failed to fetch"); + views.setTextViewText(R.id.last_updated, "Last Updated"); + views.setTextViewText(R.id.last_updated_time, getCurrentTime()); + + views.setViewVisibility(R.id.loading_indicator, View.GONE); + + // Update the widget + WidgetUtils.updateAppWidget(context, views); + } + + private String getCurrentTime() { + DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getApplicationContext()); + return dateFormat.format(new Date()); + } + + private String getNextFetchTime() { + long currentTimeMillis = System.currentTimeMillis(); + long nextFetchTimeMillis = currentTimeMillis + TimeUnit.MINUTES.toMillis(UPDATE_INTERVAL_MINUTES); + Date nextFetchDate = new Date(nextFetchTimeMillis); + DateFormat dateFormat = android.text.format.DateFormat.getTimeFormat(getApplicationContext()); + return dateFormat.format(nextFetchDate); + } + + public static PeriodicWorkRequest createWorkRequest(int intervalMinutes) { + Constraints constraints = new Constraints.Builder() + .setRequiresBatteryNotLow(true) + .build(); + + return new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class, intervalMinutes, TimeUnit.MINUTES) + .setConstraints(constraints) + .build(); + } + + public static void scheduleWork(Context context) { + WorkManager.getInstance(context).enqueueUniquePeriodicWork( + WORK_NAME, + ExistingPeriodicWorkPolicy.REPLACE, + createWorkRequest(UPDATE_INTERVAL_MINUTES) + ); + } +} \ No newline at end of file diff --git a/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java new file mode 100644 index 000000000..3b1a128fa --- /dev/null +++ b/android/app/src/main/java/io/bluewallet/bluewallet/WidgetUtils.java @@ -0,0 +1,19 @@ +package io.bluewallet.bluewallet; + +import android.appwidget.AppWidgetManager; +import android.content.ComponentName; +import android.content.Context; +import android.widget.RemoteViews; + +public class WidgetUtils { + + public static void updateAppWidget(Context context, RemoteViews views) { + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + ComponentName thisWidget = new ComponentName(context, BitcoinPriceWidget.class); + int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget); + + for (int widgetId : allWidgetIds) { + appWidgetManager.updateAppWidget(widgetId, views); + } + } +} diff --git a/android/app/src/main/res/layout/widget_layout.xml b/android/app/src/main/res/layout/widget_layout.xml index f30c0392f..cc45e32fa 100644 --- a/android/app/src/main/res/layout/widget_layout.xml +++ b/android/app/src/main/res/layout/widget_layout.xml @@ -12,7 +12,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:gravity="end"> - + + android:layout_marginEnd="8dp"/> + android:layout_marginTop="2dp"/> + android:layout_marginBottom="8dp"/> + android:layout_marginBottom="8dp"/> + android:layout_marginBottom="8dp"/> - - - + + + + \ No newline at end of file