Light novel support

This commit is contained in:
Finnley Somdahl 2023-11-30 03:41:45 -06:00
parent 32f918450a
commit c7bc1ffe9e
39 changed files with 2537 additions and 91 deletions

View file

@ -0,0 +1,211 @@
package ani.dantotsu.settings
import android.app.AlertDialog
import android.app.NotificationManager
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R
import ani.dantotsu.currContext
import ani.dantotsu.databinding.FragmentMangaExtensionsBinding
import ani.dantotsu.databinding.FragmentNovelExtensionsBinding
import ani.dantotsu.loadData
import ani.dantotsu.others.LanguageMapper
import ani.dantotsu.parsers.novel.NovelExtension
import ani.dantotsu.parsers.novel.NovelExtensionManager
import ani.dantotsu.snackString
import com.google.android.material.tabs.TabLayout
import com.google.android.material.textfield.TextInputLayout
import com.google.firebase.crashlytics.FirebaseCrashlytics
import eu.kanade.tachiyomi.data.notification.Notifications
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class InstalledNovelExtensionsFragment : Fragment() {
private var _binding: FragmentNovelExtensionsBinding? = null
private val binding get() = _binding!!
private lateinit var extensionsRecyclerView: RecyclerView
val skipIcons = loadData("skip_extension_icons") ?: false
private val novelExtensionManager: NovelExtensionManager = Injekt.get()
private val extensionsAdapter = NovelExtensionsAdapter({ pkg ->
Toast.makeText(requireContext(), "Source is not configurable", Toast.LENGTH_SHORT)
.show()
},
{ pkg ->
if (isAdded) { // Check if the fragment is currently added to its activity
val context = requireContext() // Store context in a variable
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // Initialize NotificationManager once
if (pkg.hasUpdate) {
novelExtensionManager.updateExtension(pkg)
.observeOn(AndroidSchedulers.mainThread()) // Observe on main thread
.subscribe(
{ installStep ->
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(R.drawable.ic_round_sync_24)
.setContentTitle("Updating extension")
.setContentText("Step: $installStep")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
},
{ error ->
FirebaseCrashlytics.getInstance().recordException(error)
Log.e("NovelExtensionsAdapter", "Error: ", error) // Log the error
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_ERROR
)
.setSmallIcon(R.drawable.ic_round_info_24)
.setContentTitle("Update failed: ${error.message}")
.setContentText("Error: ${error.message}")
.setPriority(NotificationCompat.PRIORITY_HIGH)
notificationManager.notify(1, builder.build())
snackString("Update failed: ${error.message}")
},
{
val builder = NotificationCompat.Builder(
context,
Notifications.CHANNEL_DOWNLOADER_PROGRESS
)
.setSmallIcon(androidx.media3.ui.R.drawable.exo_ic_check)
.setContentTitle("Update complete")
.setContentText("The extension has been successfully updated.")
.setPriority(NotificationCompat.PRIORITY_LOW)
notificationManager.notify(1, builder.build())
snackString("Extension updated")
}
)
} else {
novelExtensionManager.uninstallExtension(pkg.pkgName, currContext()?:context)
snackString("Extension uninstalled")
}
}
}, skipIcons)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNovelExtensionsBinding.inflate(inflater, container, false)
extensionsRecyclerView = binding.allNovelExtensionsRecyclerView
extensionsRecyclerView.layoutManager = LinearLayoutManager(requireContext())
extensionsRecyclerView.adapter = extensionsAdapter
lifecycleScope.launch {
novelExtensionManager.installedExtensionsFlow.collect { extensions ->
extensionsAdapter.updateData(extensions)
}
}
val extensionsRecyclerView: RecyclerView = binding.allNovelExtensionsRecyclerView
return binding.root
}
override fun onResume() {
super.onResume()
}
override fun onDestroyView() {
super.onDestroyView();_binding = null
}
private class NovelExtensionsAdapter(
private val onSettingsClicked: (NovelExtension.Installed) -> Unit,
private val onUninstallClicked: (NovelExtension.Installed) -> Unit,
skipIcons: Boolean
) : ListAdapter<NovelExtension.Installed, NovelExtensionsAdapter.ViewHolder>(
DIFF_CALLBACK_INSTALLED
) {
val skipIcons = skipIcons
fun updateData(newExtensions: List<NovelExtension.Installed>) {
Log.d("NovelExtensionsAdapter", "updateData: $newExtensions")
submitList(newExtensions) // Use submitList instead of manual list handling
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_extension, parent, false)
Log.d("NovelExtensionsAdapter", "onCreateViewHolder: $view")
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val extension = getItem(position) // Use getItem() from ListAdapter
val nsfw = ""
val lang = LanguageMapper.mapLanguageCodeToName("all")
holder.extensionNameTextView.text = extension.name
holder.extensionVersionTextView.text = "$lang ${extension.versionName} $nsfw"
if (!skipIcons) {
holder.extensionIconImageView.setImageDrawable(extension.icon)
}
if (extension.hasUpdate) {
holder.closeTextView.setImageResource(R.drawable.ic_round_sync_24)
} else {
holder.closeTextView.setImageResource(R.drawable.ic_round_delete_24)
}
holder.closeTextView.setOnClickListener {
onUninstallClicked(extension)
}
holder.settingsImageView.setOnClickListener {
onSettingsClicked(extension)
}
}
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val extensionNameTextView: TextView = view.findViewById(R.id.extensionNameTextView)
val extensionVersionTextView: TextView = view.findViewById(R.id.extensionVersionTextView)
val settingsImageView: ImageView = view.findViewById(R.id.settingsImageView)
val extensionIconImageView: ImageView = view.findViewById(R.id.extensionIconImageView)
val closeTextView: ImageView = view.findViewById(R.id.closeTextView)
}
companion object {
val DIFF_CALLBACK_INSTALLED =
object : DiffUtil.ItemCallback<NovelExtension.Installed>() {
override fun areItemsTheSame(
oldItem: NovelExtension.Installed,
newItem: NovelExtension.Installed
): Boolean {
return oldItem.pkgName == newItem.pkgName
}
override fun areContentsTheSame(
oldItem: NovelExtension.Installed,
newItem: NovelExtension.Installed
): Boolean {
return oldItem == newItem
}
}
}
}
}