Vengatesh Murugasamy
Vengatesh Murugasamy

Reputation: 351

How to update Glance App Widget inside a service in android or outside of the GlanceAppWidget

Android 12 introduced new API for creating app widgets called Glance. My use case is, I have a button inside a Glance composable and upon pressing that a foreground service is invoked which fetches data from server and update the widget. In legacy API we can do update by creating new remote views and update the widget via app widget manager like below.

class COVIDSummaryUpdateService : Service() {
            private val TAG = "COVIDSummaryUpdateService"
            private val FOREGROUND_SERVICE_ID = 111

            override fun onBind(intent: Intent?): IBinder? {
                return null
            }

            override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
                startNotificationForForeground()
                val views = RemoteViews(
                    [email protected],
                    R.layout.covid_summary_layout
                )
                views.setViewVisibility(R.id.ivRefresh, View.GONE)
                views.setViewVisibility(R.id.progressBar, View.VISIBLE)
                val componentName = ComponentName(
                    [email protected],
                    COVIDSummaryAppWidgetProvider::class.java
                )
                val manager =
                    AppWidgetManager.getInstance([email protected])
                manager.updateAppWidget(componentName, views)

                COVIDApiService.getCOVIDApi().getSummary()
                    .enqueue(object : Callback<SummaryResponse> {
                        override fun onResponse(
                            call: Call<SummaryResponse>,
                            response: Response<SummaryResponse>
                        ) {
                            if (response.isSuccessful && response.code() == 200) {
                                updateWidgetWithResponse(response.body())
                            } else {
                                hideProgressView()
                            }
                            stopService()
                        }

                        override fun onFailure(call: Call<SummaryResponse>, t: Throwable) {
                            t.message?.let { Log.d(TAG, it) }
                            hideProgressView()
                            stopService()
                        }
                    })
                return START_STICKY
            }

            private fun stopService() {
                stopForeground(true)
                stopSelf()
            }

            private fun updateWidgetWithResponse(summaryResponse: SummaryResponse?) {
                try {
                    summaryResponse?.let {
                        val views = RemoteViews(
                            [email protected],
                            R.layout.covid_summary_layout
                        )
                        views.setTextViewText(
                            R.id.tvNewConfirmedCases,
                            "${summaryResponse.global.newConfirmed}"
                        )
                        views.setTextViewText(
                            R.id.tvNewDeaths,
                            "${summaryResponse.global.newDeaths}"
                        )
                        views.setTextViewText(
                            R.id.tvNewRecovered,
                            "${summaryResponse.global.newRecovered}"
                        )
                        views.setTextViewText(
                            R.id.tvTotalConfirmedCases,
                            "${summaryResponse.global.totalConfirmed}"
                        )
                        views.setTextViewText(
                            R.id.tvTotalDeaths,
                            "${summaryResponse.global.totalDeaths}"
                        )
                        views.setTextViewText(
                            R.id.tvTotalRecovered,
                            "${summaryResponse.global.totalRecovered}"
                        )
                        views.setViewVisibility(R.id.ivRefresh, View.VISIBLE)
                        views.setViewVisibility(R.id.progressBar, View.GONE)
                        val componentName = ComponentName(
                            [email protected],
                            COVIDSummaryAppWidgetProvider::class.java
                        )
                        val manager =
                            AppWidgetManager.getInstance([email protected])
                        manager.updateAppWidget(componentName, views)
                    }
                } catch (e: Exception) {
                    e.message?.let { Log.d(TAG, it) }
                    hideProgressView()
                    stopService()
                }
            }

            private fun hideProgressView() {
                val views = RemoteViews(
                    [email protected],
                    R.layout.covid_summary_layout
                )
                views.setViewVisibility(R.id.ivRefresh, View.VISIBLE)
                views.setViewVisibility(R.id.progressBar, View.GONE)
                val componentName = ComponentName(
                    [email protected],
                    COVIDSummaryAppWidgetProvider::class.java
                )
                val manager =
                    AppWidgetManager.getInstance([email protected])
                manager.updateAppWidget(componentName, views)
            }

            private fun startNotificationForForeground() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    startForeground(
                        FOREGROUND_SERVICE_ID,
                        createNotification(
                            "COVID19Service", "COVID19 Summary Channel",
                            null,
                            getString(R.string.foreground_not_text)
                        )
                    )
                }
            }

            @RequiresApi(Build.VERSION_CODES.O)
            private fun createNotification(
                channelId: String,
                channelName: String,
                contentTitle: String?,
                contentText: String,
                pendingIntent: PendingIntent? = null
            ): Notification {
                val notificationChannel =
                    NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE)
                notificationChannel.description = channelId
                notificationChannel.setSound(null, null)
                notificationChannel.lightColor = Color.BLUE
                notificationChannel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE

                val notificationManager =
                    getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
                notificationManager.createNotificationChannel(notificationChannel)

                return Notification.Builder(this, channelId).let { builder ->
                    contentTitle?.let {
                        builder.setContentTitle(contentTitle)
                    }
                    builder.setContentText(contentText)
                    builder.setSmallIcon(R.drawable.ic_covid_19_33)
                    pendingIntent?.let { builder.setContentIntent(it) }
                    builder.build()
                }
            }
        }
    }

Upvotes: 4

Views: 2670

Answers (2)

Enes Zor
Enes Zor

Reputation: 1190

You can use GlanceAppWidgetReceiver to fetch data from your source. To send data to your widget:

val glanceId = GlanceAppWidgetManager(context).
getGlanceIds(YourAppWidget::class.java).firstOrNull()

YourAppWidget.update(context,glanceId)

Please check this article for all source code : https://enofeb.medium.com/android-jetpack-glance-for-app-widgets-bd7a704624ba

Upvotes: 2

Piotr Prus
Piotr Prus

Reputation: 355

To update the widget, you can pass the variable in your GlanceWidget constructor:

class TestGlanceWidget(val state: WidgetState) : GlanceAppWidget() {

then you can call one of the update functions on the GlanceWidget:

val state = WidgetState()
TestGlanceWidget(state = state).updateAll(context)

Upvotes: 0

Related Questions