I have made a home screen widget with compose glance.
It works perfectly on API 32.
But for all API < 32, the widget is not updating when new preferences/state are updated.
What I have tried :
Understanding deeper the way state preferences interact with GlanceWidget to interact with the data, but it seems I made my best...
Here is my GlanceAppWidget()
class CrTWidget : GlanceAppWidget() {
override val stateDefinition: GlanceStateDefinition<*> = PreferencesGlanceStateDefinition
//override val stateDefinition = TickerInfoStateDefinition
override suspend fun provideGlance(context: Context, id: GlanceId) {
//Log.v("GlanceAppWidget", "provideGlance")
provideContent {
//we're in composable context
val prefs = currentState<Preferences>()
var prefsMap = prefs.asMap()
//val prefs = currentState<TickerInfo>()
// create your AppWidget here
Log.v("GlanceAppWidget", "provideContent")
MyContent(prefsMap)
}
}
@Composable
private fun MyContent(mapTickerz : Map<Preferences.Key<*>, Any>) {
//https://developer.android.com/jetpack/compose/glance/glance-app-widget
val listTickerz = mutableListOf<String>()
mapTickerz.forEach{
listTickerz.add(it.key.toString())
//Log.v("GlanceAppWidget", "KEY FOUND IS IN PREFS MAP IS ${it.key}")
}
Log.v("GlanceAppWidget","PREFS MAP IS $listTickerz")
}
SomeCompositionWithmapTickerz(mapTickerz)
}
& GlanceAppWidgetReceiver()
class CrTWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = CrTWidget()
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
super.onUpdate(context, appWidgetManager, appWidgetIds)
Log.v("CrTWidgetReceiver", "onUpdate GlanceAppWidgetReceiver")
observeData(context)
}
override fun onReceive(context: Context, intent: Intent) {
super.onReceive(context, intent)
Log.v("CrTWidgetReceiver", "onReceive GlanceAppWidgetReceiver + ${intent.action}")
if(intent.action == context.packageName + ".UpdateWIDGET"){
observeData(context)
}
/*if(intent.action == "android.appwidget.action.APPWIDGET_UPDATE"){
observeData(context)
}*/
}
private fun observeData(context: Context) {
try{
Log.v("CrTWidgetReceiver", "observeData STARTED")
val applicationScope = CoroutineScope(SupervisorJob())
// Using by lazy so the database and the repository are only created when they're needed
// rather than when the application starts
val database by lazy { DatabasePT.getDatabase(context, applicationScope) }
val repository by lazy { RepositoryPT(database.CrTickerDAO(), database.CrPairsDAO()) }
var mapTickers: MutableMap<String, CrTickerModel> = mutableMapOf()
//fetch data from repo
var firstFlowReached = false
CoroutineScope(SupervisorJob()).launch {
repository.getAllDataSymbol.takeWhile { !firstFlowReached }.collect {syms ->
//Log.v("CrTWidgetReceiver", "collect OCCURENCE")
syms.forEach {symbol ->
mapTickers[symbol.cryptoTickName] = symbol
}
GlanceAppWidgetManager(context).getGlanceIds(CrTWidget::class.java).forEach { glanceId ->
updateAppWidgetState(context, glanceId) { pref ->
pref.clear()
mapTickers.forEach { symbol ->
Log.v("CrTWidgetReceiver", "LAST PRICE OF ${symbol.value.cryptoTickSymbol} IS ${symbol.value.cryptoTickQuote}")
val tick = symbol.value.cryptoTickSymbol
val tickQuoter = symbol.value.cryptoTickQuoter
val price = symbol.value.cryptoTickQuote
val source = symbol.value.cryptoTickSource
pref[stringPreferencesKey(symbol.value.cryptoTickName)] = "$tick¤$tickQuoter¤$price¤$source"
}
}
CrTWidget().update(context, glanceId)
}
firstFlowReached = true
Log.v("CrTWidgetReceiver", "EXITING COLLECT")
}
}
}catch (e : Exception){
var stringWriter = StringWriter()
e.printStackTrace(PrintWriter(stringWriter))
Log.e("CrTWidgetReceiver", "observeData ERROR $stringWriter")
}
}
}
After reading
I still don't find the way to make it work :( If anyone can give me an hint ;)
EDIT
After trying @summers-pittman proposition and @marcel explainations, I ended up using this :
class CrTWidget : GlanceAppWidget() {
override suspend fun provideGlance(context: Context, id: GlanceId) {
val applicationScope = CoroutineScope(SupervisorJob())
// Using by lazy so the database and the repository are only created when they're needed
// rather than when the application starts
val database by lazy { DatabasePT.getDatabase(context, applicationScope) }
val repository by lazy { RepositoryPT(database.CrTickerDAO(), database.CrPairsDAO()) }
provideContent {
var watchTickerState = repository.getAllDataSymbol.collectAsState(initial = listOf(
CrTickerModel(cryptoTickTimestamp = "0", cryptoTickName = "EMPTY", cryptoTickNameAPI = "EMPTY", cryptoTickQuote = "EMPTY", cryptoTickQuoter = "EMPTY", cryptoTickSource = "BINANCE", cryptoTickSymbol = "EMPTY")
))
//we're in composable context
val prefs = currentState<Preferences>()
var prefsMap = prefs.asMap()
MyContent(watchTickerState)
}
}
@Composable
private fun MyContent(watchTickerState: State<List<CrTickerModel>>) {
//https://developer.android.com/jetpack/compose/glance/glance-app-widget
watchTickerState.value.let { modelList ->
Log.v("recomposed", modelList.toString())
SomeCompositionWithList(modelList)
}
}
}
With this example, I even don't need to call CrTWidget().updateAll(context)
to update the state of the widget as it does automatically with var watchTickerState = repository.getAllDataSymbol.collectAsState()
But the problem is the same, it works perfectly on API>32 but on others it stop updating after the first update...