Create individual stream notifications for convenience on Android 7.0 and later.

This commit is contained in:
Isira Seneviratne 2023-05-03 20:41:19 +05:30 committed by TobiGr
parent 8cfba4003d
commit 7742c40ac0
3 changed files with 81 additions and 29 deletions

View File

@ -1,6 +1,8 @@
package org.schabi.newpipe.local.feed.notifications
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
@ -12,6 +14,7 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.preference.PreferenceManager
import com.squareup.picasso.Picasso
import com.squareup.picasso.Target
@ -26,23 +29,22 @@ import org.schabi.newpipe.util.PicassoHelper
* Helper for everything related to show notifications about new streams to the user.
*/
class NotificationHelper(val context: Context) {
private val manager = context.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
private val manager = context.getSystemService<NotificationManager>()!!
private val iconLoadingTargets = ArrayList<Target>()
/**
* Show a notification about new streams from a single channel.
* Opening the notification will open the corresponding channel page.
* Show notifications for new streams from a single channel. The individual notifications are
* expandable on Android 7.0 and later.
*
* Opening the summary notification will open the corresponding channel page. Opening the
* individual notifications will open the corresponding video.
*/
fun displayNewStreamsNotification(data: FeedUpdateInfo) {
val newStreams: List<StreamInfoItem> = data.newStreams
fun displayNewStreamsNotifications(data: FeedUpdateInfo) {
val newStreams = data.newStreams
val summary = context.resources.getQuantityString(
R.plurals.new_streams, newStreams.size, newStreams.size
)
val builder = NotificationCompat.Builder(
val summaryBuilder = NotificationCompat.Builder(
context,
context.getString(R.string.streams_notification_channel_id)
)
@ -50,7 +52,7 @@ class NotificationHelper(val context: Context) {
.setContentText(
data.listInfo.relatedItems.joinToString(
context.getString(R.string.enumeration_comma)
) { x -> x.name }
) { it.name }
)
.setNumber(newStreams.size)
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
@ -60,16 +62,19 @@ class NotificationHelper(val context: Context) {
.setColorized(true)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
.setGroupSummary(true)
.setGroup(data.listInfo.url)
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
// Build style
// Build a summary notification for Android versions < 7.0
val style = NotificationCompat.InboxStyle()
.setSummaryText(summary)
.setBigContentTitle(data.name)
newStreams.forEach { style.addLine(it.name) }
style.setSummaryText(summary)
style.setBigContentTitle(data.name)
builder.setStyle(style)
summaryBuilder.setStyle(style)
// open the channel page when clicking on the notification
builder.setContentIntent(
// open the channel page when clicking on the summary notification
summaryBuilder.setContentIntent(
PendingIntentCompat.getActivity(
context,
data.pseudoId,
@ -84,13 +89,21 @@ class NotificationHelper(val context: Context) {
// a Target is like a listener for image loading events
val target = object : Target {
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
builder.setLargeIcon(bitmap) // set only if there is actually one
manager.notify(data.pseudoId, builder.build())
summaryBuilder.setLargeIcon(bitmap) // set only if there is actually one
// Show individual stream notifications
showStreamNotifications(newStreams, data.listInfo.serviceId)
// Show summary notification
manager.notify(data.pseudoId, summaryBuilder.build())
iconLoadingTargets.remove(this) // allow it to be garbage-collected
}
override fun onBitmapFailed(e: Exception, errorDrawable: Drawable) {
manager.notify(data.pseudoId, builder.build())
// Show individual stream notifications
showStreamNotifications(newStreams, data.listInfo.serviceId)
// Show summary notification
manager.notify(data.pseudoId, summaryBuilder.build())
iconLoadingTargets.remove(this) // allow it to be garbage-collected
}
@ -106,6 +119,41 @@ class NotificationHelper(val context: Context) {
PicassoHelper.loadNotificationIcon(data.avatarUrl).into(target)
}
private fun showStreamNotifications(newStreams: List<StreamInfoItem>, serviceId: Int) {
newStreams.asSequence()
.map { it to createStreamNotification(it, serviceId) }
.forEach { (stream, notification) ->
manager.notify(stream.url.hashCode(), notification)
}
}
private fun createStreamNotification(item: StreamInfoItem, serviceId: Int): Notification {
return NotificationCompat.Builder(
context,
context.getString(R.string.streams_notification_channel_id)
)
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
.setContentTitle(item.name)
.setContentText(item.uploaderName)
.setGroup(item.uploaderUrl)
.setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
.setColorized(true)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
.setContentIntent(
// Open the stream link in the player when clicking on the notification.
PendingIntentCompat.getActivity(
context,
item.url.hashCode(),
NavigationHelper.getStreamIntent(context, serviceId, item.url, item.name),
PendingIntent.FLAG_UPDATE_CURRENT,
false
)
)
.setSilent(true) // Avoid creating noise for individual stream notifications.
.build()
}
companion object {
/**
* Check whether notifications are enabled on the device.
@ -124,9 +172,7 @@ class NotificationHelper(val context: Context) {
fun areNotificationsEnabledOnDevice(context: Context): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = context.getString(R.string.streams_notification_channel_id)
val manager = context.getSystemService(
Context.NOTIFICATION_SERVICE
) as NotificationManager
val manager = context.getSystemService<NotificationManager>()!!
val enabled = manager.areNotificationsEnabled()
val channel = manager.getNotificationChannel(channelId)
val importance = channel?.importance

View File

@ -55,7 +55,7 @@ class NotificationWorker(
.map { feedUpdateInfoList ->
// display notifications for each feedUpdateInfo (i.e. channel)
feedUpdateInfoList.forEach { feedUpdateInfo ->
notificationHelper.displayNewStreamsNotification(feedUpdateInfo)
notificationHelper.displayNewStreamsNotifications(feedUpdateInfo)
}
return@map Result.success()
}

View File

@ -563,11 +563,8 @@ public final class NavigationHelper {
@Nullable final PlayQueue playQueue,
final boolean switchingPlayers) {
final Intent intent = getOpenIntent(context, url, serviceId,
StreamingService.LinkType.STREAM);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Constants.KEY_TITLE, title);
intent.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers);
final Intent intent = getStreamIntent(context, serviceId, url, title)
.putExtra(VideoDetailFragment.KEY_SWITCHING_PLAYERS, switchingPlayers);
if (playQueue != null) {
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
@ -680,6 +677,15 @@ public final class NavigationHelper {
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.CHANNEL);
}
public static Intent getStreamIntent(final Context context,
final int serviceId,
final String url,
@Nullable final String title) {
return getOpenIntent(context, url, serviceId, StreamingService.LinkType.STREAM)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(Constants.KEY_TITLE, title);
}
/**
* Finish this <code>Activity</code> as well as all <code>Activities</code> running below it
* and then start <code>MainActivity</code>.