mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 23:27:26 +01:00
REF: Android Widget to Kotlin
This commit is contained in:
parent
f151668775
commit
27e99affd3
9 changed files with 249 additions and 365 deletions
|
@ -1,125 +0,0 @@
|
|||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.PowerManager;
|
||||
import android.util.Log;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
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_WIDGET";
|
||||
private static final long UPDATE_INTERVAL_MINUTES = 15; // Update interval in minutes
|
||||
private static final int MAX_RETRIES = 3;
|
||||
|
||||
private static PowerManager.WakeLock wakeLock;
|
||||
private static int retryCount = 0;
|
||||
private static boolean isScreenOn = true;
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
super.onEnabled(context);
|
||||
registerScreenReceiver(context);
|
||||
schedulePeriodicUpdates(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
super.onDisabled(context);
|
||||
unregisterScreenReceiver(context);
|
||||
WorkManager.getInstance(context).cancelUniqueWork("UpdateWidgetWork");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
super.onUpdate(context, appWidgetManager, appWidgetIds);
|
||||
if (isScreenOn) {
|
||||
scheduleWork(context);
|
||||
}
|
||||
}
|
||||
|
||||
private void schedulePeriodicUpdates(Context context) {
|
||||
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class,
|
||||
UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
.setInitialDelay(UPDATE_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
||||
"UpdateWidgetWork",
|
||||
ExistingPeriodicWorkPolicy.REPLACE,
|
||||
workRequest
|
||||
);
|
||||
}
|
||||
|
||||
private void registerScreenReceiver(Context context) {
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
filter.addAction(Intent.ACTION_SCREEN_OFF);
|
||||
context.getApplicationContext().registerReceiver(screenReceiver, filter);
|
||||
}
|
||||
|
||||
private void unregisterScreenReceiver(Context context) {
|
||||
context.getApplicationContext().unregisterReceiver(screenReceiver);
|
||||
}
|
||||
|
||||
private final BroadcastReceiver screenReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction() != null) {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
switch (intent.getAction()) {
|
||||
case Intent.ACTION_SCREEN_ON:
|
||||
isScreenOn = true;
|
||||
Log.d(TAG, "Screen ON");
|
||||
acquireWakeLock(context);
|
||||
scheduleWork(context);
|
||||
break;
|
||||
case Intent.ACTION_SCREEN_OFF:
|
||||
isScreenOn = false;
|
||||
Log.d(TAG, "Screen OFF");
|
||||
releaseWakeLock();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void acquireWakeLock(Context context) {
|
||||
if (wakeLock == null) {
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
if (pm != null) {
|
||||
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
|
||||
wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseWakeLock() {
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
wakeLock = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleWork(Context context) {
|
||||
Log.d(TAG, "Scheduling work for widget update");
|
||||
WorkManager.getInstance(context).enqueue(WidgetUpdateWorker.createWorkRequest());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package io.bluewallet.bluewallet
|
||||
|
||||
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.widget.RemoteViews
|
||||
import androidx.work.WorkManager
|
||||
|
||||
class BitcoinPriceWidget : AppWidgetProvider() {
|
||||
|
||||
override fun onEnabled(context: Context) {
|
||||
super.onEnabled(context)
|
||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||
val thisAppWidget = ComponentName(context.packageName, javaClass.name)
|
||||
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget)
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
WorkManager.getInstance(context).enqueue(WidgetUpdateWorker.createWorkRequest(appWidgetId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
|
||||
appWidgetIds.forEach { appWidgetId ->
|
||||
WorkManager.getInstance(context).enqueue(WidgetUpdateWorker.createWorkRequest(appWidgetId))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisabled(context: Context) {
|
||||
super.onDisabled(context)
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(javaClass.name)
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.os.Bundle;
|
||||
import com.facebook.react.ReactActivity;
|
||||
import com.facebook.react.ReactActivityDelegate;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||
|
||||
public class MainActivity extends ReactActivity {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
@Override
|
||||
protected String getMainComponentName() {
|
||||
return "BlueWallet";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(null);
|
||||
if (getResources().getBoolean(R.bool.portrait_only)) {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||
* (aka React 18) with two boolean flags.
|
||||
*/
|
||||
@Override
|
||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||
return new DefaultReactActivityDelegate(
|
||||
this,
|
||||
getMainComponentName(),
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.getFabricEnabled());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript.
|
||||
* This is used to schedule rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String {
|
||||
return "BlueWallet"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(null)
|
||||
if (resources.getBoolean(R.bool.portrait_only)) {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. Here we use a util class [DefaultReactActivityDelegate]
|
||||
* which allows you to easily enable Fabric and Concurrent React (aka React 18) with two boolean flags.
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||
return DefaultReactActivityDelegate(
|
||||
this,
|
||||
mainComponentName,
|
||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||
DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
package io.bluewallet.bluewallet;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.bugsnag.android.Bugsnag;
|
||||
import com.facebook.react.PackageList;
|
||||
import com.facebook.react.ReactApplication;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
import com.facebook.react.ReactPackage;
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||
import com.facebook.soloader.SoLoader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
||||
import java.util.List;
|
||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||
return packages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getJSMainModuleName() {
|
||||
return "index";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isNewArchEnabled() {
|
||||
return BuildConfig.IS_NEW_ARCHITECTURE_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) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// Schedule periodic widget updates
|
||||
PeriodicWorkRequest workRequest = new PeriodicWorkRequest.Builder(WidgetUpdateWorker.class, 4, TimeUnit.MINUTES).build();
|
||||
WorkManager.getInstance(this).enqueueUniquePeriodicWork("UpdateWidgetWork", ExistingPeriodicWorkPolicy.REPLACE, workRequest);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import com.bugsnag.android.Bugsnag
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactInstanceManager
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.soloader.SoLoader
|
||||
import com.facebook.react.modules.i18nmanager.I18nUtil
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
private val mReactNativeHost = object : DefaultReactNativeHost(this) {
|
||||
override fun getUseDeveloperSupport() = BuildConfig.DEBUG
|
||||
|
||||
override fun getPackages(): List<ReactPackage> {
|
||||
val packages = PackageList(this).packages
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
return packages
|
||||
}
|
||||
|
||||
override fun getJSMainModuleName() = "index"
|
||||
|
||||
override fun isNewArchEnabled() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
|
||||
override fun isHermesEnabled() = BuildConfig.IS_HERMES_ENABLED
|
||||
}
|
||||
|
||||
override fun getReactNativeHost() = mReactNativeHost
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
val sharedI18nUtilInstance = I18nUtil.getInstance()
|
||||
sharedI18nUtilInstance.allowRTL(applicationContext, 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()
|
||||
}
|
||||
SharedPreferences sharedPref = getApplicationContext().getSharedPreferences("group.io.bluewallet.bluewallet", Context.MODE_PRIVATE)
|
||||
|
||||
// Retrieve the "donottrack" value. Default to "0" if not found.
|
||||
val isDoNotTrackEnabled = sharedPref.getString("donottrack", "0")
|
||||
|
||||
// Check if do not track is not enabled and initialize Bugsnag if so
|
||||
if (isDoNotTrackEnabled != "1") {
|
||||
// Initialize Bugsnag or your error tracking here
|
||||
Bugsnag.start(this)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,108 +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 java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
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 PREFS_NAME = "BitcoinPriceWidgetPrefs";
|
||||
private static final String PREF_PREFIX_KEY_CURRENT = "appwidget_current_";
|
||||
private static final String PREF_PREFIX_KEY_PREVIOUS = "appwidget_previous_";
|
||||
|
||||
public WidgetUpdateWorker(@NonNull Context context, @NonNull WorkerParameters params) {
|
||||
super(context, params);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
Context context = getApplicationContext();
|
||||
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
|
||||
ComponentName thisWidget = new ComponentName(context, BitcoinPriceWidget.class);
|
||||
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
|
||||
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
|
||||
String prevPrice = prefs.getString(PREF_PREFIX_KEY_PREVIOUS + appWidgetId, "N/A");
|
||||
String currentPrice = prefs.getString(PREF_PREFIX_KEY_CURRENT + appWidgetId, "N/A");
|
||||
|
||||
Log.d(TAG, "Previous price: " + prevPrice);
|
||||
Log.d(TAG, "Current price: " + currentPrice);
|
||||
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
|
||||
|
||||
// Fetch the latest price
|
||||
String newPrice = MarketAPI.fetchPrice(context, "USD");
|
||||
if (newPrice != null) {
|
||||
String currentTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date());
|
||||
NumberFormat currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault());
|
||||
|
||||
// Update the widget views
|
||||
views.setTextViewText(R.id.price_value, currencyFormat.format(Double.parseDouble(newPrice)));
|
||||
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 (!prevPrice.equals("N/A") && !prevPrice.equals(newPrice)) {
|
||||
views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE);
|
||||
views.setTextViewText(R.id.previous_price, currencyFormat.format(Double.parseDouble(prevPrice)));
|
||||
views.setViewVisibility(R.id.previous_price_label, View.VISIBLE);
|
||||
views.setViewVisibility(R.id.previous_price, View.VISIBLE);
|
||||
if (Double.parseDouble(newPrice) > Double.parseDouble(prevPrice)) {
|
||||
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);
|
||||
views.setViewVisibility(R.id.previous_price_label, View.GONE);
|
||||
views.setViewVisibility(R.id.previous_price, View.GONE);
|
||||
}
|
||||
|
||||
// Save the new price and time
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putString(PREF_PREFIX_KEY_PREVIOUS + appWidgetId, currentPrice);
|
||||
editor.putString(PREF_PREFIX_KEY_CURRENT + appWidgetId, newPrice);
|
||||
editor.apply();
|
||||
|
||||
Log.d(TAG, "Fetch completed with price: " + newPrice + " at " + currentTime + ". Previous price: " + prevPrice);
|
||||
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
|
||||
// Log the next update time
|
||||
long nextUpdateTimeMillis = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(4);
|
||||
String nextUpdateTime = new SimpleDateFormat("hh:mm a", Locale.getDefault()).format(new Date(nextUpdateTimeMillis));
|
||||
Log.d(TAG, "Next fetch scheduled at: " + nextUpdateTime);
|
||||
} else {
|
||||
Log.e(TAG, "Failed to fetch Bitcoin price");
|
||||
views.setTextViewText(R.id.price_value, "Error");
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
}
|
||||
|
||||
public static OneTimeWorkRequest createWorkRequest() {
|
||||
return new OneTimeWorkRequest.Builder(WidgetUpdateWorker.class).build();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
package io.bluewallet.bluewallet
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.PeriodicWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import java.text.NumberFormat
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class WidgetUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
|
||||
companion object {
|
||||
private const val FETCH_INTERVAL_MINUTES = 15L
|
||||
private const val PREFS_NAME = "BitcoinPriceWidgetPrefs"
|
||||
private const val PREF_PREFIX_KEY = "appwidget_"
|
||||
|
||||
fun createWorkRequest(appWidgetId: Int): PeriodicWorkRequest {
|
||||
return PeriodicWorkRequest.Builder(WidgetUpdateWorker::class.java, FETCH_INTERVAL_MINUTES, TimeUnit.MINUTES)
|
||||
.addTag(appWidgetId.toString())
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
override fun doWork(): Result {
|
||||
val appWidgetId = inputData.getInt("appWidgetId", -1)
|
||||
if (appWidgetId == -1) return Result.failure()
|
||||
|
||||
val context = applicationContext
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val previousPrice = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null)
|
||||
val previousTime = prefs.getString("${PREF_PREFIX_KEY}${appWidgetId}_time", null)
|
||||
|
||||
val price = MarketAPI.fetchPrice(context, "USD")
|
||||
if (price != null) {
|
||||
updateWidgetWithPrice(context, appWidgetId, price, previousPrice, previousTime)
|
||||
Log.d("WidgetUpdateWorker", "Fetch completed with price: $price at ${getCurrentTime()}. Previous price: $previousPrice at $previousTime. Next fetch at: ${getNextFetchTime()}")
|
||||
} else {
|
||||
handleError(context, appWidgetId)
|
||||
Log.e("WidgetUpdateWorker", "Failed to fetch Bitcoin price. Next fetch at: ${getNextFetchTime()}")
|
||||
}
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun getCurrentTime(): String {
|
||||
return SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
|
||||
}
|
||||
|
||||
private fun getNextFetchTime(): String {
|
||||
val nextFetch = System.currentTimeMillis() + FETCH_INTERVAL_MINUTES * 60 * 1000
|
||||
return SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date(nextFetch))
|
||||
}
|
||||
|
||||
private fun updateWidgetWithPrice(context: Context, appWidgetId: Int, price: String, previousPrice: String?, previousTime: String?) {
|
||||
val views = RemoteViews(context.packageName, R.layout.widget_layout)
|
||||
val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit()
|
||||
val currencyFormat = NumberFormat.getCurrencyInstance(Locale.US)
|
||||
views.setTextViewText(R.id.price_value, currencyFormat.format(price.toDouble()))
|
||||
|
||||
if (previousPrice != null && previousTime != null) {
|
||||
val previousPriceValue = previousPrice.toDouble()
|
||||
val currentPriceValue = price.toDouble()
|
||||
if (currentPriceValue != previousPriceValue) {
|
||||
views.setTextViewText(R.id.previous_price, "From ${currencyFormat.format(previousPriceValue)}")
|
||||
views.setViewVisibility(R.id.price_arrow, View.VISIBLE)
|
||||
views.setViewVisibility(R.id.previous_price, View.VISIBLE)
|
||||
if (currentPriceValue > previousPriceValue) {
|
||||
views.setImageViewResource(R.id.price_arrow, R.drawable.ic_arrow_upward)
|
||||
} else {
|
||||
views.setImageViewResource(R.id.price_arrow, R.drawable.ic_arrow_downward)
|
||||
}
|
||||
} else {
|
||||
views.setViewVisibility(R.id.price_arrow, View.GONE)
|
||||
views.setViewVisibility(R.id.previous_price, View.GONE)
|
||||
}
|
||||
} else {
|
||||
views.setViewVisibility(R.id.price_arrow, View.GONE)
|
||||
views.setViewVisibility(R.id.previous_price, View.GONE)
|
||||
}
|
||||
|
||||
prefs.putString(PREF_PREFIX_KEY + appWidgetId, price)
|
||||
prefs.putString("${PREF_PREFIX_KEY}${appWidgetId}_time", getCurrentTime())
|
||||
prefs.apply()
|
||||
|
||||
views.setTextViewText(R.id.last_updated, "Last Updated")
|
||||
views.setTextViewText(R.id.last_updated_time, getCurrentTime())
|
||||
|
||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
|
||||
private fun handleError(context: Context, appWidgetId: Int) {
|
||||
val views = RemoteViews(context.packageName, R.layout.widget_layout)
|
||||
views.setTextViewText(R.id.price_value, "Error")
|
||||
views.setViewVisibility(R.id.price_arrow, View.GONE)
|
||||
views.setViewVisibility(R.id.previous_price, View.GONE)
|
||||
|
||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:initialLayout="@layout/widget_layout"
|
||||
android:minWidth="160dp"
|
||||
android:minHeight="140dp"
|
||||
android:minHeight="80dp"
|
||||
android:updatePeriodMillis="0"
|
||||
android:widgetCategory="home_screen"
|
||||
android:previewImage="@drawable/widget_preview"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
android:minResizeWidth="160dp"
|
||||
android:minResizeHeight="140dp" />
|
||||
android:resizeMode="none" />
|
||||
|
|
Loading…
Add table
Reference in a new issue