Skip to content

Commit 7742c40

Browse files
Isira-SeneviratneTobiGr
authored andcommitted
Create individual stream notifications for convenience on Android 7.0 and later.
1 parent 8cfba40 commit 7742c40

3 files changed

Lines changed: 81 additions & 29 deletions

File tree

app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationHelper.kt

Lines changed: 69 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package org.schabi.newpipe.local.feed.notifications
22

3+
import android.app.Notification
34
import android.app.NotificationManager
5+
import android.app.PendingIntent
46
import android.content.Context
57
import android.content.Intent
68
import android.graphics.Bitmap
@@ -12,6 +14,7 @@ import androidx.core.app.NotificationCompat
1214
import androidx.core.app.NotificationManagerCompat
1315
import androidx.core.app.PendingIntentCompat
1416
import androidx.core.content.ContextCompat
17+
import androidx.core.content.getSystemService
1518
import androidx.preference.PreferenceManager
1619
import com.squareup.picasso.Picasso
1720
import com.squareup.picasso.Target
@@ -26,31 +29,30 @@ import org.schabi.newpipe.util.PicassoHelper
2629
* Helper for everything related to show notifications about new streams to the user.
2730
*/
2831
class NotificationHelper(val context: Context) {
29-
30-
private val manager = context.getSystemService(
31-
Context.NOTIFICATION_SERVICE
32-
) as NotificationManager
33-
32+
private val manager = context.getSystemService<NotificationManager>()!!
3433
private val iconLoadingTargets = ArrayList<Target>()
3534

3635
/**
37-
* Show a notification about new streams from a single channel.
38-
* Opening the notification will open the corresponding channel page.
36+
* Show notifications for new streams from a single channel. The individual notifications are
37+
* expandable on Android 7.0 and later.
38+
*
39+
* Opening the summary notification will open the corresponding channel page. Opening the
40+
* individual notifications will open the corresponding video.
3941
*/
40-
fun displayNewStreamsNotification(data: FeedUpdateInfo) {
41-
val newStreams: List<StreamInfoItem> = data.newStreams
42+
fun displayNewStreamsNotifications(data: FeedUpdateInfo) {
43+
val newStreams = data.newStreams
4244
val summary = context.resources.getQuantityString(
4345
R.plurals.new_streams, newStreams.size, newStreams.size
4446
)
45-
val builder = NotificationCompat.Builder(
47+
val summaryBuilder = NotificationCompat.Builder(
4648
context,
4749
context.getString(R.string.streams_notification_channel_id)
4850
)
4951
.setContentTitle(Localization.concatenateStrings(data.name, summary))
5052
.setContentText(
5153
data.listInfo.relatedItems.joinToString(
5254
context.getString(R.string.enumeration_comma)
53-
) { x -> x.name }
55+
) { it.name }
5456
)
5557
.setNumber(newStreams.size)
5658
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
@@ -60,16 +62,19 @@ class NotificationHelper(val context: Context) {
6062
.setColorized(true)
6163
.setAutoCancel(true)
6264
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
65+
.setGroupSummary(true)
66+
.setGroup(data.listInfo.url)
67+
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
6368

64-
// Build style
69+
// Build a summary notification for Android versions < 7.0
6570
val style = NotificationCompat.InboxStyle()
71+
.setSummaryText(summary)
72+
.setBigContentTitle(data.name)
6673
newStreams.forEach { style.addLine(it.name) }
67-
style.setSummaryText(summary)
68-
style.setBigContentTitle(data.name)
69-
builder.setStyle(style)
74+
summaryBuilder.setStyle(style)
7075

71-
// open the channel page when clicking on the notification
72-
builder.setContentIntent(
76+
// open the channel page when clicking on the summary notification
77+
summaryBuilder.setContentIntent(
7378
PendingIntentCompat.getActivity(
7479
context,
7580
data.pseudoId,
@@ -84,13 +89,21 @@ class NotificationHelper(val context: Context) {
8489
// a Target is like a listener for image loading events
8590
val target = object : Target {
8691
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())
92+
summaryBuilder.setLargeIcon(bitmap) // set only if there is actually one
93+
94+
// Show individual stream notifications
95+
showStreamNotifications(newStreams, data.listInfo.serviceId)
96+
// Show summary notification
97+
manager.notify(data.pseudoId, summaryBuilder.build())
98+
8999
iconLoadingTargets.remove(this) // allow it to be garbage-collected
90100
}
91101

92102
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
93-
manager.notify(data.pseudoId, builder.build())
103+
// Show individual stream notifications
104+
showStreamNotifications(newStreams, data.listInfo.serviceId)
105+
// Show summary notification
106+
manager.notify(data.pseudoId, summaryBuilder.build())
94107
iconLoadingTargets.remove(this) // allow it to be garbage-collected
95108
}
96109

@@ -106,6 +119,41 @@ class NotificationHelper(val context: Context) {
106119
PicassoHelper.loadNotificationIcon(data.avatarUrl).into(target)
107120
}
108121

122+
private fun showStreamNotifications(newStreams: List<StreamInfoItem>, serviceId: Int) {
123+
newStreams.asSequence()
124+
.map { it to createStreamNotification(it, serviceId) }
125+
.forEach { (stream, notification) ->
126+
manager.notify(stream.url.hashCode(), notification)
127+
}
128+
}
129+
130+
private fun createStreamNotification(item: StreamInfoItem, serviceId: Int): Notification {
131+
return NotificationCompat.Builder(
132+
context,
133+
context.getString(R.string.streams_notification_channel_id)
134+
)
135+
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
136+
.setContentTitle(item.name)
137+
.setContentText(item.uploaderName)
138+
.setGroup(item.uploaderUrl)
139+
.setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
140+
.setColorized(true)
141+
.setAutoCancel(true)
142+
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
143+
.setContentIntent(
144+
// Open the stream link in the player when clicking on the notification.
145+
PendingIntentCompat.getActivity(
146+
context,
147+
item.url.hashCode(),
148+
NavigationHelper.getStreamIntent(context, serviceId, item.url, item.name),
149+
PendingIntent.FLAG_UPDATE_CURRENT,
150+
false
151+
)
152+
)
153+
.setSilent(true) // Avoid creating noise for individual stream notifications.
154+
.build()
155+
}
156+
109157
companion object {
110158
/**
111159
* Check whether notifications are enabled on the device.
@@ -124,9 +172,7 @@ class NotificationHelper(val context: Context) {
124172
fun areNotificationsEnabledOnDevice(context: Context): Boolean {
125173
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
126174
val channelId = context.getString(R.string.streams_notification_channel_id)
127-
val manager = context.getSystemService(
128-
Context.NOTIFICATION_SERVICE
129-
) as NotificationManager
175+
val manager = context.getSystemService<NotificationManager>()!!
130176
val enabled = manager.areNotificationsEnabled()
131177
val channel = manager.getNotificationChannel(channelId)
132178
val importance = channel?.importance

app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ class NotificationWorker(
5555
.map { feedUpdateInfoList ->
5656
// display notifications for each feedUpdateInfo (i.e. channel)
5757
feedUpdateInfoList.forEach { feedUpdateInfo ->
58-
notificationHelper.displayNewStreamsNotification(feedUpdateInfo)
58+
notificationHelper.displayNewStreamsNotifications(feedUpdateInfo)
5959
}
6060
return@map Result.success()
6161
}

app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -563,11 +563,8 @@ public static void openVideoDetail(final Context context,
563563
@Nullable final PlayQueue playQueue,
564564
final boolean switchingPlayers) {
565565

566-
final Intent intent = getOpenIntent(context, url, serviceId,
567-
StreamingService.LinkType.STREAM);
568-
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
569-
intent.putExtra(Constants.KEY_TITLE, title);
570-
intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers);
566+
final Intent intent = getStreamIntent(context, serviceId, url, title)
567+
.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers);
571568

572569
if (playQueue != null) {
573570
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
@@ -680,6 +677,15 @@ public static Intent getChannelIntent(final Context context,
680677
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL);
681678
}
682679

680+
public static Intent getStreamIntent(final Context context,
681+
final int serviceId,
682+
final String url,
683+
@Nullable final String title) {
684+
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM)
685+
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
686+
.putExtra(Constants.KEY_TITLE, title);
687+
}
688+
683689
/**
684690
* Finish this <code>Activity</code> as well as all <code>Activities</code> running below it
685691
* and then start <code>MainActivity</code>.

0 commit comments

Comments
 (0)