diff --git a/app/build.gradle b/app/build.gradle
index deb9afdca..c3b04a84f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -104,6 +104,7 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
implementation "com.google.dagger:dagger:2.50"
+ implementation 'androidx.glance:glance-appwidget:1.1.0'
ksp "com.google.dagger:dagger-compiler:2.50"
implementation "androidx.room:room-runtime:2.6.1"
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 34fd6830c..59b5efeea 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -31,6 +31,17 @@
+
+
+
+
+
+
+
+
+ QuickPairsWidgetRefreshWorker(
+ params = workerParameters,
+ context = appContext,
+ )
+
+ else -> null
+ }
+ }
+}
diff --git a/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt b/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt
index 2429e6ef9..63766677c 100644
--- a/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt
+++ b/app/src/main/java/dev/arkbuilders/rate/di/AppComponent.kt
@@ -5,6 +5,7 @@ import android.content.Context
import dagger.BindsInstance
import dagger.Component
import dev.arkbuilders.rate.data.repo.PortfolioRepoImpl
+import dev.arkbuilders.rate.data.repo.QuickRepoImpl
import dev.arkbuilders.rate.data.repo.currency.CurrencyRepoImpl
import dev.arkbuilders.rate.data.worker.AppWorkerFactory
import dev.arkbuilders.rate.data.worker.CurrencyMonitorWorker
@@ -14,6 +15,7 @@ import dev.arkbuilders.rate.di.module.RepoModule
import dev.arkbuilders.rate.domain.repo.NetworkStatus
import dev.arkbuilders.rate.domain.repo.Prefs
import dev.arkbuilders.rate.domain.usecase.CalcFrequentCurrUseCase
+import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase
import dev.arkbuilders.rate.presentation.pairalert.AddPairAlertViewModelFactory
import dev.arkbuilders.rate.presentation.pairalert.PairAlertViewModelFactory
import dev.arkbuilders.rate.presentation.portfolio.AddAssetViewModelFactory
@@ -62,6 +64,10 @@ interface AppComponent {
fun assetsRepo(): PortfolioRepoImpl
+ fun quickRepo(): QuickRepoImpl
+
+ fun convertUseCase(): ConvertWithRateUseCase
+
fun inject(currencyMonitorWorker: CurrencyMonitorWorker)
fun calcFrequentCurrUseCase(): CalcFrequentCurrUseCase
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt
index 3685ff711..75dd1c98e 100644
--- a/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/App.kt
@@ -4,6 +4,7 @@ import android.app.Application
import androidx.work.Configuration
import androidx.work.Constraints
import androidx.work.ExistingPeriodicWorkPolicy
+import androidx.work.ListenableWorker
import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
@@ -11,6 +12,7 @@ import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import dev.arkbuilders.rate.BuildConfig
import dev.arkbuilders.rate.data.worker.CurrencyMonitorWorker
+import dev.arkbuilders.rate.data.worker.QuickPairsWidgetRefreshWorker
import dev.arkbuilders.rate.di.DIManager
import dev.arkbuilders.rate.domain.repo.PreferenceKey
import kotlinx.coroutines.CoroutineScope
@@ -26,7 +28,8 @@ class App : Application(), Configuration.Provider {
DIManager.init(this)
initCrashlytics()
- initWorker()
+ initWorker(CurrencyMonitorWorker::class.java, CurrencyMonitorWorker.NAME)
+ initWorker(QuickPairsWidgetRefreshWorker::class.java, QuickPairsWidgetRefreshWorker.NAME)
}
private fun initCrashlytics() =
@@ -41,9 +44,11 @@ class App : Application(), Configuration.Provider {
Firebase.crashlytics.setCrashlyticsCollectionEnabled(collect)
}
- private fun initWorker() {
+ private fun initWorker(
+ workerClass: Class,
+ workerName: String,
+ ) {
val workManager = WorkManager.getInstance(this)
-
val constraints =
Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
@@ -51,14 +56,14 @@ class App : Application(), Configuration.Provider {
val workRequest =
PeriodicWorkRequest.Builder(
- CurrencyMonitorWorker::class.java,
+ workerClass,
8L,
TimeUnit.HOURS,
).setConstraints(constraints)
.build()
workManager.enqueueUniquePeriodicWork(
- CurrencyMonitorWorker.NAME,
+ workerName,
ExistingPeriodicWorkPolicy.KEEP,
workRequest,
)
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt
index a350dafc3..f69afdb07 100644
--- a/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/MainActivity.kt
@@ -1,10 +1,12 @@
package dev.arkbuilders.rate.presentation
+import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.view.WindowCompat
+import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver
import dev.arkbuilders.rate.presentation.theme.ARKRateTheme
class MainActivity : ComponentActivity() {
@@ -18,4 +20,13 @@ class MainActivity : ComponentActivity() {
}
}
}
+
+ override fun onStop() {
+ sendBroadcast(
+ Intent(this, QuickPairsWidgetReceiver::class.java).apply {
+ action = QuickPairsWidgetReceiver.PINNED_PAIRS_REFRESH
+ },
+ )
+ super.onStop()
+ }
}
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt
index b2a47d305..772a9c52a 100644
--- a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/QuickScreen.kt
@@ -1,11 +1,8 @@
-@file:OptIn(
- ExperimentalComposeUiApi::class,
- ExperimentalFoundationApi::class,
-)
-
package dev.arkbuilders.rate.presentation.quick
-import androidx.compose.foundation.ExperimentalFoundationApi
+import android.app.Activity
+import android.content.Context
+import android.content.ContextWrapper
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@@ -29,12 +26,12 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
@@ -61,6 +58,7 @@ import dev.arkbuilders.rate.domain.model.CurrencyName
import dev.arkbuilders.rate.domain.model.PinnedQuickPair
import dev.arkbuilders.rate.domain.model.QuickPair
import dev.arkbuilders.rate.presentation.destinations.AddQuickScreenDestination
+import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction.Companion.ADD_NEW_PAIR
import dev.arkbuilders.rate.presentation.theme.ArkColor
import dev.arkbuilders.rate.presentation.ui.AppButton
import dev.arkbuilders.rate.presentation.ui.AppHorDiv16
@@ -93,7 +91,15 @@ fun QuickScreen(navigator: DestinationsNavigator) {
val state by viewModel.collectAsState()
val snackState = remember { SnackbarHostState() }
val ctx = LocalContext.current
-
+ LaunchedEffect(key1 = Unit) {
+ val activity = ctx.findActivity()
+ val intent = activity?.intent
+ val createNewPair = intent?.getStringExtra(ADD_NEW_PAIR) ?: ""
+ if (createNewPair.isNotEmpty()) {
+ navigator.navigate(AddQuickScreenDestination())
+ intent?.removeExtra(ADD_NEW_PAIR)
+ }
+ }
viewModel.collectSideEffect { effect ->
when (effect) {
is QuickScreenEffect.ShowSnackbarAdded ->
@@ -585,3 +591,10 @@ private fun QuickEmpty(navigator: DestinationsNavigator) {
}
}
}
+
+fun Context.findActivity(): Activity? =
+ when (this) {
+ is Activity -> this
+ is ContextWrapper -> baseContext.findActivity()
+ else -> null
+ }
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt
new file mode 100644
index 000000000..99f7e7d53
--- /dev/null
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairItem.kt
@@ -0,0 +1,92 @@
+package dev.arkbuilders.rate.presentation.quick.glancewidget
+
+import android.content.Context
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.dp
+import androidx.glance.GlanceModifier
+import androidx.glance.Image
+import androidx.glance.ImageProvider
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Column
+import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
+import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.padding
+import androidx.glance.layout.size
+import androidx.glance.layout.width
+import androidx.glance.text.FontWeight
+import androidx.glance.text.Text
+import androidx.glance.text.TextStyle
+import androidx.glance.unit.ColorProvider
+import dev.arkbuilders.rate.data.CurrUtils
+import dev.arkbuilders.rate.domain.model.PinnedQuickPair
+import dev.arkbuilders.rate.presentation.theme.ArkColor
+import dev.arkbuilders.rate.presentation.utils.IconUtils
+
+@Composable
+fun QuickPairItem(
+ quick: PinnedQuickPair,
+ context: Context,
+) {
+ Row(
+ modifier = GlanceModifier.padding(vertical = 2.dp),
+ ) {
+ Image(
+ modifier = GlanceModifier.size(24.dp),
+ provider =
+ ImageProvider(
+ IconUtils.iconForCurrCode(
+ context,
+ quick.pair.from,
+ ),
+ ),
+ contentDescription = null,
+ )
+ Column(
+ modifier = GlanceModifier.padding(start = 8.dp),
+ verticalAlignment = Alignment.Vertical.CenterVertically,
+ ) {
+ Text(
+ text = "${quick.pair.from} to ${quick.pair.to.joinToString(
+ separator = ", ",
+ ) { it.code }}",
+ style =
+ TextStyle(
+ color = ColorProvider(ArkColor.TextPrimary),
+ fontWeight = FontWeight.Medium,
+ ),
+ )
+
+ Text(
+ text = "${CurrUtils.prepareToDisplay(quick.pair.amount)} ${quick.pair.from} = ",
+ style =
+ TextStyle(
+ color = ColorProvider(ArkColor.TextTertiary),
+ ),
+ )
+ for (toAmount in quick.actualTo) {
+ Row(modifier = GlanceModifier.fillMaxWidth()) {
+ Image(
+ modifier = GlanceModifier.size(16.dp),
+ provider =
+ ImageProvider(
+ IconUtils.iconForCurrCode(
+ context,
+ toAmount.code,
+ ),
+ ),
+ contentDescription = null,
+ )
+ Spacer(modifier = GlanceModifier.width(4.dp))
+ Text(
+ text = "${CurrUtils.prepareToDisplay(toAmount.value)} ${toAmount.code}",
+ style =
+ TextStyle(
+ color = ColorProvider(ArkColor.TextTertiary),
+ ),
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt
new file mode 100644
index 000000000..9db64cf5e
--- /dev/null
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidget.kt
@@ -0,0 +1,125 @@
+package dev.arkbuilders.rate.presentation.quick.glancewidget
+
+import android.content.Context
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.unit.dp
+import androidx.datastore.preferences.core.Preferences
+import androidx.glance.GlanceId
+import androidx.glance.GlanceModifier
+import androidx.glance.Image
+import androidx.glance.ImageProvider
+import androidx.glance.action.clickable
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.action.actionRunCallback
+import androidx.glance.appwidget.lazy.LazyColumn
+import androidx.glance.appwidget.lazy.items
+import androidx.glance.appwidget.provideContent
+import androidx.glance.background
+import androidx.glance.currentState
+import androidx.glance.layout.Alignment
+import androidx.glance.layout.Column
+import androidx.glance.layout.Row
+import androidx.glance.layout.Spacer
+import androidx.glance.layout.fillMaxHeight
+import androidx.glance.layout.fillMaxSize
+import androidx.glance.layout.fillMaxWidth
+import androidx.glance.layout.height
+import androidx.glance.layout.padding
+import androidx.glance.layout.size
+import androidx.glance.text.FontWeight
+import androidx.glance.text.Text
+import androidx.glance.text.TextStyle
+import androidx.glance.unit.ColorProvider
+import com.google.gson.GsonBuilder
+import dev.arkbuilders.rate.R
+import dev.arkbuilders.rate.domain.model.PinnedQuickPair
+import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction
+import dev.arkbuilders.rate.presentation.quick.glancewidget.action.OpenAppAction
+import dev.arkbuilders.rate.presentation.theme.ArkColor
+
+class QuickPairsWidget : GlanceAppWidget() {
+ override suspend fun provideGlance(
+ context: Context,
+ id: GlanceId,
+ ) {
+ provideContent {
+ val prefs = currentState()
+ val quickPairsString = prefs[QuickPairsWidgetReceiver.quickDisplayPairs]
+ val quickPairsList = quickPairsString?.let { parseQuickPairs(it) }
+ Column(
+ modifier =
+ GlanceModifier.fillMaxSize().background(Color.White)
+ .padding(horizontal = 12.dp),
+ ) {
+ Row(
+ modifier = GlanceModifier.fillMaxWidth().padding(top = 8.dp),
+ verticalAlignment = Alignment.Vertical.CenterVertically,
+ ) {
+ Image(
+ modifier =
+ GlanceModifier.size(24.dp).padding(4.dp)
+ .clickable(actionRunCallback()),
+ provider =
+ ImageProvider(
+ R.drawable.ic_about_logo,
+ ),
+ contentDescription = null,
+ )
+ Text(
+ modifier = GlanceModifier.defaultWeight(),
+ text = context.getString(R.string.quick_pinned_pairs),
+ style =
+ TextStyle(
+ color = ColorProvider(ArkColor.TextTertiary),
+ fontWeight = FontWeight.Medium,
+ ),
+ )
+ Image(
+ modifier =
+ GlanceModifier.size(24.dp).padding(4.dp)
+ .clickable(actionRunCallback()),
+ provider =
+ ImageProvider(
+ R.drawable.ic_add_circle,
+ ),
+ contentDescription = null,
+ )
+ Text(
+ modifier =
+ GlanceModifier
+ .clickable(actionRunCallback()),
+ text = context.getString(R.string.quick_open_app),
+ style =
+ TextStyle(
+ color = ColorProvider(ArkColor.Primary),
+ fontWeight = FontWeight.Medium,
+ ),
+ )
+ }
+ LazyColumn(modifier = GlanceModifier.fillMaxHeight()) {
+ quickPairsList?.let { pairs ->
+ items(pairs) { quick ->
+ Column {
+ QuickPairItem(
+ quick = quick,
+ context = context,
+ )
+ Spacer(
+ modifier =
+ GlanceModifier.fillMaxWidth().height(1.dp)
+ .background(Color.Gray.copy(alpha = 0.2f))
+ .padding(vertical = 2.dp),
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private fun parseQuickPairs(quickPairsString: String): List {
+ val gson = GsonBuilder().create()
+ return gson.fromJson(quickPairsString, Array::class.java).toList()
+ }
+}
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt
new file mode 100644
index 000000000..20dd7c402
--- /dev/null
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/QuickPairsWidgetReceiver.kt
@@ -0,0 +1,122 @@
+package dev.arkbuilders.rate.presentation.quick.glancewidget
+
+import android.appwidget.AppWidgetManager
+import android.content.Context
+import android.content.Intent
+import androidx.datastore.preferences.core.stringPreferencesKey
+import androidx.glance.appwidget.GlanceAppWidget
+import androidx.glance.appwidget.GlanceAppWidgetManager
+import androidx.glance.appwidget.GlanceAppWidgetReceiver
+import androidx.glance.appwidget.state.updateAppWidgetState
+import androidx.glance.state.PreferencesGlanceStateDefinition
+import com.google.gson.GsonBuilder
+import dev.arkbuilders.rate.di.DIManager
+import dev.arkbuilders.rate.domain.model.PinnedQuickPair
+import dev.arkbuilders.rate.domain.model.QuickPair
+import dev.arkbuilders.rate.domain.repo.QuickRepo
+import dev.arkbuilders.rate.domain.usecase.ConvertWithRateUseCase
+import dev.arkbuilders.rate.presentation.MainActivity
+import dev.arkbuilders.rate.presentation.quick.QuickScreenPage
+import dev.arkbuilders.rate.presentation.quick.glancewidget.action.AddNewPairAction
+import dev.arkbuilders.rate.presentation.quick.glancewidget.action.OpenAppAction
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import timber.log.Timber
+import java.time.OffsetDateTime
+
+class QuickPairsWidgetReceiver(
+ private val quickRepo: QuickRepo = DIManager.component.quickRepo(),
+ private val convertUseCase: ConvertWithRateUseCase = DIManager.component.convertUseCase(),
+) : GlanceAppWidgetReceiver() {
+ private val coroutineScope = MainScope()
+
+ override val glanceAppWidget: GlanceAppWidget = QuickPairsWidget()
+
+ override fun onReceive(
+ context: Context,
+ intent: Intent,
+ ) {
+ super.onReceive(context, intent)
+ val action = intent.action
+ Timber.d(action)
+ when (action) {
+ AppWidgetManager.ACTION_APPWIDGET_ENABLED, PINNED_PAIRS_REFRESH ->
+ getQuickPairs(context)
+ OpenAppAction.OPEN_APP -> {
+ context.startActivity(
+ Intent(context, MainActivity::class.java).apply {
+ setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ },
+ )
+ }
+ AddNewPairAction.ADD_NEW_PAIR -> {
+ context.startActivity(
+ Intent(context, MainActivity::class.java).apply {
+ putExtra(AddNewPairAction.ADD_NEW_PAIR, "ADD_NEW_PAIR")
+ setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ },
+ )
+ }
+ }
+ }
+
+ override fun onUpdate(
+ context: Context,
+ appWidgetManager: AppWidgetManager,
+ appWidgetIds: IntArray,
+ ) {
+ super.onUpdate(context, appWidgetManager, appWidgetIds)
+ getQuickPairs(context)
+ }
+
+ private fun getQuickPairs(context: Context) {
+ Timber.d("Get quick pairs for widget")
+ quickRepo.allFlow().onEach { quick ->
+ val pages = mapPairsToPages(quick)
+ val quickDisplayPair = pages.first().pinned
+ val quickPairs = GsonBuilder().create().toJson(quickDisplayPair)
+ val glanceIds =
+ GlanceAppWidgetManager(context).getGlanceIds(QuickPairsWidget::class.java)
+ for (glanceId in glanceIds) {
+ glanceId.let { id ->
+ updateAppWidgetState(context, PreferencesGlanceStateDefinition, id) { pref ->
+ pref.toMutablePreferences().apply {
+ this[quickDisplayPairs] = quickPairs
+ }
+ }
+ glanceAppWidget.update(context, id)
+ }
+ }
+ }.launchIn(coroutineScope)
+ }
+
+ private suspend fun mapPairsToPages(pairs: List): List {
+ val pages =
+ pairs
+ .reversed()
+ .groupBy { it.group }
+ .map { (group, pairs) ->
+ val pinnedQuickPairs = pairs.filter { it.isPinned() }
+ val pinnedMappedQuickPairs = pinnedQuickPairs.map { mapPairToPinned(it) }
+ val sortedPinned =
+ pinnedMappedQuickPairs.sortedByDescending { it.pair.pinnedDate }
+ QuickScreenPage(group, sortedPinned, listOf())
+ }
+ return pages
+ }
+
+ private suspend fun mapPairToPinned(pair: QuickPair): PinnedQuickPair {
+ val actualTo =
+ pair.to.map { to ->
+ val (amount, _) = convertUseCase.invoke(pair.from, pair.amount, to.code)
+ amount
+ }
+ return PinnedQuickPair(pair, actualTo, OffsetDateTime.now())
+ }
+
+ companion object {
+ val quickDisplayPairs = stringPreferencesKey("quick_pair_display")
+ const val PINNED_PAIRS_REFRESH = "PINNED_PAIRS_REFRESH"
+ }
+}
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt
new file mode 100644
index 000000000..9cf90980b
--- /dev/null
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/AddNewPairAction.kt
@@ -0,0 +1,26 @@
+package dev.arkbuilders.rate.presentation.quick.glancewidget.action
+
+import android.content.Context
+import android.content.Intent
+import androidx.glance.GlanceId
+import androidx.glance.action.ActionParameters
+import androidx.glance.appwidget.action.ActionCallback
+import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver
+
+class AddNewPairAction : ActionCallback {
+ override suspend fun onAction(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters,
+ ) {
+ val intent =
+ Intent(context, QuickPairsWidgetReceiver::class.java).apply {
+ action = ADD_NEW_PAIR
+ }
+ context.sendBroadcast(intent)
+ }
+
+ companion object {
+ const val ADD_NEW_PAIR = "QUICK_PAIRS_WIDGET_NEW_PAIR"
+ }
+}
diff --git a/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt
new file mode 100644
index 000000000..02c1e4958
--- /dev/null
+++ b/app/src/main/java/dev/arkbuilders/rate/presentation/quick/glancewidget/action/OpenAppAction.kt
@@ -0,0 +1,26 @@
+package dev.arkbuilders.rate.presentation.quick.glancewidget.action
+
+import android.content.Context
+import android.content.Intent
+import androidx.glance.GlanceId
+import androidx.glance.action.ActionParameters
+import androidx.glance.appwidget.action.ActionCallback
+import dev.arkbuilders.rate.presentation.quick.glancewidget.QuickPairsWidgetReceiver
+
+class OpenAppAction : ActionCallback {
+ override suspend fun onAction(
+ context: Context,
+ glanceId: GlanceId,
+ parameters: ActionParameters,
+ ) {
+ val intent =
+ Intent(context, QuickPairsWidgetReceiver::class.java).apply {
+ action = OPEN_APP
+ }
+ context.sendBroadcast(intent)
+ }
+
+ companion object {
+ const val OPEN_APP = "QUICK_PAIRS_WIDGET_OPEN_APP"
+ }
+}
diff --git a/app/src/main/res/drawable/ic_add_circle.xml b/app/src/main/res/drawable/ic_add_circle.xml
new file mode 100644
index 000000000..eed3306a0
--- /dev/null
+++ b/app/src/main/res/drawable/ic_add_circle.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1a36d6cc1..7492cde00 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -22,6 +22,7 @@
The calculation has been deleted
You deleted %1$s calculation
%1$s to %2$s
+ Open app
Asset detail
diff --git a/app/src/main/res/xml/glance_widget_provider.xml b/app/src/main/res/xml/glance_widget_provider.xml
new file mode 100644
index 000000000..b02129196
--- /dev/null
+++ b/app/src/main/res/xml/glance_widget_provider.xml
@@ -0,0 +1,13 @@
+
+
+
\ No newline at end of file