feat: view subscriptions in settings

This commit is contained in:
rebelonion 2024-05-17 08:57:59 -05:00
parent f1d16ba16a
commit 6c1176a182
14 changed files with 221 additions and 15 deletions

View file

@ -143,6 +143,8 @@ data class FileUrl(
operator fun get(url: String?, headers: Map<String, String> = mapOf()): FileUrl? { operator fun get(url: String?, headers: Map<String, String> = mapOf()): FileUrl? {
return FileUrl(url ?: return null, headers) return FileUrl(url ?: return null, headers)
} }
private const val serialVersionUID = 1L
} }
} }

View file

@ -294,6 +294,7 @@ class OfflineAnimeFragment : Fragment(), OfflineAnimeSearchListener {
val tDownloads = downloadManager.animeDownloadedTypes.filter { it.titleName.findValidName() == title } val tDownloads = downloadManager.animeDownloadedTypes.filter { it.titleName.findValidName() == title }
val download = tDownloads.firstOrNull() ?: continue val download = tDownloads.firstOrNull() ?: continue
val offlineAnimeModel = loadOfflineAnimeModel(download) val offlineAnimeModel = loadOfflineAnimeModel(download)
if (offlineAnimeModel.title == "unknown") offlineAnimeModel.title = title
newAnimeDownloads += offlineAnimeModel newAnimeDownloads += offlineAnimeModel
} }
downloads = newAnimeDownloads downloads = newAnimeDownloads

View file

