mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-02-23 07:15:35 +01:00
wip
This commit is contained in:
parent
3e394373e5
commit
c3418d0678
5 changed files with 169 additions and 174 deletions
|
@ -7,33 +7,26 @@ import android.widget.RemoteViews
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import androidx.work.testing.WorkManagerTestInitHelper
|
import androidx.work.testing.WorkManagerTestInitHelper
|
||||||
import androidx.work.WorkManager
|
import com.squareup.okhttp.mockwebserver.MockResponse
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.squareup.okhttp.mockwebserver.MockWebServer
|
||||||
import okhttp3.mockwebserver.MockResponse
|
|
||||||
import okhttp3.mockwebserver.MockWebServer
|
|
||||||
import org.json.JSONObject
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class MockServerTest {
|
class MockServerTest {
|
||||||
|
|
||||||
private lateinit var mockWebServer: MockWebServer
|
private lateinit var mockWebServer: MockWebServer
|
||||||
private lateinit var context: Context
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
context = ApplicationProvider.getApplicationContext()
|
|
||||||
mockWebServer = MockWebServer()
|
mockWebServer = MockWebServer()
|
||||||
mockWebServer.start()
|
mockWebServer.start()
|
||||||
|
MarketAPI.baseUrl = mockWebServer.url("/").toString()
|
||||||
val baseUrl = mockWebServer.url("/").toString()
|
|
||||||
MarketAPI.baseUrl = baseUrl
|
|
||||||
|
|
||||||
WorkManagerTestInitHelper.initializeTestWorkManager(context)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -43,45 +36,39 @@ class MockServerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testWidgetUpdate() {
|
fun testWidgetUpdate() {
|
||||||
// Mock API response
|
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||||
val mockResponse = MockResponse()
|
WorkManagerTestInitHelper.initializeTestWorkManager(context)
|
||||||
.setBody(createMockJsonResponse("USD", "61500"))
|
|
||||||
.setResponseCode(200)
|
val prices = listOf("60000", "60500", "61000", "61500", "62000")
|
||||||
mockWebServer.enqueue(mockResponse)
|
prices.forEach {
|
||||||
|
mockWebServer.enqueue(MockResponse().setBody("""{"USD": {"price": "$it"}}"""))
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger the widget update
|
|
||||||
WidgetUpdateWorker.scheduleWork(context)
|
WidgetUpdateWorker.scheduleWork(context)
|
||||||
|
WorkManagerTestInitHelper.getTestDriver()?.setAllConstraintsMet(WidgetUpdateWorker.WORK_NAME)
|
||||||
|
|
||||||
// Wait for the worker to run
|
|
||||||
val workInfos = WorkManager.getInstance(context).getWorkInfosByTag(WidgetUpdateWorker.WORK_NAME).get()
|
|
||||||
val testDriver = WorkManagerTestInitHelper.getTestDriver()
|
|
||||||
for (workInfo in workInfos) {
|
|
||||||
testDriver?.setAllConstraintsMet(workInfo.id)
|
|
||||||
}
|
|
||||||
Thread.sleep(10000) // Wait for 10 seconds to simulate the update interval
|
|
||||||
|
|
||||||
// Validate the widget updates
|
|
||||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||||
val widgetIds = appWidgetManager.getAppWidgetIds(ComponentName(context, BitcoinPriceWidget::class.java))
|
val thisWidget = ComponentName(context, BitcoinPriceWidget::class.java)
|
||||||
|
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
|
||||||
|
val views = RemoteViews(context.packageName, R.layout.widget_layout)
|
||||||
|
|
||||||
for (widgetId in widgetIds) {
|
prices.forEachIndexed { index, price ->
|
||||||
val views = RemoteViews(context.packageName, R.layout.widget_layout)
|
val currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()).apply {
|
||||||
// Add your assertions here to validate the widget update
|
maximumFractionDigits = 0
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createMockJsonResponse(currency: String, price: String): String {
|
|
||||||
return """
|
|
||||||
{
|
|
||||||
"$currency": {
|
|
||||||
"endPointKey": "$currency",
|
|
||||||
"locale": "en-US",
|
|
||||||
"source": "Kraken",
|
|
||||||
"symbol": "$",
|
|
||||||
"country": "United States (US Dollar)",
|
|
||||||
"price": "$price"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
val formattedPrice = currencyFormat.format(price.toDouble())
|
||||||
|
views.setTextViewText(R.id.price_value, formattedPrice)
|
||||||
|
views.setTextViewText(R.id.last_updated_time, SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date()))
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
views.setViewVisibility(R.id.price_arrow_container, View.VISIBLE)
|
||||||
|
views.setTextViewText(R.id.previous_price, currencyFormat.format(prices[index - 1].toDouble()))
|
||||||
|
} else {
|
||||||
|
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
|
||||||
|
}
|
||||||
|
|
||||||
|
appWidgetManager.updateAppWidget(appWidgetIds, views)
|
||||||
|
Thread.sleep(5000) // Wait for 5 seconds before updating to the next price
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,65 +8,77 @@ import android.view.View
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import java.util.Locale
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class WidgetUpdateWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
|
class WidgetUpdateWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "WidgetUpdateWorker"
|
||||||
|
const val WORK_NAME = "widget_update_work"
|
||||||
|
const val REPEAT_INTERVAL_MINUTES = 15L
|
||||||
|
|
||||||
|
fun scheduleWork(context: Context) {
|
||||||
|
val workRequest = PeriodicWorkRequestBuilder<WidgetUpdateWorker>(
|
||||||
|
REPEAT_INTERVAL_MINUTES, TimeUnit.MINUTES
|
||||||
|
).build()
|
||||||
|
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
||||||
|
WORK_NAME,
|
||||||
|
ExistingPeriodicWorkPolicy.REPLACE,
|
||||||
|
workRequest
|
||||||
|
)
|
||||||
|
Log.d(TAG, "Scheduling work for widget updates, will run every $REPEAT_INTERVAL_MINUTES minutes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
|
Log.d(TAG, "Widget update worker running")
|
||||||
val widgetComponent = ComponentName(applicationContext, BitcoinPriceWidget::class.java)
|
|
||||||
val allWidgetIds = appWidgetManager.getAppWidgetIds(widgetComponent)
|
|
||||||
|
|
||||||
val price = MarketAPI.fetchPrice(applicationContext, "USD")
|
val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
|
||||||
|
val thisWidget = ComponentName(applicationContext, BitcoinPriceWidget::class.java)
|
||||||
|
val appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget)
|
||||||
val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
|
val views = RemoteViews(applicationContext.packageName, R.layout.widget_layout)
|
||||||
|
|
||||||
if (price != null) {
|
// Simulate fetching price data
|
||||||
val formattedPrice = NumberFormat.getCurrencyInstance(Locale.getDefault()).format(price.toDouble())
|
val currentTime = SimpleDateFormat("hh:mm a", Locale.getDefault()).format(Date())
|
||||||
|
val price = fetchPrice() // Simulated method to fetch the price
|
||||||
|
val previousPrice = getPreviousPrice() // Simulated method to fetch the previous price
|
||||||
|
|
||||||
views.setTextViewText(R.id.price_value, formattedPrice)
|
// Log fetched data
|
||||||
views.setViewVisibility(R.id.loading_indicator, View.GONE)
|
Log.d(TAG, "Fetch completed with price: $price at $currentTime. Previous price: $previousPrice")
|
||||||
views.setViewVisibility(R.id.price_value, View.VISIBLE)
|
|
||||||
views.setViewVisibility(R.id.last_updated, View.VISIBLE)
|
|
||||||
|
|
||||||
val currentTime = System.currentTimeMillis()
|
// Update views
|
||||||
val formattedTime = java.text.DateFormat.getTimeInstance().format(currentTime)
|
val currencyFormat = NumberFormat.getCurrencyInstance(Locale.getDefault()).apply {
|
||||||
views.setTextViewText(R.id.last_updated, "Last Updated: $formattedTime")
|
maximumFractionDigits = 0
|
||||||
|
}
|
||||||
|
views.setTextViewText(R.id.price_value, currencyFormat.format(price.toDouble()))
|
||||||
|
views.setTextViewText(R.id.last_updated_time, currentTime)
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
Log.d("WidgetUpdateWorker", "Failed to fetch price")
|
views.setViewVisibility(R.id.price_arrow_container, View.GONE)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (widgetId in allWidgetIds) {
|
appWidgetManager.updateAppWidget(appWidgetIds, views)
|
||||||
appWidgetManager.updateAppWidget(widgetId, views)
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleNextUpdate()
|
|
||||||
return Result.success()
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun scheduleNextUpdate() {
|
private fun fetchPrice(): String {
|
||||||
val request = OneTimeWorkRequestBuilder<WidgetUpdateWorker>()
|
// Simulate a network call to fetch the price
|
||||||
.setInitialDelay(10, TimeUnit.MINUTES)
|
return (60000 + Random().nextInt(5000)).toString() // Replace with actual fetch logic
|
||||||
.build()
|
|
||||||
|
|
||||||
WorkManager.getInstance(applicationContext).enqueueUniqueWork(
|
|
||||||
"widget_update_work",
|
|
||||||
ExistingWorkPolicy.REPLACE,
|
|
||||||
request
|
|
||||||
)
|
|
||||||
|
|
||||||
Log.d("WidgetUpdateWorker", "Scheduled next update for widget")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
private fun getPreviousPrice(): String? {
|
||||||
fun createWorkRequest(): OneTimeWorkRequest {
|
// Simulate retrieving the previous price from shared preferences or a database
|
||||||
return OneTimeWorkRequestBuilder<WidgetUpdateWorker>()
|
return (60000 + Random().nextInt(5000)).toString() // Replace with actual retrieval logic
|
||||||
.setInitialDelay(0, TimeUnit.SECONDS)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scheduleWork(context: Context) {
|
|
||||||
WorkManager.getInstance(context).enqueue(createWorkRequest())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,57 +1,95 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="wrap_content"
|
android:id="@+id/widget_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:background="@drawable/widget_background"
|
android:background="@drawable/widget_background"
|
||||||
android:layout_margin="16dp"
|
android:gravity="end">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/price_value"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/loading"
|
android:orientation="vertical"
|
||||||
android:textSize="18sp"
|
android:gravity="end">
|
||||||
android:textColor="@color/text_primary"
|
<TextView
|
||||||
android:layout_centerHorizontal="true"/>
|
android:id="@+id/last_updated_label"
|
||||||
|
style="@style/WidgetTextSecondary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Last Updated"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginEnd="8dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/last_updated"
|
android:id="@+id/last_updated_time"
|
||||||
android:layout_width="wrap_content"
|
style="@style/WidgetTextPrimary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginTop="2dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/last_updated"
|
android:orientation="vertical"
|
||||||
android:textSize="12sp"
|
android:gravity="end">
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:layout_below="@id/price_value"
|
<TextView
|
||||||
android:layout_centerHorizontal="true"
|
android:id="@+id/price_value"
|
||||||
android:visibility="gone"
|
style="@style/WidgetTextPrimary"
|
||||||
android:layout_marginTop="8dp"/>
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="24sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/price_arrow_container"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/price_arrow"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="4dp"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/previous_price_label"
|
||||||
|
style="@style/WidgetTextPrimary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="From:"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/previous_price"
|
||||||
|
style="@style/WidgetTextPrimary"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text=""
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
android:layout_marginBottom="8dp"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/loading_indicator"
|
android:id="@+id/loading_indicator"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerHorizontal="true"
|
android:layout_gravity="center"
|
||||||
android:layout_centerVertical="true"
|
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
</LinearLayout>
|
||||||
<ImageView
|
|
||||||
android:id="@+id/price_arrow"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@id/price_value"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/previous_price"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="14sp"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:layout_below="@id/price_arrow"
|
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:visibility="gone"/>
|
|
||||||
</RelativeLayout>
|
|
|
@ -2,7 +2,7 @@
|
||||||
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:initialLayout="@layout/widget_layout"
|
android:initialLayout="@layout/widget_layout"
|
||||||
android:minWidth="130dp"
|
android:minWidth="130dp"
|
||||||
android:minHeight="40dp"
|
android:minHeight="80dp"
|
||||||
android:updatePeriodMillis="0"
|
android:updatePeriodMillis="0"
|
||||||
android:widgetCategory="home_screen"
|
android:widgetCategory="home_screen"
|
||||||
android:previewImage="@drawable/widget_preview"
|
android:previewImage="@drawable/widget_preview"
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
{
|
|
||||||
"originHash" : "52530e6b1e3a85c8854952ef703a6d1bbe1acd82713be2b3166476b9b277db23",
|
|
||||||
"pins" : [
|
|
||||||
{
|
|
||||||
"identity" : "bugsnag-cocoa",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/bugsnag/bugsnag-cocoa",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "16b9145fc66e5296f16e733f6feb5d0e450574e8",
|
|
||||||
"version" : "6.28.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "efqrcode",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/EFPrefix/EFQRCode.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "2991c2f318ad9529d93b2a73a382a3f9c72c64ce",
|
|
||||||
"version" : "6.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "keychain-swift",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/evgenyneu/keychain-swift.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "5e1b02b6a9dac2a759a1d5dbc175c86bd192a608",
|
|
||||||
"version" : "24.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"identity" : "swift_qrcodejs",
|
|
||||||
"kind" : "remoteSourceControl",
|
|
||||||
"location" : "https://github.com/ApolloZhu/swift_qrcodejs.git",
|
|
||||||
"state" : {
|
|
||||||
"revision" : "374dc7f7b9e76c6aeb393f6a84590c6d387e1ecb",
|
|
||||||
"version" : "2.2.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"version" : 3
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue