mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-07-16 00:42:57 +00:00

Reduced memory footprint of FeedUpdateInfo objects. Those objects might stay around for a while and accumulate (up to BUFFER_COUNT_BEFORE_INSERT = 20 at the moment), so in order not to fill up the memory it's better to keep as little data as possible. Previously ChannelInfo data was stored, causing ReadyChannelTabLinkHandler objects to be also stored uselessly (and those channel tabs contain prefetched JSON data which used ~700KB of memory).
219 lines
9.1 KiB
Kotlin
219 lines
9.1 KiB
Kotlin
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
|
|
import android.graphics.drawable.Drawable
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.provider.Settings
|
|
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
|
|
import org.schabi.newpipe.R
|
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem
|
|
import org.schabi.newpipe.local.feed.service.FeedUpdateInfo
|
|
import org.schabi.newpipe.util.NavigationHelper
|
|
import org.schabi.newpipe.util.image.PicassoHelper
|
|
|
|
/**
|
|
* Helper for everything related to show notifications about new streams to the user.
|
|
*/
|
|
class NotificationHelper(val context: Context) {
|
|
private val manager = NotificationManagerCompat.from(context)
|
|
private val iconLoadingTargets = ArrayList<Target>()
|
|
|
|
/**
|
|
* 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 displayNewStreamsNotifications(data: FeedUpdateInfo) {
|
|
val newStreams = data.newStreams
|
|
val summary = context.resources.getQuantityString(
|
|
R.plurals.new_streams, newStreams.size, newStreams.size
|
|
)
|
|
val summaryBuilder = NotificationCompat.Builder(
|
|
context,
|
|
context.getString(R.string.streams_notification_channel_id)
|
|
)
|
|
.setContentTitle(data.name)
|
|
.setContentText(summary)
|
|
.setNumber(newStreams.size)
|
|
.setBadgeIconType(NotificationCompat.BADGE_ICON_LARGE)
|
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
|
.setColor(ContextCompat.getColor(context, R.color.ic_launcher_background))
|
|
.setColorized(true)
|
|
.setAutoCancel(true)
|
|
.setCategory(NotificationCompat.CATEGORY_SOCIAL)
|
|
.setGroupSummary(true)
|
|
.setGroup(data.url)
|
|
.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_SUMMARY)
|
|
|
|
// Build a summary notification for Android versions < 7.0
|
|
val style = NotificationCompat.InboxStyle()
|
|
.setBigContentTitle(data.name)
|
|
newStreams.forEach { style.addLine(it.name) }
|
|
summaryBuilder.setStyle(style)
|
|
|
|
// open the channel page when clicking on the summary notification
|
|
summaryBuilder.setContentIntent(
|
|
PendingIntentCompat.getActivity(
|
|
context,
|
|
data.pseudoId,
|
|
NavigationHelper
|
|
.getChannelIntent(context, data.serviceId, data.url)
|
|
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
|
|
0,
|
|
false
|
|
)
|
|
)
|
|
|
|
// a Target is like a listener for image loading events
|
|
val target = object : Target {
|
|
override fun onBitmapLoaded(bitmap: Bitmap, from: Picasso.LoadedFrom) {
|
|
// set channel icon only if there is actually one (for Android versions < 7.0)
|
|
summaryBuilder.setLargeIcon(bitmap)
|
|
|
|
// Show individual stream notifications, set channel icon only if there is actually
|
|
// one
|
|
showStreamNotifications(newStreams, data.serviceId, bitmap)
|
|
// 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) {
|
|
// Show individual stream notifications
|
|
showStreamNotifications(newStreams, data.serviceId, null)
|
|
// Show summary notification
|
|
manager.notify(data.pseudoId, summaryBuilder.build())
|
|
iconLoadingTargets.remove(this) // allow it to be garbage-collected
|
|
}
|
|
|
|
override fun onPrepareLoad(placeHolderDrawable: Drawable) {
|
|
// Nothing to do
|
|
}
|
|
}
|
|
|
|
// add the target to the list to hold a strong reference and prevent it from being garbage
|
|
// collected, since Picasso only holds weak references to targets
|
|
iconLoadingTargets.add(target)
|
|
|
|
PicassoHelper.loadNotificationIcon(data.avatarUrl).into(target)
|
|
}
|
|
|
|
private fun showStreamNotifications(
|
|
newStreams: List<StreamInfoItem>,
|
|
serviceId: Int,
|
|
channelIcon: Bitmap?
|
|
) {
|
|
for (stream in newStreams) {
|
|
val notification = createStreamNotification(stream, serviceId, channelIcon)
|
|
manager.notify(stream.url.hashCode(), notification)
|
|
}
|
|
}
|
|
|
|
private fun createStreamNotification(
|
|
item: StreamInfoItem,
|
|
serviceId: Int,
|
|
channelIcon: Bitmap?
|
|
): Notification {
|
|
return NotificationCompat.Builder(
|
|
context,
|
|
context.getString(R.string.streams_notification_channel_id)
|
|
)
|
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
|
.setLargeIcon(channelIcon)
|
|
.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.
|
|
* Users can disable them via the system settings for a single app.
|
|
* If this is the case, the app cannot create any notifications
|
|
* and display them to the user.
|
|
* <br>
|
|
* On Android 26 and above, notification channels are used by NewPipe.
|
|
* These can be configured by the user, too.
|
|
* The notification channel for new streams is also checked by this method.
|
|
*
|
|
* @param context Context
|
|
* @return <code>true</code> if notifications are allowed and can be displayed;
|
|
* <code>false</code> otherwise
|
|
*/
|
|
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<NotificationManager>()!!
|
|
val enabled = manager.areNotificationsEnabled()
|
|
val channel = manager.getNotificationChannel(channelId)
|
|
val importance = channel?.importance
|
|
enabled && channel != null && importance != NotificationManager.IMPORTANCE_NONE
|
|
} else {
|
|
NotificationManagerCompat.from(context).areNotificationsEnabled()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Whether the user enabled the notifications for new streams in the app settings.
|
|
*/
|
|
@JvmStatic
|
|
fun areNewStreamsNotificationsEnabled(context: Context): Boolean {
|
|
return (
|
|
PreferenceManager.getDefaultSharedPreferences(context)
|
|
.getBoolean(context.getString(R.string.enable_streams_notifications), false) &&
|
|
areNotificationsEnabledOnDevice(context)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Open the system's notification settings for NewPipe on Android Oreo (API 26) and later.
|
|
* Open the system's app settings for NewPipe on previous Android versions.
|
|
*/
|
|
fun openNewPipeSystemNotificationSettings(context: Context) {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
|
|
.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
context.startActivity(intent)
|
|
} else {
|
|
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
|
intent.data = Uri.parse("package:" + context.packageName)
|
|
context.startActivity(intent)
|
|
}
|
|
}
|
|
}
|
|
}
|