@ -3,7 +3,7 @@ package ani.dantotsu.download.anime
import android.net.Uri import android.net.Uri
data class OfflineAnimeModel( data class OfflineAnimeModel(
val title: String, var title: String,
val score: String, val score: String,
val totalEpisode: String, val totalEpisode: String,
val totalEpisodeList: String, val totalEpisodeList: String,

View file

@ -5,14 +5,18 @@ import ani.dantotsu.currContext
import ani.dantotsu.media.Media import ani.dantotsu.media.Media
import ani.dantotsu.media.MediaNameAdapter import ani.dantotsu.media.MediaNameAdapter
import ani.dantotsu.media.Selected import ani.dantotsu.media.Selected
import ani.dantotsu.media.emptyMedia
import ani.dantotsu.parsers.AnimeParser import ani.dantotsu.parsers.AnimeParser
import ani.dantotsu.parsers.AnimeSources import ani.dantotsu.parsers.AnimeSources
import ani.dantotsu.parsers.BaseParser
import ani.dantotsu.parsers.Episode import ani.dantotsu.parsers.Episode
import ani.dantotsu.parsers.MangaChapter import ani.dantotsu.parsers.MangaChapter
import ani.dantotsu.parsers.MangaParser import ani.dantotsu.parsers.MangaParser
import ani.dantotsu.parsers.MangaSources import ani.dantotsu.parsers.MangaSources
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.toast
import ani.dantotsu.tryWithSuspend import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger import ani.dantotsu.util.Logger
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
@ -50,16 +54,18 @@ class SubscriptionHelper {
suspend fun getEpisode( suspend fun getEpisode(
parser: AnimeParser, parser: AnimeParser,
id: Int subscribeMedia: SubscribeMedia
): Episode? { ): Episode? {
val selected = loadSelected(id) val selected = loadSelected(subscribeMedia.id)
val ep = withTimeoutOrNull(10 * 1000) { val ep = withTimeoutOrNull(10 * 1000) {
tryWithSuspend { tryWithSuspend {
val show = parser.loadSavedShowResponse(id) ?: throw Exception( val show = parser.loadSavedShowResponse(subscribeMedia.id)
?: forceLoadShowResponse(subscribeMedia, selected, parser)
?: throw Exception(
currContext()?.getString( currContext()?.getString(
R.string.failed_to_load_data, R.string.failed_to_load_data,
id subscribeMedia.id
) )
) )
show.sAnime?.let { show.sAnime?.let {
@ -73,7 +79,7 @@ class SubscriptionHelper {
return ep?.apply { return ep?.apply {
selected.latest = number.toFloat() selected.latest = number.toFloat()
saveSelected(id, selected) saveSelected(subscribeMedia.id, selected)
} }
} }
@ -89,15 +95,17 @@ class SubscriptionHelper {
suspend fun getChapter( suspend fun getChapter(
parser: MangaParser, parser: MangaParser,
id: Int subscribeMedia: SubscribeMedia
): MangaChapter? { ): MangaChapter? {
val selected = loadSelected(id) val selected = loadSelected(subscribeMedia.id)
val chp = withTimeoutOrNull(10 * 1000) { val chp = withTimeoutOrNull(10 * 1000) {
tryWithSuspend { tryWithSuspend {
val show = parser.loadSavedShowResponse(id) ?: throw Exception( val show = parser.loadSavedShowResponse(subscribeMedia.id)
?: forceLoadShowResponse(subscribeMedia, selected, parser)
?: throw Exception(
currContext()?.getString( currContext()?.getString(
R.string.failed_to_load_data, R.string.failed_to_load_data,
id subscribeMedia.id
) )
) )
show.sManga?.let { show.sManga?.let {
@ -111,10 +119,28 @@ class SubscriptionHelper {
return chp?.apply { return chp?.apply {
selected.latest = MediaNameAdapter.findChapterNumber(number) ?: 0f selected.latest = MediaNameAdapter.findChapterNumber(number) ?: 0f
saveSelected(id, selected) saveSelected(subscribeMedia.id, selected)
} }
} }
private suspend fun forceLoadShowResponse(subscribeMedia: SubscribeMedia, selected: Selected, parser: BaseParser): ShowResponse? {
val tempMedia = Media(
id = subscribeMedia.id,
name = null,
nameRomaji = subscribeMedia.name,
userPreferredName = subscribeMedia.name,
isAdult = subscribeMedia.isAdult,
isFav = false,
isListPrivate = false,
userScore = 0,
userRepeat = 0,
format = null,
selected = selected
)
parser.autoSearch(tempMedia)
return parser.loadSavedShowResponse(subscribeMedia.id)
}
data class SubscribeMedia( data class SubscribeMedia(
val isAnime: Boolean, val isAnime: Boolean,
val isAdult: Boolean, val isAdult: Boolean,
@ -134,6 +160,19 @@ class SubscriptionHelper {
) 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")
fun deleteSubscription(id: Int, showSnack: Boolean = false) {
val data = PrefManager.getNullableCustomVal(
SUBSCRIPTIONS,
null,
Map::class.java
) as? MutableMap<Int, SubscribeMedia>
?: mutableMapOf()
data.remove(id)
PrefManager.setCustomVal(SUBSCRIPTIONS, data)
if (showSnack) toast(R.string.subscription_deleted)
}
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun saveSubscription(media: Media, subscribed: Boolean) { fun saveSubscription(media: Media, subscribed: Boolean) {
val data = PrefManager.getNullableCustomVal( val data = PrefManager.getNullableCustomVal(

View file

@ -98,7 +98,7 @@ class SubscriptionNotificationTask : Task {
val ep: Episode? = val ep: Episode? =
SubscriptionHelper.getEpisode( SubscriptionHelper.getEpisode(
parser, parser,
media.id media
) )
if (ep != null) context.getString(R.string.episode) + "${ep.number}${ if (ep != null) context.getString(R.string.episode) + "${ep.number}${
if (ep.title != null) " : ${ep.title}" else "" if (ep.title != null) " : ${ep.title}" else ""
@ -113,7 +113,7 @@ class SubscriptionNotificationTask : Task {
val ep: MangaChapter? = val ep: MangaChapter? =
SubscriptionHelper.getChapter( SubscriptionHelper.getChapter(
parser, parser,
media.id media
) )
if (ep != null) ep.number + " " + context.getString(R.string.just_released) to null if (ep != null) ep.number + " " + context.getString(R.string.just_released) to null
else null else null

View file

@ -18,6 +18,7 @@ import ani.dantotsu.navBarHeight
import ani.dantotsu.notifications.TaskScheduler import ani.dantotsu.notifications.TaskScheduler
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.SubscriptionHelper
import ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker import ani.dantotsu.notifications.subscription.SubscriptionNotificationWorker
import ani.dantotsu.openSettings import ani.dantotsu.openSettings
import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefManager
@ -102,6 +103,19 @@ class SettingsNotificationActivity : AppCompatActivity() {
).scheduleAllTasks(context) ).scheduleAllTasks(context)
} }
), ),
Settings(
type = 1,
name = getString(R.string.view_subscriptions),
desc = getString(R.string.view_subscriptions_desc),
icon = R.drawable.ic_round_search_24,
onClick = {
val subscriptions = SubscriptionHelper.getSubscriptions()
SubscriptionsBottomDialog.newInstance(subscriptions).show(
supportFragmentManager,
"subscriptions"
)
}
),
Settings( Settings(
type = 1, type = 1,
name = getString(R.string.anilist_notification_filters), name = getString(R.string.anilist_notification_filters),

View file

@ -0,0 +1,52 @@
package ani.dantotsu.settings
import android.content.Intent
import android.view.View
import androidx.core.content.ContextCompat
import ani.dantotsu.R
import ani.dantotsu.databinding.ItemSubscriptionBinding
import ani.dantotsu.media.MediaDetailsActivity
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import com.xwray.groupie.GroupieAdapter
import com.xwray.groupie.Item
import com.xwray.groupie.viewbinding.BindableItem
class SubscriptionItem(
val id: Int,
private val media: SubscriptionHelper.Companion.SubscribeMedia,
private val adapter: GroupieAdapter
) : BindableItem<ItemSubscriptionBinding>() {
private lateinit var binding: ItemSubscriptionBinding
override fun bind(p0: ItemSubscriptionBinding, p1: Int) {
val context = p0.root.context
binding = p0
val parserName = if (media.isAnime)
SubscriptionHelper.getAnimeParser(media.id).name
else
SubscriptionHelper.getMangaParser(media.id).name
val mediaName = media.name
val showName = "$mediaName - $parserName"
binding.subscriptionName.text = showName
binding.root.setOnClickListener {
ContextCompat.startActivity(
context,
Intent(context, MediaDetailsActivity::class.java).putExtra(
"mediaId", media.id
),
null
)
}
binding.deleteSubscription.setOnClickListener {
SubscriptionHelper.deleteSubscription(id, true)
adapter.remove(this)
}
}
override fun getLayout(): Int {
return R.layout.item_subscription
}
override fun initializeViewBinding(p0: View): ItemSubscriptionBinding {
return ItemSubscriptionBinding.bind(p0)
}
}

View file

@ -0,0 +1,56 @@
package ani.dantotsu.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import ani.dantotsu.BottomSheetDialogFragment
import ani.dantotsu.R
import ani.dantotsu.databinding.BottomSheetRecyclerBinding
import ani.dantotsu.notifications.subscription.SubscriptionHelper
import com.xwray.groupie.GroupieAdapter
class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
private var _binding: BottomSheetRecyclerBinding? = null
private val binding get() = _binding!!
private val adapter: GroupieAdapter = GroupieAdapter()
private var subscriptions: Map<Int, SubscriptionHelper.Companion.SubscribeMedia> = mapOf()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = BottomSheetRecyclerBinding.inflate(inflater, container, false)
return _binding?.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.repliesRecyclerView.adapter = adapter
binding.repliesRecyclerView.layoutManager = LinearLayoutManager(
context,
LinearLayoutManager.VERTICAL,
false
)
val context = requireContext()
binding.title.text = context.getString(R.string.subscriptions)
binding.replyButton.visibility = View.GONE
subscriptions.forEach { (id, media) ->
adapter.add(SubscriptionItem(id, media, adapter))
}
}
override fun onDestroyView() {
_binding = null
super.onDestroyView()
}
companion object {
fun newInstance(subscriptions: Map<Int, SubscriptionHelper.Companion.SubscribeMedia>): SubscriptionsBottomDialog {
val dialog = SubscriptionsBottomDialog()
dialog.subscriptions = subscriptions
return dialog
}
}
}

View file

@ -395,9 +395,11 @@ object PrefManager {
val obj = ois.readObject() as T? val obj = ois.readObject() as T?
obj obj
} else { } else {
Logger.log("Serialized data is null (key: $key)")
default default
} }
} catch (e: java.io.InvalidClassException) { } catch (e: java.io.InvalidClassException) {
Logger.log(e)
try { try {
getPrefLocation(location).edit().remove(key).apply() getPrefLocation(location).edit().remove(key).apply()
default default

View file

@ -85,5 +85,7 @@ interface SAnime : Serializable {
fun create(): SAnime { fun create(): SAnime {
return SAnimeImpl() return SAnimeImpl()
} }
private const val serialVersionUID = 1L
} }
} }

View file

@ -84,5 +84,7 @@ interface SManga : Serializable {
fun create(): SManga { fun create(): SManga {
return SMangaImpl() return SMangaImpl()
} }
private const val serialVersionUID = 1L
} }
} }

View file

@ -46,12 +46,12 @@
android:layout_weight="1" android:layout_weight="1"
android:fontFamily="@font/poppins_bold" android:fontFamily="@font/poppins_bold"
android:text="@string/notifications" android:text="@string/notifications"
android:textSize="28sp" /> android:textSize="27sp" />
<ImageView <ImageView
android:layout_width="96dp" android:layout_width="96dp"
android:layout_height="96dp" android:layout_height="96dp"
android:layout_marginEnd="20dp" android:layout_marginEnd="14dp"
android:layout_marginBottom="20dp" android:layout_marginBottom="20dp"
android:padding="24dp" android:padding="24dp"
app:srcCompat="@drawable/ic_round_notifications_none_24" app:srcCompat="@drawable/ic_round_notifications_none_24"

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:layout_marginHorizontal="8dp"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<TextView
android:id="@+id/subscriptionName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:fontFamily="@font/poppins_bold"
android:text="@string/placeholder"
android:textSize="16sp" />
<ImageView
android:id="@+id/deleteSubscription"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="8dp"
android:contentDescription="@string/delete"
android:src="@drawable/ic_circle_cancel"
app:tint="?attr/colorOnBackground" />
</LinearLayout>

View file

@ -991,4 +991,8 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="replies">Replies</string> <string name="replies">Replies</string>
<string name="open_rules">Open Rules</string> <string name="open_rules">Open Rules</string>
<string name="post_to_anilist_warning">By posting to AniList, you agree to the rules and guidelines of AniList</string> <string name="post_to_anilist_warning">By posting to AniList, you agree to the rules and guidelines of AniList</string>
<string name="view_subscriptions">View Subscriptions</string>
<string name="view_subscriptions_desc">View and edit all your subscriptions</string>
<string name="subscriptions">Subscriptions</string>
<string name="subscription_deleted">Subscription Deleted</string>
</resources> </resources>