1

Goal

How can I pass myObject and myClass parameters to GlanceWorker below?

Background

I am using android-workmanager to run background tasks. These tasks are initialized from a home screen widget which I have added with Glance. As recommended by Google here (see “How do I fetch data?”) I use the worker to update the Glance composable which is started from an ActionCallback.

To update my widget, the worker constructor needs to take two extra parameters myObject and myClass (in addition to context and workerParameters). All examples that I have read so far have mentioned the use of a WorkerFactory in order to achieve this. This extra parameter is always provided to the factory at start-up or injected via the dagger graph.

However, in my case, the type of these extra parameters is dependent on which widget initiated the ActionCallback (each widget has its own extension of GlanceStateDefinition() and GlanceAppWidget()). Therefore I can only provide the extra parameters to the worker at runtime (as far as I'm aware) and don't know how to have it pre-provided to the custom WorkerFactory. To make it more complicated, one of my extra parameters uses a generic T.

abstract class Action<T : ShowPortfolioMenuState<T>>(
    val myObject: GlanceStateDefinition<T>,
    val myClass: Class<out WidgetAbstractClass>,
) : ActionCallback {

    override suspend fun onAction(
        context: Context,
        glanceId: GlanceId,
        parameters: ActionParameters,
    ) {
        val glanceIdInt = GlanceAppWidgetManager(context).getAppWidgetId(glanceId)
        val showMenu = parameters[ActionParameters.Key("showMenu")] ?: false

        val glanceWork =
            OneTimeWorkRequest.Builder(GlanceWorkerPortfolioChange::class.java)
                .setInputData(
                    workDataOf("GLANCE_ID" to glanceIdInt, "SHOW_MENU" to showMenu)
                ).build()

        WorkManager.getInstance(context)
            .beginUniqueWork("Worker Name",
                ExistingWorkPolicy.REPLACE,
                glanceWork
            ).enqueue()
    }
}
@HiltWorker
class GlanceWorker<T: ShowMenuState<T>> @AssistedInject constructor(
    @Assisted val context: Context,
    @Assisted workerParameters: WorkerParameters,
    val myObject: GlanceStateDefinition<T>, /*<--- how do i add to constructor*/
    val myClass: Class<out WidgetAbstractClass>, /*<--- how do i add to constructor*/
    ): CoroutineWorker(context, workerParameters) {
    
    override suspend fun doWork(): Result {
        val glanceIdInt = inputData.getInt("GLANCE_ID", -1)
        val glanceId = GlanceAppWidgetManager(context).getGlanceIdBy(glanceIdInt)
        val showMenu = inputData.getBoolean("SHOW_MENU", false)

        myClass.apply {
            updateAppWidgetState(context, myObject, glanceId) { state -> /*state is of type T*/
                state.updateShowMenu(showMenu)
            }
            update(context, glanceId)
        }
        return Result.success()
    }
}
Sidou Gmr
  • 138
  • 8
mars8
  • 770
  • 1
  • 11
  • 25
  • 1
    Perhaps your worker should be pulling this configuration information from a repository, rather than trying to push the values into the worker. – CommonsWare Nov 14 '22 at 22:54
  • @CommonsWare I am not sure what you mean. You mean a repository that holds objects and a bunch of inner classes? If so, how do I initialize the inner classes when I call them from the Worker? Also, I need to tell the Worker what objects/classes it needs to pull from the repository, So I would need to pass in some runtime information into the Worker anyway? – mars8 Nov 15 '22 at 07:57

1 Answers1

1

A couple of things:

  1. Actions can't take parameters. Glance won't be able to instantiate the class and fail. Instead:
    1. Use the ActionParameters to pass the Class name as String and then use Class.forName(..) to get the type/instance.
    2. Use the GlanceAppWidget instance to get the state https://developer.android.com/reference/kotlin/androidx/glance/appwidget/GlanceAppWidget#(androidx.glance.appwidget.GlanceAppWidget).getAppWidgetState(android.content.Context,androidx.glance.GlanceId)
  2. You can do the same for the worker by passing it as workData

Some workers examples:

https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/glance/weather/WeatherWorker.kt

https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/glance/image/ImageWorker.kt

Marcel
  • 2,094
  • 3
  • 23
  • 37