11package org.schabi.newpipe.local.feed.notifications
22
3+ import android.app.Notification
34import android.app.NotificationManager
5+ import android.app.PendingIntent
46import android.content.Context
57import android.content.Intent
68import android.graphics.Bitmap
@@ -12,46 +14,41 @@ import androidx.core.app.NotificationCompat
1214import androidx.core.app.NotificationManagerCompat
1315import androidx.core.app.PendingIntentCompat
1416import androidx.core.content.ContextCompat
17+ import androidx.core.content.getSystemService
1518import androidx.preference.PreferenceManager
1619import com.squareup.picasso.Picasso
1720import com.squareup.picasso.Target
1821import org.schabi.newpipe.R
1922import org.schabi.newpipe.extractor.stream.StreamInfoItem
2023import org.schabi.newpipe.local.feed.service.FeedUpdateInfo
21- import org.schabi.newpipe.util.Localization
2224import org.schabi.newpipe.util.NavigationHelper
2325import org.schabi.newpipe.util.PicassoHelper
2426
2527/* *
2628 * Helper for everything related to show notifications about new streams to the user.
2729 */
2830class NotificationHelper (val context : Context ) {
29-
30- private val manager = context.getSystemService(
31- Context .NOTIFICATION_SERVICE
32- ) as NotificationManager
33-
31+ private val manager = NotificationManagerCompat .from(context)
3432 private val iconLoadingTargets = ArrayList <Target >()
3533
3634 /* *
37- * Show a notification about new streams from a single channel.
38- * Opening the notification will open the corresponding channel page.
35+ * Show notifications for new streams from a single channel. The individual notifications are
36+ * expandable on Android 7.0 and later.
37+ *
38+ * Opening the summary notification will open the corresponding channel page. Opening the
39+ * individual notifications will open the corresponding video.
3940 */
40- fun displayNewStreamsNotification (data : FeedUpdateInfo ) {
41- val newStreams: List < StreamInfoItem > = data.newStreams
41+ fun displayNewStreamsNotifications (data : FeedUpdateInfo ) {
42+ val newStreams = data.newStreams
4243 val summary = context.resources.getQuantityString(
4344 R .plurals.new_streams, newStreams.size, newStreams.size
4445 )
45- val builder = NotificationCompat .Builder (
46+ val summaryBuilder = NotificationCompat .Builder (
4647 context,
4748 context.getString(R .string.streams_notification_channel_id)
4849 )
49- .setContentTitle(Localization .concatenateStrings(data.name, summary))
50- .setContentText(
51- data.listInfo.relatedItems.joinToString(
52- context.getString(R .string.enumeration_comma)
53- ) { x -> x.name }
54- )
50+ .setContentTitle(data.name)
51+ .setContentText(summary)
5552 .setNumber(newStreams.size)
5653 .setBadgeIconType(NotificationCompat .BADGE_ICON_LARGE )
5754 .setPriority(NotificationCompat .PRIORITY_DEFAULT )
@@ -60,16 +57,18 @@ class NotificationHelper(val context: Context) {
6057 .setColorized(true )
6158 .setAutoCancel(true )
6259 .setCategory(NotificationCompat .CATEGORY_SOCIAL )
60+ .setGroupSummary(true )
61+ .setGroup(data.listInfo.url)
62+ .setGroupAlertBehavior(NotificationCompat .GROUP_ALERT_SUMMARY )
6363
64- // Build style
64+ // Build a summary notification for Android versions < 7.0
6565 val style = NotificationCompat .InboxStyle ()
66+ .setBigContentTitle(data.name)
6667 newStreams.forEach { style.addLine(it.name) }
67- style.setSummaryText(summary)
68- style.setBigContentTitle(data.name)
69- builder.setStyle(style)
68+ summaryBuilder.setStyle(style)
7069
71- // open the channel page when clicking on the notification
72- builder .setContentIntent(
70+ // open the channel page when clicking on the summary notification
71+ summaryBuilder .setContentIntent(
7372 PendingIntentCompat .getActivity(
7473 context,
7574 data.pseudoId,
@@ -84,13 +83,23 @@ class NotificationHelper(val context: Context) {
8483 // a Target is like a listener for image loading events
8584 val target = object : Target {
8685 override fun onBitmapLoaded (bitmap : Bitmap , from : Picasso .LoadedFrom ) {
87- builder.setLargeIcon(bitmap) // set only if there is actually one
88- manager.notify(data.pseudoId, builder.build())
86+ // set channel icon only if there is actually one (for Android versions < 7.0)
87+ summaryBuilder.setLargeIcon(bitmap)
88+
89+ // Show individual stream notifications, set channel icon only if there is actually
90+ // one
91+ showStreamNotifications(newStreams, data.listInfo.serviceId, bitmap)
92+ // Show summary notification
93+ manager.notify(data.pseudoId, summaryBuilder.build())
94+
8995 iconLoadingTargets.remove(this ) // allow it to be garbage-collected
9096 }
9197
9298 override fun onBitmapFailed (e : Exception , errorDrawable : Drawable ) {
93- manager.notify(data.pseudoId, builder.build())
99+ // Show individual stream notifications
100+ showStreamNotifications(newStreams, data.listInfo.serviceId, null )
101+ // Show summary notification
102+ manager.notify(data.pseudoId, summaryBuilder.build())
94103 iconLoadingTargets.remove(this ) // allow it to be garbage-collected
95104 }
96105
@@ -106,6 +115,49 @@ class NotificationHelper(val context: Context) {
106115 PicassoHelper .loadNotificationIcon(data.avatarUrl).into(target)
107116 }
108117
118+ private fun showStreamNotifications (
119+ newStreams : List <StreamInfoItem >,
120+ serviceId : Int ,
121+ channelIcon : Bitmap ?
122+ ) {
123+ for (stream in newStreams) {
124+ val notification = createStreamNotification(stream, serviceId, channelIcon)
125+ manager.notify(stream.url.hashCode(), notification)
126+ }
127+ }
128+
129+ private fun createStreamNotification (
130+ item : StreamInfoItem ,
131+ serviceId : Int ,
132+ channelIcon : Bitmap ?
133+ ): Notification {
134+ return NotificationCompat .Builder (
135+ context,
136+ context.getString(R .string.streams_notification_channel_id)
137+ )
138+ .setSmallIcon(R .drawable.ic_newpipe_triangle_white)
139+ .setLargeIcon(channelIcon)
140+ .setContentTitle(item.name)
141+ .setContentText(item.uploaderName)
142+ .setGroup(item.uploaderUrl)
143+ .setColor(ContextCompat .getColor(context, R .color.ic_launcher_background))
144+ .setColorized(true )
145+ .setAutoCancel(true )
146+ .setCategory(NotificationCompat .CATEGORY_SOCIAL )
147+ .setContentIntent(
148+ // Open the stream link in the player when clicking on the notification.
149+ PendingIntentCompat .getActivity(
150+ context,
151+ item.url.hashCode(),
152+ NavigationHelper .getStreamIntent(context, serviceId, item.url, item.name),
153+ PendingIntent .FLAG_UPDATE_CURRENT ,
154+ false
155+ )
156+ )
157+ .setSilent(true ) // Avoid creating noise for individual stream notifications.
158+ .build()
159+ }
160+
109161 companion object {
110162 /* *
111163 * Check whether notifications are enabled on the device.
@@ -124,9 +176,7 @@ class NotificationHelper(val context: Context) {
124176 fun areNotificationsEnabledOnDevice (context : Context ): Boolean {
125177 return if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
126178 val channelId = context.getString(R .string.streams_notification_channel_id)
127- val manager = context.getSystemService(
128- Context .NOTIFICATION_SERVICE
129- ) as NotificationManager
179+ val manager = context.getSystemService<NotificationManager >()!!
130180 val enabled = manager.areNotificationsEnabled()
131181 val channel = manager.getNotificationChannel(channelId)
132182 val importance = channel?.importance
0 commit comments