mirror of https://github.com/TeamNewPipe/NewPipe
171 lines
6.4 KiB
Kotlin
171 lines
6.4 KiB
Kotlin
package org.schabi.newpipe.local.feed.notifications
|
|
|
|
import android.content.Context
|
|
import android.util.Log
|
|
import androidx.core.app.NotificationCompat
|
|
import androidx.work.Constraints
|
|
import androidx.work.ExistingPeriodicWorkPolicy
|
|
import androidx.work.ForegroundInfo
|
|
import androidx.work.NetworkType
|
|
import androidx.work.OneTimeWorkRequestBuilder
|
|
import androidx.work.PeriodicWorkRequest
|
|
import androidx.work.WorkManager
|
|
import androidx.work.WorkerParameters
|
|
import androidx.work.rxjava3.RxWorker
|
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
|
import io.reactivex.rxjava3.core.Single
|
|
import org.schabi.newpipe.App
|
|
import org.schabi.newpipe.R
|
|
import org.schabi.newpipe.error.ErrorInfo
|
|
import org.schabi.newpipe.error.ErrorUtil
|
|
import org.schabi.newpipe.error.UserAction
|
|
import org.schabi.newpipe.local.feed.service.FeedLoadManager
|
|
import org.schabi.newpipe.local.feed.service.FeedLoadService
|
|
import java.util.concurrent.TimeUnit
|
|
|
|
/*
|
|
* Worker which checks for new streams of subscribed channels
|
|
* in intervals which can be set by the user in the settings.
|
|
*/
|
|
class NotificationWorker(
|
|
appContext: Context,
|
|
workerParams: WorkerParameters,
|
|
) : RxWorker(appContext, workerParams) {
|
|
|
|
private val notificationHelper by lazy {
|
|
NotificationHelper(appContext)
|
|
}
|
|
private val feedLoadManager = FeedLoadManager(appContext)
|
|
|
|
override fun createWork(): Single<Result> = if (areNotificationsEnabled(applicationContext)) {
|
|
feedLoadManager.startLoading(
|
|
ignoreOutdatedThreshold = true,
|
|
groupId = FeedLoadManager.GROUP_NOTIFICATION_ENABLED
|
|
)
|
|
.doOnSubscribe { showLoadingFeedForegroundNotification() }
|
|
.map { feed ->
|
|
// filter out feedUpdateInfo items (i.e. channels) with nothing new
|
|
feed.mapNotNull {
|
|
it.value?.takeIf { feedUpdateInfo ->
|
|
feedUpdateInfo.newStreams.isNotEmpty()
|
|
}
|
|
}
|
|
}
|
|
.observeOn(AndroidSchedulers.mainThread()) // Picasso requires calls from main thread
|
|
.map { feedUpdateInfoList ->
|
|
// display notifications for each feedUpdateInfo (i.e. channel)
|
|
feedUpdateInfoList.forEach { feedUpdateInfo ->
|
|
notificationHelper.displayNewStreamsNotifications(feedUpdateInfo)
|
|
}
|
|
return@map Result.success()
|
|
}
|
|
.doOnError { throwable ->
|
|
Log.e(TAG, "Error while displaying streams notifications", throwable)
|
|
ErrorUtil.createNotification(
|
|
applicationContext,
|
|
ErrorInfo(throwable, UserAction.NEW_STREAMS_NOTIFICATIONS, "main worker")
|
|
)
|
|
}
|
|
.onErrorReturnItem(Result.failure())
|
|
} else {
|
|
// the user can disable streams notifications in the device's app settings
|
|
Single.just(Result.success())
|
|
}
|
|
|
|
private fun showLoadingFeedForegroundNotification() {
|
|
val notification = NotificationCompat.Builder(
|
|
applicationContext,
|
|
applicationContext.getString(R.string.notification_channel_id)
|
|
).setOngoing(true)
|
|
.setProgress(-1, -1, true)
|
|
.setSmallIcon(R.drawable.ic_newpipe_triangle_white)
|
|
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
|
.setContentTitle(applicationContext.getString(R.string.feed_notification_loading))
|
|
.build()
|
|
setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification))
|
|
}
|
|
|
|
companion object {
|
|
|
|
private val TAG = NotificationWorker::class.java.simpleName
|
|
private const val WORK_TAG = App.PACKAGE_NAME + "_streams_notifications"
|
|
|
|
private fun areNotificationsEnabled(context: Context) =
|
|
NotificationHelper.areNewStreamsNotificationsEnabled(context) &&
|
|
NotificationHelper.areNotificationsEnabledOnDevice(context)
|
|
|
|
/**
|
|
* Schedules a task for the [NotificationWorker]
|
|
* if the (device and in-app) notifications are enabled,
|
|
* otherwise [cancel]s all scheduled tasks.
|
|
*/
|
|
@JvmStatic
|
|
fun initialize(context: Context) {
|
|
if (areNotificationsEnabled(context)) {
|
|
schedule(context)
|
|
} else {
|
|
cancel(context)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param context the context to use
|
|
* @param options configuration options for the scheduler
|
|
* @param force Force the scheduler to use the new options
|
|
* by replacing the previously used worker.
|
|
*/
|
|
fun schedule(context: Context, options: ScheduleOptions, force: Boolean = false) {
|
|
val constraints = Constraints.Builder()
|
|
.setRequiredNetworkType(
|
|
if (options.isRequireNonMeteredNetwork) {
|
|
NetworkType.UNMETERED
|
|
} else {
|
|
NetworkType.CONNECTED
|
|
}
|
|
).build()
|
|
|
|
val request = PeriodicWorkRequest.Builder(
|
|
NotificationWorker::class.java,
|
|
options.interval,
|
|
TimeUnit.MILLISECONDS
|
|
).setConstraints(constraints)
|
|
.addTag(WORK_TAG)
|
|
.build()
|
|
|
|
WorkManager.getInstance(context)
|
|
.enqueueUniquePeriodicWork(
|
|
WORK_TAG,
|
|
if (force) {
|
|
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE
|
|
} else {
|
|
ExistingPeriodicWorkPolicy.KEEP
|
|
},
|
|
request
|
|
)
|
|
}
|
|
|
|
@JvmStatic
|
|
fun schedule(context: Context) = schedule(context, ScheduleOptions.from(context))
|
|
|
|
/**
|
|
* Check for new streams immediately
|
|
*/
|
|
@JvmStatic
|
|
fun runNow(context: Context) {
|
|
val request = OneTimeWorkRequestBuilder<NotificationWorker>()
|
|
.addTag(WORK_TAG)
|
|
.build()
|
|
WorkManager.getInstance(context).enqueue(request)
|
|
}
|
|
|
|
/**
|
|
* Cancels all current work related to the [NotificationWorker].
|
|
*/
|
|
@JvmStatic
|
|
fun cancel(context: Context) {
|
|
WorkManager.getInstance(context).cancelAllWorkByTag(WORK_TAG)
|
|
}
|
|
}
|
|
}
|