package com.github.libretube.util import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequest import androidx.work.WorkManager import com.github.libretube.constants.NOTIFICATION_WORK_NAME import com.github.libretube.constants.PUSH_CHANNEL_ID import com.github.libretube.R import com.github.libretube.activities.MainActivity import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.SubscriptionHelper import com.github.libretube.constants.PreferenceKeys import com.github.libretube.extensions.toID import kotlinx.coroutines.async import kotlinx.coroutines.runBlocking import java.util.concurrent.TimeUnit class NotificationHelper( private val context: Context ) { val NotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // the id where notification channels start private var notificationId = NotificationManager.activeNotifications.size + 5 /** * Enqueue the work manager task */ fun enqueueWork( existingPeriodicWorkPolicy: ExistingPeriodicWorkPolicy ) { // get the notification preferences PreferenceHelper.initialize(context) val notificationsEnabled = PreferenceHelper.getBoolean( PreferenceKeys.NOTIFICATION_ENABLED, true ) val checkingFrequency = PreferenceHelper.getString( PreferenceKeys.CHECKING_FREQUENCY, "60" ).toLong() // schedule the work manager request if logged in and notifications enabled if (notificationsEnabled && PreferenceHelper.getToken() != "") { // required network type for the work val networkType = when ( PreferenceHelper.getString(PreferenceKeys.REQUIRED_NETWORK, "all") ) { "all" -> NetworkType.CONNECTED "wifi" -> NetworkType.UNMETERED "metered" -> NetworkType.METERED else -> NetworkType.CONNECTED } // requirements for the work // here: network needed to run the task val constraints = Constraints.Builder() .setRequiredNetworkType(networkType) .build() // create the worker val notificationWorker = PeriodicWorkRequest.Builder( NotificationWorker::class.java, checkingFrequency, TimeUnit.MINUTES ) .setConstraints(constraints) .build() // enqueue the task WorkManager.getInstance(context) .enqueueUniquePeriodicWork( NOTIFICATION_WORK_NAME, existingPeriodicWorkPolicy, notificationWorker ) } else { // cancel the work if notifications are disabled or the user is not logged in WorkManager.getInstance(context) .cancelUniqueWork(NOTIFICATION_WORK_NAME) } } /** * check whether new streams are available in subscriptions */ fun checkForNewStreams(): Boolean { var success = true val token = PreferenceHelper.getToken() runBlocking { val task = async { if (token != "") { RetrofitInstance.authApi.getFeed(token) } else { RetrofitInstance.authApi.getUnauthenticatedFeed( SubscriptionHelper.getFormattedLocalSubscriptions() ) } } // fetch the users feed val videoFeed = try { task.await() } catch (e: Exception) { success = false return@runBlocking } val lastSeenStreamId = PreferenceHelper.getLatestVideoId() val latestFeedStreamId = videoFeed[0].url.toID() // first time notifications enabled or no new video available if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) { PreferenceHelper.setLatestVideoId(lastSeenStreamId) return@runBlocking } // filter the new videos out val lastSeenStreamItem = videoFeed.filter { it.url.toID() == lastSeenStreamId } // previous video not found if (lastSeenStreamItem.isEmpty()) return@runBlocking val lastStreamIndex = videoFeed.indexOf(lastSeenStreamItem[0]) val newVideos = videoFeed.filterIndexed { index, _ -> index < lastStreamIndex } // group the new streams by the uploader val channelGroups = newVideos.groupBy { it.uploaderUrl } // create a notification for each new stream channelGroups.forEach { (_, streams) -> createNotification( group = streams[0].uploaderUrl.toID(), title = streams[0].uploaderName.toString(), isSummary = true ) streams.forEach { streamItem -> notificationId += 1 createNotification( title = streamItem.title.toString(), description = streamItem.uploaderName.toString(), group = streamItem.uploaderUrl.toID() ) } } // save the latest streams that got notified about PreferenceHelper.setLatestVideoId(videoFeed[0].url.toID()) } // return whether the work succeeded return success } /** * Notification that is created when new streams are found */ private fun createNotification( title: String, group: String, description: String? = null, isSummary: Boolean = false ) { val intent = Intent(context, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } val pendingIntent: PendingIntent = PendingIntent.getActivity( context, 0, intent, PendingIntent.FLAG_IMMUTABLE ) val builder = NotificationCompat.Builder(context, PUSH_CHANNEL_ID) .setContentTitle(title) .setGroup(group) .setSmallIcon(R.drawable.ic_notification) .setPriority(NotificationCompat.PRIORITY_DEFAULT) // Set the intent that will fire when the user taps the notification .setContentIntent(pendingIntent) .setAutoCancel(true) if (isSummary) { builder.setGroupSummary(true) } else { builder.setContentText(description) } with(NotificationManagerCompat.from(context)) { // notificationId is a unique int for each notification that you must define notify(notificationId, builder.build()) } } }