Skip to content

Commit

Permalink
[feature] Support parsing the link tag as enclosure
Browse files Browse the repository at this point in the history
  • Loading branch information
SkyD666 committed Feb 28, 2024
1 parent 40adf00 commit c5d37eb
Show file tree
Hide file tree
Showing 20 changed files with 483 additions and 45 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 5
versionName = "1.0-beta10"
versionName = "1.0-beta11"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.skyd.anivu.model.bean

import com.skyd.anivu.base.BaseBean

data class LinkEnclosureBean(
val link: String,
) : BaseBean
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.skyd.anivu.model.bean.settings

import android.graphics.drawable.Drawable
import com.skyd.anivu.base.BaseBean

data class SettingsSwitchBean(
val title: String,
val description: String,
val icon: Drawable,
val isChecked: () -> Boolean = { false },
val onCheckedChanged: ((Boolean) -> Unit)? = null,
) : BaseBean
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.skyd.anivu.model.preference.rss

import android.content.Context
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import com.skyd.anivu.base.BasePreference
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.put
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

object ParseLinkTagAsEnclosurePreference : BasePreference<Boolean> {
private const val PARSE_LINK_TAG_AS_ENCLOSURE = "parseLinkTagAsEnclosure"
override val default = true

val key = booleanPreferencesKey(PARSE_LINK_TAG_AS_ENCLOSURE)

fun put(context: Context, scope: CoroutineScope, value: Boolean) {
scope.launch(Dispatchers.IO) {
context.dataStore.put(key, value)
}
}

override fun fromPreferences(preferences: Preferences): Boolean = preferences[key] ?: default
}
Original file line number Diff line number Diff line change
Expand Up @@ -235,27 +235,29 @@ class DownloadTorrentWorker(context: Context, parameters: WorkerParameters) :
saveDir: File,
flags: torrent_flags_t = torrent_flags_t(),
) {
if (link.startsWith("magnet:")) {
sessionManager.download(link, saveDir, flags)
} else if (
(link.startsWith("http:") || link.startsWith("https:")) &&
link.endsWith(".torrent")
) {
val tempTorrentFile = File(
Const.TEMP_TORRENT_DIR,
link.substringAfterLast('/').toDecodedUrl().validateFileName()
)
hiltEntryPoint.retrofit.create(HttpService::class.java)
.requestGetResponseBody(link).execute().body()!!.byteStream()
.use { it.saveTo(tempTorrentFile) }
sessionManager.download(
TorrentInfo(tempTorrentFile), saveDir,
null, null, null,
flags
)
} else {
error("Unsupported link: $link")
}
doIfMagnetOrTorrentLink(
link = link,
onMagnet = {
sessionManager.download(link, saveDir, flags)
},
onTorrent = {
val tempTorrentFile = File(
Const.TEMP_TORRENT_DIR,
link.substringAfterLast('/').toDecodedUrl().validateFileName()
)
hiltEntryPoint.retrofit.create(HttpService::class.java)
.requestGetResponseBody(link).execute().body()!!.byteStream()
.use { it.saveTo(tempTorrentFile) }
sessionManager.download(
TorrentInfo(tempTorrentFile), saveDir,
null, null, null,
flags
)
},
onUnsupported = {
error("Unsupported link: $link")
},
)
}

override suspend fun getForegroundInfo() = createForegroundInfo()
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/skyd/anivu/model/worker/download/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ import java.io.FileOutputStream
import java.io.IOException


fun doIfMagnetOrTorrentLink(
link: String,
onMagnet: (String) -> Unit,
onTorrent: (String) -> Unit,
onUnsupported: (String) -> Unit = {},
) {
if (link.startsWith("magnet:")) {
onMagnet(link)
} else if (
(link.startsWith("http:") || link.startsWith("https:")) &&
link.endsWith(".torrent")
) {
onTorrent(link)
} else {
onUnsupported(link)
}
}

