2022-07-28 16:09:56 +05:30
|
|
|
package com.github.libretube.util
|
|
|
|
|
2022-08-26 12:12:13 +05:30
|
|
|
import android.app.NotificationManager
|
2022-07-28 16:09:56 +05:30
|
|
|
import android.app.PendingIntent
|
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
2022-09-09 00:35:51 +05:30
|
|
|
import android.os.Build
|
2022-07-28 16:09:56 +05:30
|
|
|
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.R
|
|
|
|
import com.github.libretube.activities.MainActivity
|
2022-08-14 13:25:28 +05:30
|
|
|
import com.github.libretube.api.RetrofitInstance
|
|
|
|
import com.github.libretube.api.SubscriptionHelper
|
2022-09-08 22:12:52 +05:30
|
|
|
import com.github.libretube.constants.NOTIFICATION_WORK_NAME
|
|
|
|
import com.github.libretube.constants.PUSH_CHANNEL_ID
|
2022-09-08 21:59:00 +05:30
|
|
|
import com.github.libretube.constants.PreferenceKeys
|
2022-09-08 22:01:22 +05:30
|
|
|
import com.github.libretube.extensions.toID
|
2022-07-28 16:09:56 +05:30
|
|
|
import kotlinx.coroutines.async
|
|
|
|
import kotlinx.coroutines.runBlocking
|
|
|
|
import java.util.concurrent.TimeUnit
|
|
|
|
|
2022-08-26 02:06:07 +05:30
|
|
|
class NotificationHelper(
|
|
|
|
private val context: Context
|
|
|
|
) {
|
2022-08-26 22:39:09 +05:30
|
|
|
val NotificationManager =
|
|
|
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
2022-08-26 12:12:13 +05:30
|
|
|
|
|
|
|
// the id where notification channels start
|
2022-09-09 00:35:51 +05:30
|
|
|
private var notificationId = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
NotificationManager.activeNotifications.size + 5
|
|
|
|
} else {
|
|
|
|
5
|
|
|
|
}
|
2022-08-26 02:06:07 +05:30
|
|
|
|
2022-08-20 13:12:24 +05:30
|
|
|
/**
|
|
|
|
* Enqueue the work manager task
|
|
|
|
*/
|
2022-07-28 16:09:56 +05:30
|
|
|
fun enqueueWork(
|
2022-07-30 14:38:28 +05:30
|
|
|
existingPeriodicWorkPolicy: ExistingPeriodicWorkPolicy
|
2022-07-28 16:09:56 +05:30
|
|
|
) {
|
2022-07-28 19:28:22 +05:30
|
|
|
// get the notification preferences
|
2022-08-26 12:12:13 +05:30
|
|
|
PreferenceHelper.initialize(context)
|
2022-07-28 18:01:35 +05:30
|
|
|
val notificationsEnabled = PreferenceHelper.getBoolean(
|
|
|
|
PreferenceKeys.NOTIFICATION_ENABLED,
|
|
|
|
true
|
2022-07-28 16:09:56 +05:30
|
|
|
)
|
|
|
|
|
2022-07-28 18:01:35 +05:30
|
|
|
val checkingFrequency = PreferenceHelper.getString(
|
|
|
|
PreferenceKeys.CHECKING_FREQUENCY,
|
|
|
|
"60"
|
|
|
|
).toLong()
|
|
|
|
|
2022-07-29 17:18:59 +05:30
|
|
|
// schedule the work manager request if logged in and notifications enabled
|
|
|
|
if (notificationsEnabled && PreferenceHelper.getToken() != "") {
|
2022-07-31 18:06:39 +05:30
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-07-28 19:28:22 +05:30
|
|
|
// requirements for the work
|
|
|
|
// here: network needed to run the task
|
2022-07-28 18:01:35 +05:30
|
|
|
val constraints = Constraints.Builder()
|
2022-07-31 18:06:39 +05:30
|
|
|
.setRequiredNetworkType(networkType)
|
2022-07-28 18:01:35 +05:30
|
|
|
.build()
|
|
|
|
|
2022-07-28 19:28:22 +05:30
|
|
|
// create the worker
|
|
|
|
val notificationWorker = PeriodicWorkRequest.Builder(
|
2022-07-28 18:01:35 +05:30
|
|
|
NotificationWorker::class.java,
|
2022-07-28 18:19:02 +05:30
|
|
|
checkingFrequency,
|
|
|
|
TimeUnit.MINUTES
|
2022-07-28 16:09:56 +05:30
|
|
|
)
|
2022-07-28 18:01:35 +05:30
|
|
|
.setConstraints(constraints)
|
2022-07-28 19:28:22 +05:30
|
|
|
.build()
|
2022-07-28 18:01:35 +05:30
|
|
|
|
2022-07-28 19:28:22 +05:30
|
|
|
// enqueue the task
|
2022-07-28 18:01:35 +05:30
|
|
|
WorkManager.getInstance(context)
|
|
|
|
.enqueueUniquePeriodicWork(
|
2022-08-20 13:12:24 +05:30
|
|
|
NOTIFICATION_WORK_NAME,
|
2022-07-30 14:38:28 +05:30
|
|
|
existingPeriodicWorkPolicy,
|
2022-07-28 19:28:22 +05:30
|
|
|
notificationWorker
|
2022-07-28 18:01:35 +05:30
|
|
|
)
|
|
|
|
} else {
|
2022-07-29 17:18:59 +05:30
|
|
|
// cancel the work if notifications are disabled or the user is not logged in
|
2022-07-28 18:01:35 +05:30
|
|
|
WorkManager.getInstance(context)
|
2022-08-20 13:12:24 +05:30
|
|
|
.cancelUniqueWork(NOTIFICATION_WORK_NAME)
|
2022-07-28 18:01:35 +05:30
|
|
|
}
|
2022-07-28 16:09:56 +05:30
|
|
|
}
|
|
|
|
|
2022-07-28 18:01:35 +05:30
|
|
|
/**
|
|
|
|
* check whether new streams are available in subscriptions
|
|
|
|
*/
|
2022-08-26 02:06:07 +05:30
|
|
|
fun checkForNewStreams(): Boolean {
|
2022-08-26 12:12:13 +05:30
|
|
|
var success = true
|
2022-07-29 17:18:59 +05:30
|
|
|
|
2022-07-28 16:09:56 +05:30
|
|
|
val token = PreferenceHelper.getToken()
|
|
|
|
runBlocking {
|
|
|
|
val task = async {
|
2022-08-24 21:26:57 +05:30
|
|
|
if (token != "") {
|
|
|
|
RetrofitInstance.authApi.getFeed(token)
|
|
|
|
} else {
|
|
|
|
RetrofitInstance.authApi.getUnauthenticatedFeed(
|
|
|
|
SubscriptionHelper.getFormattedLocalSubscriptions()
|
|
|
|
)
|
|
|
|
}
|
2022-07-28 16:09:56 +05:30
|
|
|
}
|
2022-07-28 18:19:02 +05:30
|
|
|
// fetch the users feed
|
2022-07-28 18:15:29 +05:30
|
|
|
val videoFeed = try {
|
|
|
|
task.await()
|
|
|
|
} catch (e: Exception) {
|
2022-08-26 12:12:13 +05:30
|
|
|
success = false
|
2022-07-28 18:15:29 +05:30
|
|
|
return@runBlocking
|
|
|
|
}
|
2022-07-28 19:28:22 +05:30
|
|
|
|
2022-07-28 18:01:35 +05:30
|
|
|
val lastSeenStreamId = PreferenceHelper.getLatestVideoId()
|
2022-09-09 00:35:51 +05:30
|
|
|
val latestFeedStreamId = videoFeed[0].url!!.toID()
|
2022-07-28 19:28:22 +05:30
|
|
|
|
2022-08-26 02:06:07 +05:30
|
|
|
// first time notifications enabled or no new video available
|
|
|
|
if (lastSeenStreamId == "" || lastSeenStreamId == latestFeedStreamId) {
|
2022-08-24 21:26:57 +05:30
|
|
|
PreferenceHelper.setLatestVideoId(lastSeenStreamId)
|
2022-08-26 02:06:07 +05:30
|
|
|
return@runBlocking
|
|
|
|
}
|
|
|
|
|
|
|
|
// filter the new videos out
|
2022-09-09 00:35:51 +05:30
|
|
|
val lastSeenStreamItem = videoFeed.filter { it.url!!.toID() == lastSeenStreamId }
|
2022-08-26 02:06:07 +05:30
|
|
|
|
|
|
|
// 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) ->
|
2022-08-26 12:12:13 +05:30
|
|
|
createNotification(
|
2022-09-09 00:35:51 +05:30
|
|
|
group = streams[0].uploaderUrl!!.toID(),
|
2022-08-26 12:12:13 +05:30
|
|
|
title = streams[0].uploaderName.toString(),
|
|
|
|
isSummary = true
|
2022-08-26 02:06:07 +05:30
|
|
|
)
|
|
|
|
|
|
|
|
streams.forEach { streamItem ->
|
|
|
|
notificationId += 1
|
|
|
|
createNotification(
|
|
|
|
title = streamItem.title.toString(),
|
|
|
|
description = streamItem.uploaderName.toString(),
|
2022-09-09 00:35:51 +05:30
|
|
|
group = streamItem.uploaderUrl!!.toID()
|
2022-08-26 02:06:07 +05:30
|
|
|
)
|
2022-07-28 18:01:35 +05:30
|
|
|
}
|
|
|
|
}
|
2022-08-26 12:12:13 +05:30
|
|
|
// save the latest streams that got notified about
|
2022-09-09 00:35:51 +05:30
|
|
|
PreferenceHelper.setLatestVideoId(videoFeed[0].url!!.toID())
|
2022-07-28 18:01:35 +05:30
|
|
|
}
|
2022-07-29 17:18:59 +05:30
|
|
|
// return whether the work succeeded
|
2022-08-26 12:12:13 +05:30
|
|
|
return success
|
2022-07-28 16:09:56 +05:30
|
|
|
}
|
|
|
|
|
2022-07-28 18:15:29 +05:30
|
|
|
/**
|
|
|
|
* Notification that is created when new streams are found
|
|
|
|
*/
|
2022-08-26 02:06:07 +05:30
|
|
|
private fun createNotification(
|
|
|
|
title: String,
|
2022-08-26 12:12:13 +05:30
|
|
|
group: String,
|
|
|
|
description: String? = null,
|
|
|
|
isSummary: Boolean = false
|
2022-08-26 02:06:07 +05:30
|
|
|
) {
|
2022-07-28 16:09:56 +05:30
|
|
|
val intent = Intent(context, MainActivity::class.java).apply {
|
|
|
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
|
|
}
|
2022-07-30 14:51:18 +05:30
|
|
|
val pendingIntent: PendingIntent = PendingIntent.getActivity(
|
|
|
|
context,
|
|
|
|
0,
|
|
|
|
intent,
|
|
|
|
PendingIntent.FLAG_IMMUTABLE
|
|
|
|
)
|
2022-07-28 16:09:56 +05:30
|
|
|
|
2022-07-30 14:51:18 +05:30
|
|
|
val builder = NotificationCompat.Builder(context, PUSH_CHANNEL_ID)
|
2022-07-28 16:09:56 +05:30
|
|
|
.setContentTitle(title)
|
2022-08-26 02:06:07 +05:30
|
|
|
.setGroup(group)
|
|
|
|
.setSmallIcon(R.drawable.ic_notification)
|
2022-07-28 16:09:56 +05:30
|
|
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
|
|
|
// Set the intent that will fire when the user taps the notification
|
|
|
|
.setContentIntent(pendingIntent)
|
|
|
|
.setAutoCancel(true)
|
2022-08-26 02:06:07 +05:30
|
|
|
|
2022-08-26 12:34:35 +05:30
|
|
|
if (isSummary) {
|
|
|
|
builder.setGroupSummary(true)
|
|
|
|
} else {
|
|
|
|
builder.setContentText(description)
|
|
|
|
}
|
2022-08-26 02:06:07 +05:30
|
|
|
|
|
|
|
with(NotificationManagerCompat.from(context)) {
|
|
|
|
// notificationId is a unique int for each notification that you must define
|
2022-08-26 12:12:13 +05:30
|
|
|
notify(notificationId, builder.build())
|
2022-07-28 16:09:56 +05:30
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|