Notifications from WorkManager

A worker has an application Context, which is all a notification needs. CatalogSyncWorker (CoroutineWorker) remembers the cached prices before it refreshes and compares them afterwards:

sync/CatalogSyncWorker.kt: announcing price drops after a syncKotlin
  private suspend fun cachedBooks(): List<Book> = repo.observeBooks().firstOrNull().orEmpty()
  /** 5.30.3 and 5.30.5 announce price drops, redraw the widget, return the book count. */
  private suspend fun afterSync(before: List<Book>): Int {
    val oldPrice = before.associate { it.id to it.price }
    val books = cachedBooks()
    books.filter { it.price < (oldPrice[it.id] ?: it.price) }.forEach {
      BookNestNotifications.showPriceDrop(applicationContext, it, oldPrice.getValue(it.id))
    }
    BookNestWidget().updateAll(applicationContext)  // every placed BookNest widget
    return books.size
  }

To test it, a "sync now" (Constraints and Chaining) cached the test server's $18.90 for Salt and Saffron, the server's price was lowered to $15.50 (echo 15.50 > server/price), and a second "sync now" logged:

Output of 171
D/BookNestWork( 6396): sync attempt 1, id=a7a24c40
D/BookNestNet( 6396): refreshed 6 books from the network
I/BookNestNotify( 6396): posted id=3 on channel deals
D/BookNestWork( 6396): report: 6 books in the local catalog
The price-drop notification in the shade, and the home screen widget of Section 5.30.4 after the same sync
The price-drop notification in the shade, and the home screen widget of Glance Widgets after the same sync

Long workers, such as a large upload, call setForeground(ForegroundInfo(id, notification)) to run as a foreground service with an ongoing notification (Foreground Services); expedited work needs the same through getForegroundInfo() on Android 11 and earlier.