fun TorrentStatus.State.toDisplayString(context: Context): String {
return when (this) {
TorrentStatus.State.CHECKING_FILES -> context.getString(R.string.torrent_status_checking_files)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import com.skyd.anivu.databinding.ItemDownload1Binding
import com.skyd.anivu.databinding.ItemEnclosure1Binding
import com.skyd.anivu.databinding.ItemFeed1Binding
import com.skyd.anivu.databinding.ItemLicense1Binding
import com.skyd.anivu.databinding.ItemLinkEnclosure1Binding
import com.skyd.anivu.databinding.ItemMedia1Binding
import com.skyd.anivu.databinding.ItemMore1Binding
import com.skyd.anivu.databinding.ItemOtherWorks1Binding
import com.skyd.anivu.databinding.ItemParentDir1Binding
import com.skyd.anivu.databinding.ItemSettingsBaseBinding
import com.skyd.anivu.databinding.ItemSettingsSwitchBinding

abstract class BaseViewHolder<V : ViewBinding>(val binding: V) :
RecyclerView.ViewHolder(binding.root)
Expand All @@ -27,6 +29,9 @@ class Article1ViewHolder(binding: ItemArticle1Binding) :
class Enclosure1ViewHolder(binding: ItemEnclosure1Binding) :
BaseViewHolder<ItemEnclosure1Binding>(binding)

class LinkEnclosure1ViewHolder(binding: ItemLinkEnclosure1Binding) :
BaseViewHolder<ItemLinkEnclosure1Binding>(binding)

class Download1ViewHolder(binding: ItemDownload1Binding) :
BaseViewHolder<ItemDownload1Binding>(binding)

Expand All @@ -47,3 +52,6 @@ class License1ViewHolder(binding: ItemLicense1Binding) :

class SettingsBaseViewHolder(binding: ItemSettingsBaseBinding) :
BaseViewHolder<ItemSettingsBaseBinding>(binding)

class SettingsSwitchViewHolder(binding: ItemSettingsSwitchBinding) :
BaseViewHolder<ItemSettingsSwitchBinding>(binding)
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.skyd.anivu.ui.adapter.variety.proxy


import android.view.LayoutInflater
import android.view.ViewGroup
import com.skyd.anivu.databinding.ItemLinkEnclosure1Binding
import com.skyd.anivu.ext.copy
import com.skyd.anivu.model.bean.LinkEnclosureBean
import com.skyd.anivu.ui.adapter.variety.LinkEnclosure1ViewHolder
import com.skyd.anivu.ui.adapter.variety.VarietyAdapter


class LinkEnclosure1Proxy(
private val onDownload: (LinkEnclosureBean) -> Unit,
) : VarietyAdapter.Proxy<LinkEnclosureBean, ItemLinkEnclosure1Binding, LinkEnclosure1ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
LinkEnclosure1ViewHolder(
ItemLinkEnclosure1Binding
.inflate(LayoutInflater.from(parent.context), parent, false),
)

override fun onBindViewHolder(
holder: LinkEnclosure1ViewHolder,
data: LinkEnclosureBean,
index: Int,
action: ((Any?) -> Unit)?
) {
val context = holder.itemView.context
holder.binding.apply {
tvEnclosure1Url.text = data.link
tvEnclosure1Url.setOnClickListener {
data.link.copy(context)
}
btnEnclosure1Download.setOnClickListener {
onDownload(data)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.skyd.anivu.ui.adapter.variety.proxy.settings


import android.view.LayoutInflater
import android.view.ViewGroup
import com.skyd.anivu.databinding.ItemSettingsSwitchBinding
import com.skyd.anivu.model.bean.settings.SettingsSwitchBean
import com.skyd.anivu.ui.adapter.variety.SettingsSwitchViewHolder
import com.skyd.anivu.ui.adapter.variety.VarietyAdapter


class SettingsSwitchProxy(
private val adapter: VarietyAdapter,
) : VarietyAdapter.Proxy<SettingsSwitchBean, ItemSettingsSwitchBinding, SettingsSwitchViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SettingsSwitchViewHolder {
val holder = SettingsSwitchViewHolder(
ItemSettingsSwitchBinding
.inflate(LayoutInflater.from(parent.context), parent, false),
)

holder.itemView.setOnClickListener {
val data = adapter.dataList.getOrNull(holder.bindingAdapterPosition)
if (data !is SettingsSwitchBean) return@setOnClickListener
holder.binding.apply {
switchSettingsSwitch.isChecked = !switchSettingsSwitch.isChecked
data.onCheckedChanged?.invoke(switchSettingsSwitch.isChecked)
}
}
return holder
}

override fun onBindViewHolder(
holder: SettingsSwitchViewHolder,
data: SettingsSwitchBean,
index: Int,
action: ((Any?) -> Unit)?
) {
holder.binding.apply {
tvSettingsSwitchTitle.text = data.title
tvSettingsSwitchDesc.text = data.description
ivSettingsSwitchIcon.setImageDrawable(data.icon)
switchSettingsSwitch.isChecked = data.isChecked()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import com.skyd.anivu.R
import com.skyd.anivu.base.BaseBottomSheetDialogFragment
import com.skyd.anivu.databinding.BottomSheetEnclosureBinding
import com.skyd.anivu.model.bean.EnclosureBean
import com.skyd.anivu.model.bean.LinkEnclosureBean
import com.skyd.anivu.model.worker.download.DownloadTorrentWorker
import com.skyd.anivu.ui.adapter.variety.AniSpanSize
import com.skyd.anivu.ui.adapter.variety.VarietyAdapter
import com.skyd.anivu.ui.adapter.variety.proxy.Enclosure1Proxy
import com.skyd.anivu.ui.adapter.variety.proxy.LinkEnclosure1Proxy

class EnclosureBottomSheet : BaseBottomSheetDialogFragment<BottomSheetEnclosureBinding>() {
companion object {
Expand All @@ -26,38 +28,56 @@ class EnclosureBottomSheet : BaseBottomSheetDialogFragment<BottomSheetEnclosureB
) { resultMap ->
if (!resultMap.containsValue(false)) {
waitingDownloadList.removeIf {
val url = when (it) {
is EnclosureBean -> it.url
is LinkEnclosureBean -> it.link
else -> return@removeIf true
}

DownloadTorrentWorker.startWorker(
context = requireContext(),
torrentLink = it.url
torrentLink = url
)
true
}
}
}

private val waitingDownloadList = mutableListOf<EnclosureBean>()
private val waitingDownloadList = mutableListOf<Any>()

private val adapter = VarietyAdapter(
mutableListOf(Enclosure1Proxy(onDownload = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
waitingDownloadList += it
registerPostNotificationPermission.launch(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
arrayOf(
android.Manifest.permission.POST_NOTIFICATIONS,
android.Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE,
)
} else arrayOf(android.Manifest.permission.POST_NOTIFICATIONS)
)
} else {
private val onDownload: (Any) -> Unit = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
waitingDownloadList += it
registerPostNotificationPermission.launch(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
arrayOf(
android.Manifest.permission.POST_NOTIFICATIONS,
android.Manifest.permission.FOREGROUND_SERVICE_SPECIAL_USE,
)
} else arrayOf(android.Manifest.permission.POST_NOTIFICATIONS)
)
} else {
val url = when (it) {
is EnclosureBean -> it.url
is LinkEnclosureBean -> it.link
else -> null
}
if (!url.isNullOrBlank()) {
DownloadTorrentWorker.startWorker(
context = requireContext(), torrentLink = it.url
context = requireContext(), torrentLink = url
)
}
}))
}
}

private val adapter = VarietyAdapter(
mutableListOf(
Enclosure1Proxy(onDownload = onDownload),
LinkEnclosure1Proxy(onDownload = onDownload),
)
)

fun updateData(dataList: List<EnclosureBean>) {
fun updateData(dataList: List<Any>) {
adapter.dataList = dataList
}

Expand Down
29 changes: 27 additions & 2 deletions app/src/main/java/com/skyd/anivu/ui/fragment/read/ReadFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ import com.skyd.anivu.ext.addFabBottomPaddingHook
import com.skyd.anivu.ext.addInsetsByMargin
import com.skyd.anivu.ext.addInsetsByPadding
import com.skyd.anivu.ext.collectIn
import com.skyd.anivu.ext.dataStore
import com.skyd.anivu.ext.getOrDefault
import com.skyd.anivu.ext.ifNullOfBlank
import com.skyd.anivu.ext.openBrowser
import com.skyd.anivu.ext.popBackStackWithLifecycle
import com.skyd.anivu.ext.startWith
import com.skyd.anivu.ext.toHtml
import com.skyd.anivu.model.bean.LinkEnclosureBean
import com.skyd.anivu.model.preference.rss.ParseLinkTagAsEnclosurePreference
import com.skyd.anivu.model.worker.download.doIfMagnetOrTorrentLink
import com.skyd.anivu.util.html.ImageGetter
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.channels.Channel
Expand Down Expand Up @@ -80,7 +85,17 @@ class ReadFragment : BaseFragment<FragmentReadBinding>() {
)
}

enclosureBottomSheet?.updateData(article.enclosures)
val dataList: MutableList<Any> = article.enclosures.toMutableList()
if (requireContext().dataStore.getOrDefault(ParseLinkTagAsEnclosurePreference)) {
article.article.link?.let { link ->
doIfMagnetOrTorrentLink(
link = link,
onMagnet = { dataList += LinkEnclosureBean(link = link) },
onTorrent = { dataList += LinkEnclosureBean(link = link) },
)
}
}
enclosureBottomSheet?.updateData(dataList)
}
}
}
Expand Down Expand Up @@ -131,7 +146,17 @@ class ReadFragment : BaseFragment<FragmentReadBinding>() {
)
val articleState = feedViewModel.viewState.value.articleState
if (articleState is ArticleState.Success) {
enclosureBottomSheet?.updateData(articleState.article.enclosures)
val dataList: MutableList<Any> = articleState.article.enclosures.toMutableList()
if (requireContext().dataStore.getOrDefault(ParseLinkTagAsEnclosurePreference)) {
articleState.article.article.link?.let { link ->
doIfMagnetOrTorrentLink(
link = link,
onMagnet = { dataList += LinkEnclosureBean(link = link) },
onTorrent = { dataList += LinkEnclosureBean(link = link) },
)
}
}
enclosureBottomSheet?.updateData(dataList)
}
}
}
Expand Down
Loading

0 comments on commit c5d37eb

Please sign in to comment.