feat: move subscriptions to new notification method
This commit is contained in:
parent
a39db5ea93
commit
808d4e6bf5
32 changed files with 491 additions and 620 deletions
|
@ -69,7 +69,7 @@
|
||||||
android:name="android.appwidget.provider"
|
android:name="android.appwidget.provider"
|
||||||
android:resource="@xml/currently_airing_widget_info" />
|
android:resource="@xml/currently_airing_widget_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name=".subcriptions.NotificationClickReceiver" />
|
<receiver android:name=".notifications.IncognitoNotificationClickReceiver" />
|
||||||
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
|
@ -307,14 +307,6 @@
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
|
||||||
|
|
||||||
<receiver
|
|
||||||
android:name=".subcriptions.AlarmReceiver"
|
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
||||||
<action android:name="Aani.dantotsu.ACTION_ALARM" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
<receiver android:name=".notifications.AlarmPermissionStateReceiver"
|
<receiver android:name=".notifications.AlarmPermissionStateReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
@ -330,7 +322,7 @@
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name=".notifications.anilist.AnilistNotificationReceiver"/>
|
<receiver android:name=".notifications.anilist.AnilistNotificationReceiver"/>
|
||||||
<receiver android:name=".notifications.comment.CommentNotificationReceiver"/>
|
<receiver android:name=".notifications.comment.CommentNotificationReceiver"/>
|
||||||
|
<receiver android:name=".notifications.subscription.SubscriptionNotificationReceiver"/>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="preloaded_fonts"
|
android:name="preloaded_fonts"
|
||||||
|
|
|
@ -87,6 +87,7 @@ class App : MultiDexApplication() {
|
||||||
|
|
||||||
Logger.init(this)
|
Logger.init(this)
|
||||||
Thread.setDefaultUncaughtExceptionHandler(FinalExceptionHandler())
|
Thread.setDefaultUncaughtExceptionHandler(FinalExceptionHandler())
|
||||||
|
Logger.log("App: Logging started")
|
||||||
|
|
||||||
initializeNetwork(baseContext)
|
initializeNetwork(baseContext)
|
||||||
|
|
||||||
|
@ -122,22 +123,10 @@ class App : MultiDexApplication() {
|
||||||
CommentsAPI.fetchAuthToken()
|
CommentsAPI.fetchAuthToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
startWorkers()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startWorkers() {
|
|
||||||
val useAlarmManager = PrefManager.getVal<Boolean>(PrefName.UseAlarmManager)
|
val useAlarmManager = PrefManager.getVal<Boolean>(PrefName.UseAlarmManager)
|
||||||
|
|
||||||
TaskScheduler.create(this, useAlarmManager).scheduleAllTasks(this)
|
TaskScheduler.create(this, useAlarmManager).scheduleAllTasks(this)
|
||||||
|
|
||||||
androidx.work.WorkManager.getInstance(this)
|
|
||||||
.enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java))
|
|
||||||
|
|
||||||
androidx.work.WorkManager.getInstance(this)
|
|
||||||
.enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun setupNotificationChannels() {
|
private fun setupNotificationChannels() {
|
||||||
try {
|
try {
|
||||||
Notifications.createChannels(this)
|
Notifications.createChannels(this)
|
||||||
|
|
|
@ -70,6 +70,7 @@ import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.getSystemService
|
import androidx.core.content.ContextCompat.getSystemService
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
@ -92,13 +93,13 @@ import ani.dantotsu.connections.anilist.api.FuzzyDate
|
||||||
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
import ani.dantotsu.connections.crashlytics.CrashlyticsInterface
|
||||||
import ani.dantotsu.databinding.ItemCountDownBinding
|
import ani.dantotsu.databinding.ItemCountDownBinding
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
import ani.dantotsu.notifications.IncognitoNotificationClickReceiver
|
||||||
import ani.dantotsu.others.SpoilerPlugin
|
import ani.dantotsu.others.SpoilerPlugin
|
||||||
import ani.dantotsu.parsers.ShowResponse
|
import ani.dantotsu.parsers.ShowResponse
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
|
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
|
||||||
import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt
|
import ani.dantotsu.settings.saving.internal.PreferenceKeystore.Companion.generateSalt
|
||||||
import ani.dantotsu.subcriptions.NotificationClickReceiver
|
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.RequestBuilder
|
import com.bumptech.glide.RequestBuilder
|
||||||
|
@ -1172,7 +1173,7 @@ fun incognitoNotification(context: Context) {
|
||||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
|
||||||
if (incognito) {
|
if (incognito) {
|
||||||
val intent = Intent(context, NotificationClickReceiver::class.java)
|
val intent = Intent(context, IncognitoNotificationClickReceiver::class.java)
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
context, 0, intent,
|
context, 0, intent,
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
@ -1190,6 +1191,28 @@ fun incognitoNotification(context: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasNotificationPermission(context: Context): Boolean {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
context.checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED
|
||||||
|
} else {
|
||||||
|
NotificationManagerCompat.from(context).areNotificationsEnabled()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openSettings(context: Context, channelId: String?): Boolean {
|
||||||
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val intent = Intent(
|
||||||
|
if (channelId != null) Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS
|
||||||
|
else Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||||
|
).apply {
|
||||||
|
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||||
|
putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
true
|
||||||
|
} else false
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun View.pop() {
|
suspend fun View.pop() {
|
||||||
currActivity()?.runOnUiThread {
|
currActivity()?.runOnUiThread {
|
||||||
ObjectAnimator.ofFloat(this@pop, "scaleX", 1f, 1.25f).setDuration(120).start()
|
ObjectAnimator.ofFloat(this@pop, "scaleX", 1f, 1.25f).setDuration(120).start()
|
||||||
|
|
|
@ -38,6 +38,7 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.exoplayer.offline.Download
|
import androidx.media3.exoplayer.offline.Download
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
import ani.dantotsu.connections.anilist.Anilist
|
import ani.dantotsu.connections.anilist.Anilist
|
||||||
import ani.dantotsu.connections.anilist.AnilistHomeViewModel
|
import ani.dantotsu.connections.anilist.AnilistHomeViewModel
|
||||||
import ani.dantotsu.databinding.ActivityMainBinding
|
import ani.dantotsu.databinding.ActivityMainBinding
|
||||||
|
@ -49,6 +50,8 @@ import ani.dantotsu.home.LoginFragment
|
||||||
import ani.dantotsu.home.MangaFragment
|
import ani.dantotsu.home.MangaFragment
|
||||||
import ani.dantotsu.home.NoInternet
|
import ani.dantotsu.home.NoInternet
|
||||||
import ani.dantotsu.media.MediaDetailsActivity
|
import ani.dantotsu.media.MediaDetailsActivity
|
||||||
|
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
||||||
|
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
||||||
import ani.dantotsu.others.CustomBottomDialog
|
import ani.dantotsu.others.CustomBottomDialog
|
||||||
import ani.dantotsu.profile.ProfileActivity
|
import ani.dantotsu.profile.ProfileActivity
|
||||||
import ani.dantotsu.profile.activity.FeedActivity
|
import ani.dantotsu.profile.activity.FeedActivity
|
||||||
|
@ -59,7 +62,6 @@ import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
|
import ani.dantotsu.settings.saving.SharedPreferenceBooleanLiveData
|
||||||
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
|
import ani.dantotsu.settings.saving.internal.PreferenceKeystore
|
||||||
import ani.dantotsu.settings.saving.internal.PreferencePackager
|
import ani.dantotsu.settings.saving.internal.PreferencePackager
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
|
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||||
|
@ -98,6 +100,13 @@ class MainActivity : AppCompatActivity() {
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
|
||||||
|
androidx.work.WorkManager.getInstance(this)
|
||||||
|
.enqueue(OneTimeWorkRequest.Companion.from(CommentNotificationWorker::class.java))
|
||||||
|
|
||||||
|
androidx.work.WorkManager.getInstance(this)
|
||||||
|
.enqueue(OneTimeWorkRequest.Companion.from(AnilistNotificationWorker::class.java))
|
||||||
|
|
||||||
val action = intent.action
|
val action = intent.action
|
||||||
val type = intent.type
|
val type = intent.type
|
||||||
if (Intent.ACTION_VIEW == action && type != null) {
|
if (Intent.ACTION_VIEW == action && type != null) {
|
||||||
|
@ -405,9 +414,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(500)
|
|
||||||
startSubscription()
|
|
||||||
}
|
}
|
||||||
load = true
|
load = true
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,24 +14,22 @@ class UrlMedia : Activity() {
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
val data: Uri? = intent?.data
|
val data: Uri? = intent?.data
|
||||||
val type = data?.pathSegments?.getOrNull(0)
|
val type = data?.pathSegments?.getOrNull(0)
|
||||||
if (type == "anime" || type == "manga") {
|
if (type != "user") {
|
||||||
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
|
var id: Int? = intent?.extras?.getInt("media", 0) ?: 0
|
||||||
var isMAL = false
|
var isMAL = false
|
||||||
var continueMedia = true
|
var continueMedia = true
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
continueMedia = false
|
continueMedia = false
|
||||||
isMAL = data.host != "anilist.co"
|
isMAL = data?.host != "anilist.co"
|
||||||
id = data.pathSegments?.getOrNull(1)?.toIntOrNull()
|
id = data?.pathSegments?.getOrNull(1)?.toIntOrNull()
|
||||||
} else loadMedia = id
|
} else loadMedia = id
|
||||||
startMainActivity(
|
startMainActivity(
|
||||||
this,
|
this,
|
||||||
bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
|
bundleOf("mediaId" to id, "mal" to isMAL, "continue" to continueMedia)
|
||||||
)
|
)
|
||||||
} else if (type == "user") {
|
} else {
|
||||||
val username = data.pathSegments?.getOrNull(1)
|
val username = data.pathSegments?.getOrNull(1)
|
||||||
startMainActivity(this, bundleOf("username" to username))
|
startMainActivity(this, bundleOf("username" to username))
|
||||||
} else {
|
|
||||||
startMainActivity(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,7 +16,6 @@ import androidx.core.content.ContextCompat.startActivity
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.connections.comments.CommentsAPI
|
|
||||||
import ani.dantotsu.databinding.DialogLayoutBinding
|
import ani.dantotsu.databinding.DialogLayoutBinding
|
||||||
import ani.dantotsu.databinding.ItemAnimeWatchBinding
|
import ani.dantotsu.databinding.ItemAnimeWatchBinding
|
||||||
import ani.dantotsu.databinding.ItemChipBinding
|
import ani.dantotsu.databinding.ItemChipBinding
|
||||||
|
@ -30,10 +29,9 @@ import ani.dantotsu.parsers.DynamicAnimeParser
|
||||||
import ani.dantotsu.parsers.WatchSources
|
import ani.dantotsu.parsers.WatchSources
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
import eu.kanade.tachiyomi.animesource.online.AnimeHttpSource
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK
|
||||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -196,7 +194,7 @@ class AnimeWatchAdapter(
|
||||||
subscribeButton(false)
|
subscribeButton(false)
|
||||||
|
|
||||||
binding.animeSourceSubscribe.setOnLongClickListener {
|
binding.animeSourceSubscribe.setOnLongClickListener {
|
||||||
openSettings(fragment.requireContext(), getChannelId(true, media.id))
|
openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Nested Button
|
//Nested Button
|
||||||
|
|
|
@ -43,13 +43,9 @@ import ani.dantotsu.parsers.HAnimeSources
|
||||||
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
|
import ani.dantotsu.settings.extensionprefs.AnimeSourcePreferencesFragment
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.subcriptions.Notifications
|
import ani.dantotsu.notifications.subscription.SubscriptionHelper
|
||||||
import ani.dantotsu.subcriptions.Notifications.Group.ANIME_GROUP
|
import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
|
||||||
import ani.dantotsu.subcriptions.SubscriptionHelper
|
|
||||||
import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.navigationrail.NavigationRailView
|
|
||||||
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
import eu.kanade.tachiyomi.animesource.ConfigurableAnimeSource
|
||||||
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
|
import eu.kanade.tachiyomi.extension.anime.model.AnimeExtension
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -333,16 +329,7 @@ class AnimeWatchFragment : Fragment() {
|
||||||
var subscribed = false
|
var subscribed = false
|
||||||
fun onNotificationPressed(subscribed: Boolean, source: String) {
|
fun onNotificationPressed(subscribed: Boolean, source: String) {
|
||||||
this.subscribed = subscribed
|
this.subscribed = subscribed
|
||||||
saveSubscription(requireContext(), media, subscribed)
|
saveSubscription(media, subscribed)
|
||||||
if (!subscribed)
|
|
||||||
Notifications.deleteChannel(requireContext(), getChannelId(true, media.id))
|
|
||||||
else
|
|
||||||
Notifications.createChannel(
|
|
||||||
requireContext(),
|
|
||||||
ANIME_GROUP,
|
|
||||||
getChannelId(true, media.id),
|
|
||||||
media.userPreferredName
|
|
||||||
)
|
|
||||||
snackString(
|
snackString(
|
||||||
if (subscribed) getString(R.string.subscribed_notification, source)
|
if (subscribed) getString(R.string.subscribed_notification, source)
|
||||||
else getString(R.string.unsubscribed_notification)
|
else getString(R.string.unsubscribed_notification)
|
||||||
|
|
|
@ -13,11 +13,9 @@ import android.widget.LinearLayout
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
import androidx.core.view.children
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import ani.dantotsu.*
|
import ani.dantotsu.*
|
||||||
import ani.dantotsu.connections.comments.CommentsAPI
|
|
||||||
import ani.dantotsu.databinding.DialogLayoutBinding
|
import ani.dantotsu.databinding.DialogLayoutBinding
|
||||||
import ani.dantotsu.databinding.ItemAnimeWatchBinding
|
import ani.dantotsu.databinding.ItemAnimeWatchBinding
|
||||||
import ani.dantotsu.databinding.ItemChipBinding
|
import ani.dantotsu.databinding.ItemChipBinding
|
||||||
|
@ -33,9 +31,8 @@ import ani.dantotsu.parsers.MangaSources
|
||||||
import ani.dantotsu.settings.FAQActivity
|
import ani.dantotsu.settings.FAQActivity
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
|
||||||
import com.google.android.material.chip.Chip
|
import com.google.android.material.chip.Chip
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||||
import kotlinx.coroutines.MainScope
|
import kotlinx.coroutines.MainScope
|
||||||
|
@ -161,7 +158,7 @@ class MangaReadAdapter(
|
||||||
subscribeButton(false)
|
subscribeButton(false)
|
||||||
|
|
||||||
binding.animeSourceSubscribe.setOnLongClickListener {
|
binding.animeSourceSubscribe.setOnLongClickListener {
|
||||||
openSettings(fragment.requireContext(), getChannelId(true, media.id))
|
openSettings(fragment.requireContext(), CHANNEL_SUBSCRIPTION_CHECK)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.animeNestedButton.setOnClickListener {
|
binding.animeNestedButton.setOnClickListener {
|
||||||
|
|
|
@ -46,13 +46,9 @@ import ani.dantotsu.parsers.MangaSources
|
||||||
import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment
|
import ani.dantotsu.settings.extensionprefs.MangaSourcePreferencesFragment
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.subcriptions.Notifications
|
import ani.dantotsu.notifications.subscription.SubscriptionHelper
|
||||||
import ani.dantotsu.subcriptions.Notifications.Group.MANGA_GROUP
|
import ani.dantotsu.notifications.subscription.SubscriptionHelper.Companion.saveSubscription
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.getChannelId
|
|
||||||
import ani.dantotsu.subcriptions.SubscriptionHelper
|
|
||||||
import ani.dantotsu.subcriptions.SubscriptionHelper.Companion.saveSubscription
|
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import com.google.android.material.navigationrail.NavigationRailView
|
|
||||||
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
|
import eu.kanade.tachiyomi.extension.manga.model.MangaExtension
|
||||||
import eu.kanade.tachiyomi.source.ConfigurableSource
|
import eu.kanade.tachiyomi.source.ConfigurableSource
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -347,16 +343,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
var subscribed = false
|
var subscribed = false
|
||||||
fun onNotificationPressed(subscribed: Boolean, source: String) {
|
fun onNotificationPressed(subscribed: Boolean, source: String) {
|
||||||
this.subscribed = subscribed
|
this.subscribed = subscribed
|
||||||
saveSubscription(requireContext(), media, subscribed)
|
saveSubscription(media, subscribed)
|
||||||
if (!subscribed)
|
|
||||||
Notifications.deleteChannel(requireContext(), getChannelId(true, media.id))
|
|
||||||
else
|
|
||||||
Notifications.createChannel(
|
|
||||||
requireContext(),
|
|
||||||
MANGA_GROUP,
|
|
||||||
getChannelId(true, media.id),
|
|
||||||
media.userPreferredName
|
|
||||||
)
|
|
||||||
snackString(
|
snackString(
|
||||||
if (subscribed) getString(R.string.subscribed_notification, source)
|
if (subscribed) getString(R.string.subscribed_notification, source)
|
||||||
else getString(R.string.unsubscribed_notification)
|
else getString(R.string.unsubscribed_notification)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.os.Build
|
||||||
import ani.dantotsu.notifications.anilist.AnilistNotificationReceiver
|
import ani.dantotsu.notifications.anilist.AnilistNotificationReceiver
|
||||||
import ani.dantotsu.notifications.comment.CommentNotificationReceiver
|
import ani.dantotsu.notifications.comment.CommentNotificationReceiver
|
||||||
import ani.dantotsu.notifications.TaskScheduler.TaskType
|
import ani.dantotsu.notifications.TaskScheduler.TaskType
|
||||||
|
import ani.dantotsu.notifications.subscription.SubscriptionNotificationReceiver
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -25,6 +26,11 @@ class AlarmManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
context,
|
context,
|
||||||
AnilistNotificationReceiver::class.java
|
AnilistNotificationReceiver::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TaskType.SUBSCRIPTION_NOTIFICATION -> Intent(
|
||||||
|
context,
|
||||||
|
SubscriptionNotificationReceiver::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
@ -64,6 +70,11 @@ class AlarmManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
context,
|
context,
|
||||||
AnilistNotificationReceiver::class.java
|
AnilistNotificationReceiver::class.java
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TaskType.SUBSCRIPTION_NOTIFICATION -> Intent(
|
||||||
|
context,
|
||||||
|
SubscriptionNotificationReceiver::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
val pendingIntent = PendingIntent.getBroadcast(
|
||||||
|
|
|
@ -13,52 +13,48 @@ import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.util.Logger
|
import ani.dantotsu.util.Logger
|
||||||
|
|
||||||
class BootCompletedReceiver : BroadcastReceiver() {
|
class BootCompletedReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
|
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
|
||||||
if (context != null) {
|
val scheduler = AlarmManagerScheduler(context)
|
||||||
val scheduler = AlarmManagerScheduler(context)
|
PrefManager.init(context)
|
||||||
PrefManager.init(context)
|
Logger.init(context)
|
||||||
Logger.init(context)
|
Logger.log("Starting Dantotsu Subscription Service on Boot")
|
||||||
Logger.log("Starting Dantotsu Subscription Service on Boot")
|
if (PrefManager.getVal(PrefName.UseAlarmManager)) {
|
||||||
if (PrefManager.getVal(PrefName.UseAlarmManager)) {
|
val commentInterval =
|
||||||
val commentInterval =
|
CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)]
|
||||||
CommentNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.CommentNotificationInterval)]
|
val anilistInterval =
|
||||||
val anilistInterval =
|
AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
|
||||||
AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
|
scheduler.scheduleRepeatingTask(
|
||||||
scheduler.scheduleRepeatingTask(
|
TaskType.COMMENT_NOTIFICATION,
|
||||||
TaskType.COMMENT_NOTIFICATION,
|
commentInterval
|
||||||
commentInterval
|
)
|
||||||
)
|
scheduler.scheduleRepeatingTask(
|
||||||
scheduler.scheduleRepeatingTask(
|
TaskType.ANILIST_NOTIFICATION,
|
||||||
TaskType.ANILIST_NOTIFICATION,
|
anilistInterval
|
||||||
anilistInterval
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AlarmPermissionStateReceiver : BroadcastReceiver() {
|
class AlarmPermissionStateReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
if (intent?.action == AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) {
|
if (intent?.action == AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED) {
|
||||||
if (context != null) {
|
PrefManager.init(context)
|
||||||
PrefManager.init(context)
|
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
val canScheduleExactAlarms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
val canScheduleExactAlarms = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
alarmManager.canScheduleExactAlarms()
|
||||||
alarmManager.canScheduleExactAlarms()
|
} else {
|
||||||
} else {
|
true
|
||||||
true
|
|
||||||
}
|
|
||||||
if(canScheduleExactAlarms) {
|
|
||||||
TaskScheduler.create(context, false).cancelAllTasks()
|
|
||||||
TaskScheduler.create(context, true).scheduleAllTasks(context)
|
|
||||||
} else {
|
|
||||||
TaskScheduler.create(context, true).cancelAllTasks()
|
|
||||||
TaskScheduler.create(context, false).scheduleAllTasks(context)
|
|
||||||
}
|
|
||||||
PrefManager.setVal(PrefName.UseAlarmManager, canScheduleExactAlarms)
|
|
||||||
}
|
}
|
||||||
|
if (canScheduleExactAlarms) {
|
||||||
|
TaskScheduler.create(context, false).cancelAllTasks()
|
||||||
|
TaskScheduler.create(context, true).scheduleAllTasks(context)
|
||||||
|
} else {
|
||||||
|
TaskScheduler.create(context, true).cancelAllTasks()
|
||||||
|
TaskScheduler.create(context, false).scheduleAllTasks(context)
|
||||||
|
}
|
||||||
|
PrefManager.setVal(PrefName.UseAlarmManager, canScheduleExactAlarms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package ani.dantotsu.subcriptions
|
package ani.dantotsu.notifications
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
|
@ -9,7 +9,7 @@ import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
|
||||||
|
|
||||||
class NotificationClickReceiver : BroadcastReceiver() {
|
class IncognitoNotificationClickReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
|
||||||
PrefManager.setVal(PrefName.Incognito, false)
|
PrefManager.setVal(PrefName.Incognito, false)
|
|
@ -3,6 +3,7 @@ package ani.dantotsu.notifications
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
||||||
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
||||||
|
import ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ interface TaskScheduler {
|
||||||
PrefName.CommentNotificationInterval)]
|
PrefName.CommentNotificationInterval)]
|
||||||
TaskType.ANILIST_NOTIFICATION -> AnilistNotificationWorker.checkIntervals[PrefManager.getVal(
|
TaskType.ANILIST_NOTIFICATION -> AnilistNotificationWorker.checkIntervals[PrefManager.getVal(
|
||||||
PrefName.AnilistNotificationInterval)]
|
PrefName.AnilistNotificationInterval)]
|
||||||
|
TaskType.SUBSCRIPTION_NOTIFICATION -> SubscriptionNotificationWorker.checkIntervals[PrefManager.getVal(
|
||||||
|
PrefName.SubscriptionNotificationInterval)]
|
||||||
}
|
}
|
||||||
scheduleRepeatingTask(taskType, interval)
|
scheduleRepeatingTask(taskType, interval)
|
||||||
}
|
}
|
||||||
|
@ -39,7 +42,8 @@ interface TaskScheduler {
|
||||||
}
|
}
|
||||||
enum class TaskType {
|
enum class TaskType {
|
||||||
COMMENT_NOTIFICATION,
|
COMMENT_NOTIFICATION,
|
||||||
ANILIST_NOTIFICATION
|
ANILIST_NOTIFICATION,
|
||||||
|
SUBSCRIPTION_NOTIFICATION
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ package ani.dantotsu.notifications
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.work.Constraints
|
import androidx.work.Constraints
|
||||||
import androidx.work.PeriodicWorkRequest
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import ani.dantotsu.notifications.TaskScheduler.TaskType
|
||||||
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
||||||
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
||||||
import ani.dantotsu.notifications.TaskScheduler.TaskType
|
import ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker
|
||||||
|
|
||||||
class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
override fun scheduleRepeatingTask(taskType: TaskType, interval: Long) {
|
override fun scheduleRepeatingTask(taskType: TaskType, interval: Long) {
|
||||||
|
@ -17,8 +18,10 @@ class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
TaskType.COMMENT_NOTIFICATION -> {
|
TaskType.COMMENT_NOTIFICATION -> {
|
||||||
val recurringWork = PeriodicWorkRequest.Builder(
|
val recurringWork = PeriodicWorkRequest.Builder(
|
||||||
CommentNotificationWorker::class.java,
|
CommentNotificationWorker::class.java,
|
||||||
interval, java.util.concurrent.TimeUnit.MINUTES,
|
interval,
|
||||||
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, java.util.concurrent.TimeUnit.MINUTES
|
java.util.concurrent.TimeUnit.MINUTES,
|
||||||
|
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
|
||||||
|
java.util.concurrent.TimeUnit.MINUTES
|
||||||
)
|
)
|
||||||
.setConstraints(constraints)
|
.setConstraints(constraints)
|
||||||
.build()
|
.build()
|
||||||
|
@ -32,8 +35,10 @@ class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
TaskType.ANILIST_NOTIFICATION -> {
|
TaskType.ANILIST_NOTIFICATION -> {
|
||||||
val recurringWork = PeriodicWorkRequest.Builder(
|
val recurringWork = PeriodicWorkRequest.Builder(
|
||||||
AnilistNotificationWorker::class.java,
|
AnilistNotificationWorker::class.java,
|
||||||
interval, java.util.concurrent.TimeUnit.MINUTES,
|
interval,
|
||||||
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS, java.util.concurrent.TimeUnit.MINUTES
|
java.util.concurrent.TimeUnit.MINUTES,
|
||||||
|
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
|
||||||
|
java.util.concurrent.TimeUnit.MINUTES
|
||||||
)
|
)
|
||||||
.setConstraints(constraints)
|
.setConstraints(constraints)
|
||||||
.build()
|
.build()
|
||||||
|
@ -43,6 +48,23 @@ class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
recurringWork
|
recurringWork
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskType.SUBSCRIPTION_NOTIFICATION -> {
|
||||||
|
val recurringWork = PeriodicWorkRequest.Builder(
|
||||||
|
SubscriptionNotificationWorker::class.java,
|
||||||
|
interval,
|
||||||
|
java.util.concurrent.TimeUnit.MINUTES,
|
||||||
|
PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS,
|
||||||
|
java.util.concurrent.TimeUnit.MINUTES
|
||||||
|
)
|
||||||
|
.setConstraints(constraints)
|
||||||
|
.build()
|
||||||
|
androidx.work.WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
||||||
|
SubscriptionNotificationWorker.WORK_NAME,
|
||||||
|
androidx.work.ExistingPeriodicWorkPolicy.UPDATE,
|
||||||
|
recurringWork
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +79,11 @@ class WorkManagerScheduler(private val context: Context) : TaskScheduler {
|
||||||
androidx.work.WorkManager.getInstance(context)
|
androidx.work.WorkManager.getInstance(context)
|
||||||
.cancelUniqueWork(AnilistNotificationWorker.WORK_NAME)
|
.cancelUniqueWork(AnilistNotificationWorker.WORK_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TaskType.SUBSCRIPTION_NOTIFICATION -> {
|
||||||
|
androidx.work.WorkManager.getInstance(context)
|
||||||
|
.cancelUniqueWork(SubscriptionNotificationWorker.WORK_NAME)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,15 +11,16 @@ import ani.dantotsu.util.Logger
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class AnilistNotificationReceiver : BroadcastReceiver() {
|
class AnilistNotificationReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
Logger.log("AnilistNotificationReceiver: onReceive")
|
Logger.log("AnilistNotificationReceiver: onReceive")
|
||||||
if (context != null) {
|
runBlocking {
|
||||||
runBlocking {
|
AnilistNotificationTask().execute(context)
|
||||||
AnilistNotificationTask().execute(context)
|
|
||||||
}
|
|
||||||
val anilistInterval =
|
|
||||||
AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
|
|
||||||
AlarmManagerScheduler(context).scheduleRepeatingTask(TaskScheduler.TaskType.ANILIST_NOTIFICATION, anilistInterval)
|
|
||||||
}
|
}
|
||||||
|
val anilistInterval =
|
||||||
|
AnilistNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.AnilistNotificationInterval)]
|
||||||
|
AlarmManagerScheduler(context).scheduleRepeatingTask(
|
||||||
|
TaskScheduler.TaskType.ANILIST_NOTIFICATION,
|
||||||
|
anilistInterval
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ class AnilistNotificationWorker(appContext: Context, workerParams: WorkerParamet
|
||||||
return if (AnilistNotificationTask().execute(applicationContext)) {
|
return if (AnilistNotificationTask().execute(applicationContext)) {
|
||||||
Result.success()
|
Result.success()
|
||||||
} else {
|
} else {
|
||||||
|
Logger.log("AnilistNotificationWorker: doWork failed")
|
||||||
Result.retry()
|
Result.retry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,10 @@ import ani.dantotsu.util.Logger
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
class CommentNotificationReceiver : BroadcastReceiver() {
|
class CommentNotificationReceiver : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
Logger.log("CommentNotificationReceiver: onReceive")
|
Logger.log("CommentNotificationReceiver: onReceive")
|
||||||
if (context != null) {
|
runBlocking {
|
||||||
runBlocking {
|
CommentNotificationTask().execute(context)
|
||||||
CommentNotificationTask().execute(context)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,6 +14,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
|
||||||
return if (CommentNotificationTask().execute(applicationContext)) {
|
return if (CommentNotificationTask().execute(applicationContext)) {
|
||||||
Result.success()
|
Result.success()
|
||||||
} else {
|
} else {
|
||||||
|
Logger.log("CommentNotificationWorker: doWork failed")
|
||||||
Result.retry()
|
Result.retry()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +28,7 @@ class CommentNotificationWorker(appContext: Context, workerParams: WorkerParamet
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val checkIntervals = arrayOf(0L, 720, 1440)
|
val checkIntervals = arrayOf(0L, 480, 720, 1440)
|
||||||
const val WORK_NAME = "ani.dantotsu.notifications.comment.CommentNotificationWorker"
|
const val WORK_NAME = "ani.dantotsu.notifications.comment.CommentNotificationWorker"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package ani.dantotsu.subcriptions
|
package ani.dantotsu.notifications.subscription
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import ani.dantotsu.R
|
import ani.dantotsu.R
|
||||||
import ani.dantotsu.currContext
|
import ani.dantotsu.currContext
|
||||||
import ani.dantotsu.media.Media
|
import ani.dantotsu.media.Media
|
||||||
|
@ -17,15 +16,13 @@ import ani.dantotsu.parsers.MangaSources
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.tryWithSuspend
|
import ani.dantotsu.tryWithSuspend
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
|
||||||
class SubscriptionHelper {
|
class SubscriptionHelper {
|
||||||
companion object {
|
companion object {
|
||||||
private fun loadSelected(
|
private fun loadSelected(
|
||||||
context: Context,
|
mediaId: Int
|
||||||
mediaId: Int,
|
|
||||||
isAdult: Boolean,
|
|
||||||
isAnime: Boolean
|
|
||||||
): Selected {
|
): Selected {
|
||||||
val data =
|
val data =
|
||||||
PrefManager.getNullableCustomVal("${mediaId}-select", null, Selected::class.java)
|
PrefManager.getNullableCustomVal("${mediaId}-select", null, Selected::class.java)
|
||||||
|
@ -37,26 +34,25 @@ class SubscriptionHelper {
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveSelected(context: Context, mediaId: Int, data: Selected) {
|
private fun saveSelected( mediaId: Int, data: Selected) {
|
||||||
PrefManager.setCustomVal("${mediaId}-select", data)
|
PrefManager.setCustomVal("${mediaId}-select", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAnimeParser(context: Context, isAdult: Boolean, id: Int): AnimeParser {
|
fun getAnimeParser(id: Int): AnimeParser {
|
||||||
val sources = if (isAdult) HAnimeSources else AnimeSources
|
val sources = AnimeSources
|
||||||
val selected = loadSelected(context, id, isAdult, true)
|
Logger.log("getAnimeParser size: ${sources.list.size}")
|
||||||
|
val selected = loadSelected(id)
|
||||||
val parser = sources[selected.sourceIndex]
|
val parser = sources[selected.sourceIndex]
|
||||||
parser.selectDub = selected.preferDub
|
parser.selectDub = selected.preferDub
|
||||||
return parser
|
return parser
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getEpisode(
|
suspend fun getEpisode(
|
||||||
context: Context,
|
|
||||||
parser: AnimeParser,
|
parser: AnimeParser,
|
||||||
id: Int,
|
id: Int
|
||||||
isAdult: Boolean
|
|
||||||
): Episode? {
|
): Episode? {
|
||||||
|
|
||||||
val selected = loadSelected(context, id, isAdult, true)
|
val selected = loadSelected(id)
|
||||||
val ep = withTimeoutOrNull(10 * 1000) {
|
val ep = withTimeoutOrNull(10 * 1000) {
|
||||||
tryWithSuspend {
|
tryWithSuspend {
|
||||||
val show = parser.loadSavedShowResponse(id) ?: throw Exception(
|
val show = parser.loadSavedShowResponse(id) ?: throw Exception(
|
||||||
|
@ -76,23 +72,21 @@ class SubscriptionHelper {
|
||||||
|
|
||||||
return ep?.apply {
|
return ep?.apply {
|
||||||
selected.latest = number.toFloat()
|
selected.latest = number.toFloat()
|
||||||
saveSelected(context, id, selected)
|
saveSelected(id, selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMangaParser(context: Context, isAdult: Boolean, id: Int): MangaParser {
|
fun getMangaParser(id: Int): MangaParser {
|
||||||
val sources = if (isAdult) HMangaSources else MangaSources
|
val sources = MangaSources
|
||||||
val selected = loadSelected(context, id, isAdult, false)
|
val selected = loadSelected(id)
|
||||||
return sources[selected.sourceIndex]
|
return sources[selected.sourceIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getChapter(
|
suspend fun getChapter(
|
||||||
context: Context,
|
|
||||||
parser: MangaParser,
|
parser: MangaParser,
|
||||||
id: Int,
|
id: Int
|
||||||
isAdult: Boolean
|
|
||||||
): MangaChapter? {
|
): MangaChapter? {
|
||||||
val selected = loadSelected(context, id, isAdult, true)
|
val selected = loadSelected(id)
|
||||||
val chp = withTimeoutOrNull(10 * 1000) {
|
val chp = withTimeoutOrNull(10 * 1000) {
|
||||||
tryWithSuspend {
|
tryWithSuspend {
|
||||||
val show = parser.loadSavedShowResponse(id) ?: throw Exception(
|
val show = parser.loadSavedShowResponse(id) ?: throw Exception(
|
||||||
|
@ -112,7 +106,7 @@ class SubscriptionHelper {
|
||||||
|
|
||||||
return chp?.apply {
|
return chp?.apply {
|
||||||
selected.latest = MangaNameAdapter.findChapterNumber(number) ?: 0f
|
selected.latest = MangaNameAdapter.findChapterNumber(number) ?: 0f
|
||||||
saveSelected(context, id, selected)
|
saveSelected(id, selected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,21 +118,21 @@ class SubscriptionHelper {
|
||||||
val image: String?
|
val image: String?
|
||||||
) : java.io.Serializable
|
) : java.io.Serializable
|
||||||
|
|
||||||
private const val subscriptions = "subscriptions"
|
private const val SUBSCRIPTIONS = "subscriptions"
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun getSubscriptions(): Map<Int, SubscribeMedia> =
|
fun getSubscriptions(): Map<Int, SubscribeMedia> =
|
||||||
(PrefManager.getNullableCustomVal(
|
(PrefManager.getNullableCustomVal(
|
||||||
subscriptions,
|
SUBSCRIPTIONS,
|
||||||
null,
|
null,
|
||||||
Map::class.java
|
Map::class.java
|
||||||
) as? Map<Int, SubscribeMedia>)
|
) as? Map<Int, SubscribeMedia>)
|
||||||
?: mapOf<Int, SubscribeMedia>().also { PrefManager.setCustomVal(subscriptions, it) }
|
?: mapOf<Int, SubscribeMedia>().also { PrefManager.setCustomVal(SUBSCRIPTIONS, it) }
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
fun saveSubscription(context: Context, media: Media, subscribed: Boolean) {
|
fun saveSubscription(media: Media, subscribed: Boolean) {
|
||||||
val data = PrefManager.getNullableCustomVal(
|
val data = PrefManager.getNullableCustomVal(
|
||||||
subscriptions,
|
SUBSCRIPTIONS,
|
||||||
null,
|
null,
|
||||||
Map::class.java
|
Map::class.java
|
||||||
) as? MutableMap<Int, SubscribeMedia>
|
) as? MutableMap<Int, SubscribeMedia>
|
||||||
|
@ -157,7 +151,7 @@ class SubscriptionHelper {
|
||||||
} else {
|
} else {
|
||||||
data.remove(media.id)
|
data.remove(media.id)
|
||||||
}
|
}
|
||||||
PrefManager.setCustomVal(subscriptions, data)
|
PrefManager.setCustomVal(SUBSCRIPTIONS, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package ani.dantotsu.notifications.subscription
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import ani.dantotsu.notifications.AlarmManagerScheduler
|
||||||
|
import ani.dantotsu.notifications.TaskScheduler
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
class SubscriptionNotificationReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent?) {
|
||||||
|
Logger.log("SubscriptionNotificationReceiver: onReceive")
|
||||||
|
runBlocking {
|
||||||
|
SubscriptionNotificationTask().execute(context)
|
||||||
|
}
|
||||||
|
val subscriptionInterval =
|
||||||
|
SubscriptionNotificationWorker.checkIntervals[PrefManager.getVal(PrefName.SubscriptionNotificationInterval)]
|
||||||
|
AlarmManagerScheduler(context).scheduleRepeatingTask(
|
||||||
|
TaskScheduler.TaskType.SUBSCRIPTION_NOTIFICATION,
|
||||||
|
subscriptionInterval
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
package ani.dantotsu.notifications.subscription
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import ani.dantotsu.App
|
||||||
|
import ani.dantotsu.FileUrl
|
||||||
|
import ani.dantotsu.R
|
||||||
|
import ani.dantotsu.connections.anilist.UrlMedia
|
||||||
|
import ani.dantotsu.hasNotificationPermission
|
||||||
|
import ani.dantotsu.notifications.Task
|
||||||
|
import ani.dantotsu.parsers.AnimeSources
|
||||||
|
import ani.dantotsu.parsers.Episode
|
||||||
|
import ani.dantotsu.parsers.MangaChapter
|
||||||
|
import ani.dantotsu.parsers.MangaSources
|
||||||
|
import ani.dantotsu.settings.saving.PrefManager
|
||||||
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications.CHANNEL_SUBSCRIPTION_CHECK_PROGRESS
|
||||||
|
import eu.kanade.tachiyomi.data.notification.Notifications.ID_SUBSCRIPTION_CHECK_PROGRESS
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
|
class SubscriptionNotificationTask : Task {
|
||||||
|
private var currentlyPerforming = false
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override suspend fun execute(context: Context): Boolean {
|
||||||
|
if (!currentlyPerforming) {
|
||||||
|
try {
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
PrefManager.init(context)
|
||||||
|
currentlyPerforming = true
|
||||||
|
App.context = context
|
||||||
|
Logger.log("SubscriptionNotificationTask: execute")
|
||||||
|
var timeout = 15_000L
|
||||||
|
do {
|
||||||
|
delay(1000)
|
||||||
|
timeout -= 1000
|
||||||
|
} while (timeout > 0 && !AnimeSources.isInitialized && !MangaSources.isInitialized)
|
||||||
|
Logger.log("SubscriptionNotificationTask: timeout: $timeout")
|
||||||
|
if (timeout <= 0) {
|
||||||
|
currentlyPerforming = false
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
val subscriptions = SubscriptionHelper.getSubscriptions()
|
||||||
|
var i = 0
|
||||||
|
val index = subscriptions.map { i++; it.key to i }.toMap()
|
||||||
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
|
||||||
|
val progressEnabled: Boolean =
|
||||||
|
PrefManager.getVal(PrefName.SubscriptionCheckingNotifications)
|
||||||
|
val progressNotification = if (progressEnabled) getProgressNotification(
|
||||||
|
context,
|
||||||
|
subscriptions.size
|
||||||
|
) else null
|
||||||
|
if (progressNotification != null && hasNotificationPermission(context)) {
|
||||||
|
notificationManager.notify(
|
||||||
|
ID_SUBSCRIPTION_CHECK_PROGRESS,
|
||||||
|
progressNotification.build()
|
||||||
|
)
|
||||||
|
//Seems like if the parent coroutine scope gets cancelled, the notification stays
|
||||||
|
//So adding this as a safeguard? dk if this will be useful
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
delay(5 * subscriptions.size * 1000L)
|
||||||
|
notificationManager.cancel(ID_SUBSCRIPTION_CHECK_PROGRESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun progress(progress: Int, parser: String, media: String) {
|
||||||
|
if (progressNotification != null && hasNotificationPermission(context))
|
||||||
|
notificationManager.notify(
|
||||||
|
ID_SUBSCRIPTION_CHECK_PROGRESS,
|
||||||
|
progressNotification
|
||||||
|
.setProgress(subscriptions.size, progress, false)
|
||||||
|
.setContentText("$media on $parser")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscriptions.toList().map {
|
||||||
|
val media = it.second
|
||||||
|
val text = if (media.isAnime) {
|
||||||
|
val parser =
|
||||||
|
SubscriptionHelper.getAnimeParser(media.id)
|
||||||
|
progress(index[it.first]!!, parser.name, media.name)
|
||||||
|
val ep: Episode? =
|
||||||
|
SubscriptionHelper.getEpisode(
|
||||||
|
parser,
|
||||||
|
media.id
|
||||||
|
)
|
||||||
|
if (ep != null) context.getString(R.string.episode) + "${ep.number}${
|
||||||
|
if (ep.title != null) " : ${ep.title}" else ""
|
||||||
|
}${
|
||||||
|
if (ep.isFiller) " [Filler]" else ""
|
||||||
|
} " + context.getString(R.string.just_released) to ep.thumbnail
|
||||||
|
else null
|
||||||
|
} else {
|
||||||
|
val parser =
|
||||||
|
SubscriptionHelper.getMangaParser(media.id)
|
||||||
|
progress(index[it.first]!!, parser.name, media.name)
|
||||||
|
val ep: MangaChapter? =
|
||||||
|
SubscriptionHelper.getChapter(
|
||||||
|
parser,
|
||||||
|
media.id
|
||||||
|
)
|
||||||
|
if (ep != null) ep.number + " " + context.getString(R.string.just_released) to null
|
||||||
|
else null
|
||||||
|
} ?: return@map
|
||||||
|
val notification = createNotification(
|
||||||
|
context.applicationContext,
|
||||||
|
media,
|
||||||
|
text.first,
|
||||||
|
text.second
|
||||||
|
)
|
||||||
|
if (hasNotificationPermission(context)) {
|
||||||
|
NotificationManagerCompat.from(context)
|
||||||
|
.notify(
|
||||||
|
CHANNEL_SUBSCRIPTION_CHECK,
|
||||||
|
System.currentTimeMillis().toInt(),
|
||||||
|
notification
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (progressNotification != null) notificationManager.cancel(
|
||||||
|
ID_SUBSCRIPTION_CHECK_PROGRESS
|
||||||
|
)
|
||||||
|
currentlyPerforming = false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Logger.log("SubscriptionNotificationTask: ${e.message}")
|
||||||
|
Logger.log(e)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
private fun createNotification(
|
||||||
|
context: Context,
|
||||||
|
media: SubscriptionHelper.Companion.SubscribeMedia,
|
||||||
|
text: String,
|
||||||
|
thumbnail: FileUrl?
|
||||||
|
): android.app.Notification {
|
||||||
|
val pendingIntent = getIntent(context, media.id)
|
||||||
|
val icon =
|
||||||
|
if (media.isAnime) R.drawable.ic_round_movie_filter_24 else R.drawable.ic_round_menu_book_24
|
||||||
|
|
||||||
|
val builder = NotificationCompat.Builder(context, CHANNEL_SUBSCRIPTION_CHECK)
|
||||||
|
.setSmallIcon(icon)
|
||||||
|
.setContentTitle(media.name)
|
||||||
|
.setContentText(text)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
|
||||||
|
if (thumbnail != null) {
|
||||||
|
val bitmap = getBitmapFromUrl(thumbnail.url)
|
||||||
|
if (bitmap != null) {
|
||||||
|
builder.setLargeIcon(bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getProgressNotification(
|
||||||
|
context: Context,
|
||||||
|
size: Int
|
||||||
|
): NotificationCompat.Builder {
|
||||||
|
return NotificationCompat.Builder(context, CHANNEL_SUBSCRIPTION_CHECK_PROGRESS)
|
||||||
|
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.setSmallIcon(R.drawable.notification_icon)
|
||||||
|
.setContentTitle(context.getString(R.string.checking_subscriptions_title))
|
||||||
|
.setProgress(size, 0, false)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setAutoCancel(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBitmapFromUrl(url: String): Bitmap? {
|
||||||
|
return try {
|
||||||
|
val inputStream = java.net.URL(url).openStream()
|
||||||
|
BitmapFactory.decodeStream(inputStream)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun getIntent(context: Context, mediaId: Int): PendingIntent {
|
||||||
|
val notifyIntent = Intent(context, UrlMedia::class.java)
|
||||||
|
.putExtra("media", mediaId)
|
||||||
|
.setAction(mediaId.toString())
|
||||||
|
.apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
}
|
||||||
|
return PendingIntent.getActivity(
|
||||||
|
context, mediaId, notifyIntent,
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_ONE_SHOT
|
||||||
|
} else {
|
||||||
|
PendingIntent.FLAG_ONE_SHOT
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package ani.dantotsu.notifications.subscription
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
import ani.dantotsu.notifications.anilist.AnilistNotificationTask
|
||||||
|
import ani.dantotsu.util.Logger
|
||||||
|
|
||||||
|
class SubscriptionNotificationWorker(appContext: Context, workerParams: WorkerParameters) :
|
||||||
|
CoroutineWorker(appContext, workerParams) {
|
||||||
|
|
||||||
|
override suspend fun doWork(): Result {
|
||||||
|
Logger.log("SubscriptionNotificationWorker: doWork")
|
||||||
|
return if (AnilistNotificationTask().execute(applicationContext)) {
|
||||||
|
Result.success()
|
||||||
|
} else {
|
||||||
|
Logger.log("SubscriptionNotificationWorker: doWork failed")
|
||||||
|
Result.retry()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val checkIntervals = arrayOf(0L, 480, 720, 1440)
|
||||||
|
const val WORK_NAME =
|
||||||
|
"ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker"
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.first
|
||||||
object AnimeSources : WatchSources() {
|
object AnimeSources : WatchSources() {
|
||||||
override var list: List<Lazier<BaseParser>> = emptyList()
|
override var list: List<Lazier<BaseParser>> = emptyList()
|
||||||
var pinnedAnimeSources: List<String> = emptyList()
|
var pinnedAnimeSources: List<String> = emptyList()
|
||||||
|
var isInitialized = false
|
||||||
|
|
||||||
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>) {
|
suspend fun init(fromExtensions: StateFlow<List<AnimeExtension.Installed>>) {
|
||||||
pinnedAnimeSources =
|
pinnedAnimeSources =
|
||||||
|
@ -23,6 +24,7 @@ object AnimeSources : WatchSources() {
|
||||||
{ OfflineAnimeParser() },
|
{ OfflineAnimeParser() },
|
||||||
"Downloaded"
|
"Downloaded"
|
||||||
)
|
)
|
||||||
|
isInitialized = true
|
||||||
|
|
||||||
// Update as StateFlow emits new values
|
// Update as StateFlow emits new values
|
||||||
fromExtensions.collect { extensions ->
|
fromExtensions.collect { extensions ->
|
||||||
|
|
|
@ -11,6 +11,7 @@ import kotlinx.coroutines.flow.first
|
||||||
object MangaSources : MangaReadSources() {
|
object MangaSources : MangaReadSources() {
|
||||||
override var list: List<Lazier<BaseParser>> = emptyList()
|
override var list: List<Lazier<BaseParser>> = emptyList()
|
||||||
var pinnedMangaSources: List<String> = emptyList()
|
var pinnedMangaSources: List<String> = emptyList()
|
||||||
|
var isInitialized = false
|
||||||
|
|
||||||
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
|
suspend fun init(fromExtensions: StateFlow<List<MangaExtension.Installed>>) {
|
||||||
pinnedMangaSources =
|
pinnedMangaSources =
|
||||||
|
@ -23,6 +24,7 @@ object MangaSources : MangaReadSources() {
|
||||||
{ OfflineMangaParser() },
|
{ OfflineMangaParser() },
|
||||||
"Downloaded"
|
"Downloaded"
|
||||||
)
|
)
|
||||||
|
isInitialized = true
|
||||||
|
|
||||||
// Update as StateFlow emits new values
|
// Update as StateFlow emits new values
|
||||||
fromExtensions.collect { extensions ->
|
fromExtensions.collect { extensions ->
|
||||||
|
|
|
@ -6,7 +6,6 @@ import android.app.AlertDialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.drawable.Animatable
|
import android.graphics.drawable.Animatable
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Build.BRAND
|
import android.os.Build.BRAND
|
||||||
import android.os.Build.DEVICE
|
import android.os.Build.DEVICE
|
||||||
|
@ -54,7 +53,9 @@ import ani.dantotsu.navBarHeight
|
||||||
import ani.dantotsu.notifications.TaskScheduler
|
import ani.dantotsu.notifications.TaskScheduler
|
||||||
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
import ani.dantotsu.notifications.comment.CommentNotificationWorker
|
||||||
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
import ani.dantotsu.notifications.anilist.AnilistNotificationWorker
|
||||||
|
import ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker.Companion.checkIntervals
|
||||||
import ani.dantotsu.openLinkInBrowser
|
import ani.dantotsu.openLinkInBrowser
|
||||||
|
import ani.dantotsu.openSettings
|
||||||
import ani.dantotsu.others.AppUpdater
|
import ani.dantotsu.others.AppUpdater
|
||||||
import ani.dantotsu.others.CustomBottomDialog
|
import ani.dantotsu.others.CustomBottomDialog
|
||||||
import ani.dantotsu.pop
|
import ani.dantotsu.pop
|
||||||
|
@ -68,11 +69,6 @@ import ani.dantotsu.settings.saving.internal.PreferencePackager
|
||||||
import ani.dantotsu.snackString
|
import ani.dantotsu.snackString
|
||||||
import ani.dantotsu.startMainActivity
|
import ani.dantotsu.startMainActivity
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.subcriptions.Notifications
|
|
||||||
import ani.dantotsu.subcriptions.Notifications.Companion.openSettings
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes
|
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import ani.dantotsu.toast
|
import ani.dantotsu.toast
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
@ -652,8 +648,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var curTime = PrefManager.getVal(PrefName.SubscriptionsTimeS, defaultTime)
|
var curTime = PrefManager.getVal<Int>(PrefName.SubscriptionNotificationInterval)
|
||||||
val timeNames = timeMinutes.map {
|
val timeNames = checkIntervals.map {
|
||||||
val mins = it % 60
|
val mins = it % 60
|
||||||
val hours = it / 60
|
val hours = it / 60
|
||||||
if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}"
|
if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}"
|
||||||
|
@ -668,15 +664,19 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
curTime = i
|
curTime = i
|
||||||
binding.settingsSubscriptionsTime.text =
|
binding.settingsSubscriptionsTime.text =
|
||||||
getString(R.string.subscriptions_checking_time_s, timeNames[i])
|
getString(R.string.subscriptions_checking_time_s, timeNames[i])
|
||||||
PrefManager.setVal(PrefName.SubscriptionsTimeS, curTime)
|
PrefManager.setVal(PrefName.SubscriptionNotificationInterval, curTime)
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
startSubscription(true)
|
TaskScheduler.create(this,
|
||||||
|
PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
|
).scheduleAllTasks(this)
|
||||||
}.show()
|
}.show()
|
||||||
dialog.window?.setDimAmount(0.8f)
|
dialog.window?.setDimAmount(0.8f)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.settingsSubscriptionsTime.setOnLongClickListener {
|
binding.settingsSubscriptionsTime.setOnLongClickListener {
|
||||||
startSubscription(true)
|
TaskScheduler.create(this,
|
||||||
|
PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
|
).scheduleAllTasks(this)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -699,6 +699,9 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
binding.settingsAnilistSubscriptionsTime.text =
|
binding.settingsAnilistSubscriptionsTime.text =
|
||||||
getString(R.string.anilist_notifications_checking_time, aItems[i])
|
getString(R.string.anilist_notifications_checking_time, aItems[i])
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
TaskScheduler.create(this,
|
||||||
|
PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
|
).scheduleAllTasks(this)
|
||||||
}
|
}
|
||||||
.create()
|
.create()
|
||||||
dialog.window?.setDimAmount(0.8f)
|
dialog.window?.setDimAmount(0.8f)
|
||||||
|
@ -743,6 +746,9 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
binding.settingsCommentSubscriptionsTime.text =
|
binding.settingsCommentSubscriptionsTime.text =
|
||||||
getString(R.string.comment_notification_checking_time, cItems[i])
|
getString(R.string.comment_notification_checking_time, cItems[i])
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
TaskScheduler.create(this,
|
||||||
|
PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
|
).scheduleAllTasks(this)
|
||||||
}
|
}
|
||||||
.create()
|
.create()
|
||||||
dialog.window?.setDimAmount(0.8f)
|
dialog.window?.setDimAmount(0.8f)
|
||||||
|
@ -753,16 +759,6 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
|
||||||
PrefManager.getVal(PrefName.SubscriptionCheckingNotifications)
|
PrefManager.getVal(PrefName.SubscriptionCheckingNotifications)
|
||||||
binding.settingsNotificationsCheckingSubscriptions.setOnCheckedChangeListener { _, isChecked ->
|
binding.settingsNotificationsCheckingSubscriptions.setOnCheckedChangeListener { _, isChecked ->
|
||||||
PrefManager.setVal(PrefName.SubscriptionCheckingNotifications, isChecked)
|
PrefManager.setVal(PrefName.SubscriptionCheckingNotifications, isChecked)
|
||||||
if (isChecked)
|
|
||||||
Notifications.createChannel(
|
|
||||||
this,
|
|
||||||
null,
|
|
||||||
"subscription_checking",
|
|
||||||
getString(R.string.checking_subscriptions),
|
|
||||||
false
|
|
||||||
)
|
|
||||||
else
|
|
||||||
Notifications.deleteChannel(this, "subscription_checking")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.settingsNotificationsCheckingSubscriptions.setOnLongClickListener {
|
binding.settingsNotificationsCheckingSubscriptions.setOnLongClickListener {
|
||||||
|
|
|
@ -17,7 +17,6 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
|
||||||
ContinueMedia(Pref(Location.General, Boolean::class, true)),
|
ContinueMedia(Pref(Location.General, Boolean::class, true)),
|
||||||
RecentlyListOnly(Pref(Location.General, Boolean::class, false)),
|
RecentlyListOnly(Pref(Location.General, Boolean::class, false)),
|
||||||
SettingsPreferDub(Pref(Location.General, Boolean::class, false)),
|
SettingsPreferDub(Pref(Location.General, Boolean::class, false)),
|
||||||
SubscriptionsTimeS(Pref(Location.General, Int::class, 0)),
|
|
||||||
SubscriptionCheckingNotifications(Pref(Location.General, Boolean::class, true)),
|
SubscriptionCheckingNotifications(Pref(Location.General, Boolean::class, true)),
|
||||||
CheckUpdate(Pref(Location.General, Boolean::class, true)),
|
CheckUpdate(Pref(Location.General, Boolean::class, true)),
|
||||||
VerboseLogging(Pref(Location.General, Boolean::class, false)),
|
VerboseLogging(Pref(Location.General, Boolean::class, false)),
|
||||||
|
@ -36,6 +35,7 @@ enum class PrefName(val data: Pref) { //TODO: Split this into multiple files
|
||||||
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
NovelSourcesOrder(Pref(Location.General, List::class, listOf<String>())),
|
||||||
CommentNotificationInterval(Pref(Location.General, Int::class, 0)),
|
CommentNotificationInterval(Pref(Location.General, Int::class, 0)),
|
||||||
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),
|
AnilistNotificationInterval(Pref(Location.General, Int::class, 3)),
|
||||||
|
SubscriptionNotificationInterval(Pref(Location.General, Int::class, 2)),
|
||||||
LastAnilistNotificationId(Pref(Location.General, Int::class, 0)),
|
LastAnilistNotificationId(Pref(Location.General, Int::class, 0)),
|
||||||
AnilistFilteredTypes(Pref(Location.General, Set::class, setOf<String>())),
|
AnilistFilteredTypes(Pref(Location.General, Set::class, setOf<String>())),
|
||||||
UseAlarmManager(Pref(Location.General, Boolean::class, false)),
|
UseAlarmManager(Pref(Location.General, Boolean::class, false)),
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
package ani.dantotsu.subcriptions
|
|
||||||
|
|
||||||
import android.app.AlarmManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import ani.dantotsu.currContext
|
|
||||||
import ani.dantotsu.isOnline
|
|
||||||
import ani.dantotsu.util.Logger
|
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes
|
|
||||||
import ani.dantotsu.tryWith
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
class AlarmReceiver : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
|
||||||
when (intent?.action) {
|
|
||||||
Intent.ACTION_BOOT_COMPLETED -> tryWith(true) {
|
|
||||||
Logger.log("Starting Dantotsu Subscription Service on Boot")
|
|
||||||
context?.startSubscription()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val con = context ?: currContext() ?: return@launch
|
|
||||||
if (isOnline(con)) Subscription.perform(con)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun alarm(context: Context) {
|
|
||||||
val alarmIntent = Intent(context, AlarmReceiver::class.java)
|
|
||||||
alarmIntent.action = "ani.dantotsu.ACTION_ALARM"
|
|
||||||
|
|
||||||
val pendingIntent = PendingIntent.getBroadcast(
|
|
||||||
context, 0, alarmIntent,
|
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
|
||||||
)
|
|
||||||
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
||||||
val curTime = PrefManager.getVal(PrefName.SubscriptionsTimeS, defaultTime)
|
|
||||||
|
|
||||||
if (timeMinutes[curTime] > 0)
|
|
||||||
alarmManager.setRepeating(
|
|
||||||
AlarmManager.RTC,
|
|
||||||
System.currentTimeMillis(),
|
|
||||||
(timeMinutes[curTime] * 60 * 1000),
|
|
||||||
pendingIntent
|
|
||||||
)
|
|
||||||
else alarmManager.cancel(pendingIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,176 +0,0 @@
|
||||||
package ani.dantotsu.subcriptions
|
|
||||||
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationChannelGroup
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Context.NOTIFICATION_SERVICE
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.provider.Settings
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import ani.dantotsu.FileUrl
|
|
||||||
import ani.dantotsu.R
|
|
||||||
import ani.dantotsu.connections.anilist.UrlMedia
|
|
||||||
import com.bumptech.glide.Glide
|
|
||||||
import com.bumptech.glide.load.model.GlideUrl
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
@Suppress("MemberVisibilityCanBePrivate", "unused")
|
|
||||||
class Notifications {
|
|
||||||
enum class Group(val title: String, val icon: Int) {
|
|
||||||
ANIME_GROUP("New Episodes", R.drawable.ic_round_movie_filter_24),
|
|
||||||
MANGA_GROUP("New Chapters", R.drawable.ic_round_menu_book_24)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun openSettings(context: Context, channelId: String?): Boolean {
|
|
||||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val intent = Intent(
|
|
||||||
if (channelId != null) Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS
|
|
||||||
else Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
|
||||||
).apply {
|
|
||||||
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
|
||||||
putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
|
|
||||||
}
|
|
||||||
context.startActivity(intent)
|
|
||||||
true
|
|
||||||
} else false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getIntent(context: Context, mediaId: Int): PendingIntent {
|
|
||||||
val notifyIntent = Intent(context, UrlMedia::class.java)
|
|
||||||
.putExtra("media", mediaId)
|
|
||||||
.setAction(mediaId.toString())
|
|
||||||
.apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
}
|
|
||||||
return PendingIntent.getActivity(
|
|
||||||
context, 0, notifyIntent,
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_ONE_SHOT
|
|
||||||
} else {
|
|
||||||
PendingIntent.FLAG_ONE_SHOT
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun createChannel(
|
|
||||||
context: Context,
|
|
||||||
group: Group?,
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
silent: Boolean = false
|
|
||||||
) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val importance =
|
|
||||||
if (!silent) NotificationManager.IMPORTANCE_HIGH else NotificationManager.IMPORTANCE_LOW
|
|
||||||
val mChannel = NotificationChannel(id, name, importance)
|
|
||||||
|
|
||||||
val notificationManager =
|
|
||||||
context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
|
|
||||||
if (group != null) {
|
|
||||||
notificationManager.createNotificationChannelGroup(
|
|
||||||
NotificationChannelGroup(
|
|
||||||
group.name,
|
|
||||||
group.title
|
|
||||||
)
|
|
||||||
)
|
|
||||||
mChannel.group = group.name
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.createNotificationChannel(mChannel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deleteChannel(context: Context, id: String) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
val notificationManager =
|
|
||||||
context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
|
||||||
notificationManager.deleteNotificationChannel(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getNotification(
|
|
||||||
context: Context,
|
|
||||||
group: Group?,
|
|
||||||
channelId: String,
|
|
||||||
title: String,
|
|
||||||
text: String?,
|
|
||||||
silent: Boolean = false
|
|
||||||
): NotificationCompat.Builder {
|
|
||||||
createChannel(context, group, channelId, title, silent)
|
|
||||||
return NotificationCompat.Builder(context, channelId)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
.setSmallIcon(group?.icon ?: R.drawable.monochrome)
|
|
||||||
.setContentTitle(title)
|
|
||||||
.setContentText(text)
|
|
||||||
.setAutoCancel(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getNotification(
|
|
||||||
context: Context,
|
|
||||||
group: Group?,
|
|
||||||
channelId: String,
|
|
||||||
title: String,
|
|
||||||
text: String,
|
|
||||||
img: FileUrl?,
|
|
||||||
silent: Boolean = false,
|
|
||||||
largeImg: FileUrl?
|
|
||||||
): NotificationCompat.Builder {
|
|
||||||
val builder = getNotification(context, group, channelId, title, text, silent)
|
|
||||||
return if (img != null) {
|
|
||||||
val bitmap = withContext(Dispatchers.IO) {
|
|
||||||
Glide.with(context)
|
|
||||||
.asBitmap()
|
|
||||||
.load(GlideUrl(img.url) { img.headers })
|
|
||||||
.submit()
|
|
||||||
.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("BlockingMethodInNonBlockingContext")
|
|
||||||
val largeBitmap = if (largeImg != null) Glide.with(context)
|
|
||||||
.asBitmap()
|
|
||||||
.load(GlideUrl(largeImg.url) { largeImg.headers })
|
|
||||||
.submit()
|
|
||||||
.get()
|
|
||||||
else null
|
|
||||||
|
|
||||||
if (largeBitmap != null) builder.setStyle(
|
|
||||||
NotificationCompat
|
|
||||||
.BigPictureStyle()
|
|
||||||
.bigPicture(largeBitmap)
|
|
||||||
.bigLargeIcon(bitmap)
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.setLargeIcon(bitmap)
|
|
||||||
} else builder
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getNotification(
|
|
||||||
context: Context,
|
|
||||||
group: Group?,
|
|
||||||
channelId: String,
|
|
||||||
title: String,
|
|
||||||
text: String,
|
|
||||||
img: String? = null,
|
|
||||||
silent: Boolean = false,
|
|
||||||
largeImg: FileUrl? = null
|
|
||||||
): NotificationCompat.Builder {
|
|
||||||
return getNotification(
|
|
||||||
context,
|
|
||||||
group,
|
|
||||||
channelId,
|
|
||||||
title,
|
|
||||||
text,
|
|
||||||
if (img != null) FileUrl(img) else null,
|
|
||||||
silent,
|
|
||||||
largeImg
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,147 +0,0 @@
|
||||||
package ani.dantotsu.subcriptions
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.Notification
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import ani.dantotsu.*
|
|
||||||
import ani.dantotsu.parsers.Episode
|
|
||||||
import ani.dantotsu.parsers.MangaChapter
|
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
|
||||||
import ani.dantotsu.util.Logger
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
class Subscription {
|
|
||||||
companion object {
|
|
||||||
const val defaultTime = 1
|
|
||||||
val timeMinutes = arrayOf(0L, 720, 1440)
|
|
||||||
|
|
||||||
private var alreadyStarted = false
|
|
||||||
fun Context.startSubscription(force: Boolean = false) {
|
|
||||||
if (!alreadyStarted || force) {
|
|
||||||
alreadyStarted = true
|
|
||||||
SubscriptionWorker.enqueue(this)
|
|
||||||
AlarmReceiver.alarm(this)
|
|
||||||
} else Logger.log("Already Subscribed")
|
|
||||||
}
|
|
||||||
|
|
||||||
private var currentlyPerforming = false
|
|
||||||
|
|
||||||
suspend fun perform(context: Context) {
|
|
||||||
if (!currentlyPerforming) tryWithSuspend {
|
|
||||||
currentlyPerforming = true
|
|
||||||
App.context = context
|
|
||||||
|
|
||||||
val subscriptions = SubscriptionHelper.getSubscriptions()
|
|
||||||
var i = 0
|
|
||||||
val index = subscriptions.map { i++; it.key to i }.toMap()
|
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
|
||||||
|
|
||||||
val progressEnabled: Boolean =
|
|
||||||
PrefManager.getVal(PrefName.SubscriptionCheckingNotifications)
|
|
||||||
val progressNotification = if (progressEnabled) getProgressNotification(
|
|
||||||
context,
|
|
||||||
subscriptions.size
|
|
||||||
) else null
|
|
||||||
if (progressNotification != null) {
|
|
||||||
notificationManager.notify(progressNotificationId, progressNotification.build())
|
|
||||||
//Seems like if the parent coroutine scope gets cancelled, the notification stays
|
|
||||||
//So adding this as a safeguard? dk if this will be useful
|
|
||||||
CoroutineScope(Dispatchers.Main).launch {
|
|
||||||
delay(5 * subscriptions.size * 1000L)
|
|
||||||
notificationManager.cancel(progressNotificationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun progress(progress: Int, parser: String, media: String) {
|
|
||||||
if (progressNotification != null)
|
|
||||||
notificationManager.notify(
|
|
||||||
progressNotificationId,
|
|
||||||
progressNotification
|
|
||||||
.setProgress(subscriptions.size, progress, false)
|
|
||||||
.setContentText("$media on $parser")
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptions.toList().map {
|
|
||||||
val media = it.second
|
|
||||||
val text = if (media.isAnime) {
|
|
||||||
val parser =
|
|
||||||
SubscriptionHelper.getAnimeParser(context, media.isAdult, media.id)
|
|
||||||
progress(index[it.first]!!, parser.name, media.name)
|
|
||||||
val ep: Episode? =
|
|
||||||
SubscriptionHelper.getEpisode(context, parser, media.id, media.isAdult)
|
|
||||||
if (ep != null) currActivity()!!.getString(R.string.episode) + "${ep.number}${
|
|
||||||
if (ep.title != null) " : ${ep.title}" else ""
|
|
||||||
}${
|
|
||||||
if (ep.isFiller) " [Filler]" else ""
|
|
||||||
} " + currActivity()!!.getString(R.string.just_released) to ep.thumbnail
|
|
||||||
else null
|
|
||||||
} else {
|
|
||||||
val parser =
|
|
||||||
SubscriptionHelper.getMangaParser(context, media.isAdult, media.id)
|
|
||||||
progress(index[it.first]!!, parser.name, media.name)
|
|
||||||
val ep: MangaChapter? =
|
|
||||||
SubscriptionHelper.getChapter(context, parser, media.id, media.isAdult)
|
|
||||||
if (ep != null) ep.number + " " + currActivity()!!.getString(R.string.just_released) to null
|
|
||||||
else null
|
|
||||||
} ?: return@map
|
|
||||||
createNotification(context.applicationContext, media, text.first, text.second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progressNotification != null) notificationManager.cancel(progressNotificationId)
|
|
||||||
currentlyPerforming = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getChannelId(isAnime: Boolean, mediaId: Int) =
|
|
||||||
"${if (isAnime) "anime" else "manga"}_${mediaId}"
|
|
||||||
|
|
||||||
private suspend fun createNotification(
|
|
||||||
context: Context,
|
|
||||||
media: SubscriptionHelper.Companion.SubscribeMedia,
|
|
||||||
text: String,
|
|
||||||
thumbnail: FileUrl?
|
|
||||||
) {
|
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
|
||||||
|
|
||||||
val notification = Notifications.getNotification(
|
|
||||||
context,
|
|
||||||
if (media.isAnime) Notifications.Group.ANIME_GROUP else Notifications.Group.MANGA_GROUP,
|
|
||||||
getChannelId(media.isAnime, media.id),
|
|
||||||
media.name,
|
|
||||||
text,
|
|
||||||
media.image,
|
|
||||||
false,
|
|
||||||
thumbnail
|
|
||||||
).setContentIntent(Notifications.getIntent(context, media.id)).build()
|
|
||||||
|
|
||||||
notification.flags = Notification.FLAG_AUTO_CANCEL
|
|
||||||
//+100 to have extra ids for other notifications?
|
|
||||||
notificationManager.notify(100 + media.id, notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val progressNotificationId = 100
|
|
||||||
|
|
||||||
private fun getProgressNotification(
|
|
||||||
context: Context,
|
|
||||||
size: Int
|
|
||||||
): NotificationCompat.Builder {
|
|
||||||
return Notifications.getNotification(
|
|
||||||
context,
|
|
||||||
null,
|
|
||||||
"subscription_checking",
|
|
||||||
currContext()!!.getString(R.string.checking_subscriptions_title),
|
|
||||||
null,
|
|
||||||
true
|
|
||||||
).setOngoing(true).setProgress(size, 0, false).setAutoCancel(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
package ani.dantotsu.subcriptions
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.work.Constraints
|
|
||||||
import androidx.work.CoroutineWorker
|
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
|
||||||
import androidx.work.NetworkType
|
|
||||||
import androidx.work.PeriodicWorkRequest
|
|
||||||
import androidx.work.WorkManager
|
|
||||||
import androidx.work.WorkerParameters
|
|
||||||
import ani.dantotsu.settings.saving.PrefManager
|
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime
|
|
||||||
import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
class SubscriptionWorker(val context: Context, params: WorkerParameters) :
|
|
||||||
CoroutineWorker(context, params) {
|
|
||||||
|
|
||||||
override suspend fun doWork(): Result {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
Subscription.perform(context)
|
|
||||||
}
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val SUBSCRIPTION_WORK_NAME = "work_subscription"
|
|
||||||
fun enqueue(context: Context) {
|
|
||||||
val curTime = PrefManager.getVal(PrefName.SubscriptionsTimeS, defaultTime)
|
|
||||||
if (timeMinutes[curTime] > 0L) {
|
|
||||||
val constraints =
|
|
||||||
Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
|
|
||||||
val periodicSyncDataWork = PeriodicWorkRequest.Builder(
|
|
||||||
SubscriptionWorker::class.java, 6, TimeUnit.HOURS
|
|
||||||
).apply {
|
|
||||||
addTag(SUBSCRIPTION_WORK_NAME)
|
|
||||||
setConstraints(constraints)
|
|
||||||
}.build()
|
|
||||||
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
|
|
||||||
SUBSCRIPTION_WORK_NAME,
|
|
||||||
ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE,
|
|
||||||
periodicSyncDataWork
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -86,7 +86,7 @@ object Logger {
|
||||||
fun log(message: String) {
|
fun log(message: String) {
|
||||||
val trace = Thread.currentThread().stackTrace[3]
|
val trace = Thread.currentThread().stackTrace[3]
|
||||||
loggerExecutor.execute {
|
loggerExecutor.execute {
|
||||||
if (file == null) Log.d("Internal Logger", "$message)")
|
if (file == null) Log.d("Internal Logger", message)
|
||||||
else {
|
else {
|
||||||
val className = trace.className
|
val className = trace.className
|
||||||
val methodName = trace.methodName
|
val methodName = trace.methodName
|
||||||
|
|
|
@ -63,6 +63,15 @@ object Notifications {
|
||||||
const val CHANNEL_ANILIST = "anilist_channel"
|
const val CHANNEL_ANILIST = "anilist_channel"
|
||||||
const val ID_ANILIST = -901
|
const val ID_ANILIST = -901
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notification channel and ids used subscription checks.
|
||||||
|
*/
|
||||||
|
const val GROUP_SUBSCRIPTION_CHECK = "group_subscription_check"
|
||||||
|
const val CHANNEL_SUBSCRIPTION_CHECK = "subscription_check_channel"
|
||||||
|
const val CHANNEL_SUBSCRIPTION_CHECK_PROGRESS = "subscription_check_progress_channel"
|
||||||
|
const val ID_SUBSCRIPTION_CHECK = -1001
|
||||||
|
const val ID_SUBSCRIPTION_CHECK_PROGRESS = -1002
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notification channel and ids used for app and extension updates.
|
* Notification channel and ids used for app and extension updates.
|
||||||
|
@ -118,6 +127,9 @@ object Notifications {
|
||||||
buildNotificationChannelGroup(GROUP_ANILIST) {
|
buildNotificationChannelGroup(GROUP_ANILIST) {
|
||||||
setName("Anilist")
|
setName("Anilist")
|
||||||
},
|
},
|
||||||
|
buildNotificationChannelGroup(GROUP_SUBSCRIPTION_CHECK) {
|
||||||
|
setName("Subscription Checks")
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -154,6 +166,14 @@ object Notifications {
|
||||||
setName("Anilist")
|
setName("Anilist")
|
||||||
setGroup(GROUP_ANILIST)
|
setGroup(GROUP_ANILIST)
|
||||||
},
|
},
|
||||||
|
buildNotificationChannel(CHANNEL_SUBSCRIPTION_CHECK, IMPORTANCE_LOW) {
|
||||||
|
setName("Subscription Checks")
|
||||||
|
setGroup(GROUP_SUBSCRIPTION_CHECK)
|
||||||
|
},
|
||||||
|
buildNotificationChannel(CHANNEL_SUBSCRIPTION_CHECK_PROGRESS, IMPORTANCE_LOW) {
|
||||||
|
setName("Subscription Checks Progress")
|
||||||
|
setGroup(GROUP_SUBSCRIPTION_CHECK)
|
||||||
|
},
|
||||||
buildNotificationChannel(CHANNEL_APP_GLOBAL, IMPORTANCE_HIGH) {
|
buildNotificationChannel(CHANNEL_APP_GLOBAL, IMPORTANCE_HIGH) {
|
||||||
setName("Global Updates")
|
setName("Global Updates")
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue