feat: repo editor in extension window (#332)

* fix: error checking in repo editor

* feat: edit repos from extension page
This commit is contained in:
TwistedUmbrellaX 2024-04-07 22:27:27 -04:00 committed by GitHub
parent f96d2ffaa5
commit 29e115ce41
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 253 additions and 99 deletions

View file

@ -1,34 +1,54 @@
package ani.dantotsu.settings package ani.dantotsu.settings
import android.app.AlertDialog import android.app.AlertDialog
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.TextWatcher import android.text.TextWatcher
import android.view.HapticFeedbackConstants
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.AutoCompleteTextView import android.widget.AutoCompleteTextView
import android.widget.EditText
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R import ani.dantotsu.R
import ani.dantotsu.copyToClipboard
import ani.dantotsu.currContext import ani.dantotsu.currContext
import ani.dantotsu.databinding.ActivityExtensionsBinding import ani.dantotsu.databinding.ActivityExtensionsBinding
import ani.dantotsu.databinding.DialogRepositoriesBinding
import ani.dantotsu.databinding.ItemRepositoryBinding
import ani.dantotsu.initActivity import ani.dantotsu.initActivity
import ani.dantotsu.media.MediaType
import ani.dantotsu.navBarHeight import ani.dantotsu.navBarHeight
import ani.dantotsu.others.AndroidBug5497Workaround import ani.dantotsu.others.AndroidBug5497Workaround
import ani.dantotsu.others.LanguageMapper import ani.dantotsu.others.LanguageMapper
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.snackString
import ani.dantotsu.statusBarHeight import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager import ani.dantotsu.themes.ThemeManager
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager
import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import uy.kohesive.injekt.injectLazy
class ExtensionsActivity : AppCompatActivity() { class ExtensionsActivity : AppCompatActivity() {
lateinit var binding: ActivityExtensionsBinding lateinit var binding: ActivityExtensionsBinding
private val animeExtensionManager: AnimeExtensionManager by injectLazy()
private val mangaExtensionManager: MangaExtensionManager by injectLazy()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -87,6 +107,13 @@ class ExtensionsActivity : AppCompatActivity() {
viewPager.updateLayoutParams<ViewGroup.LayoutParams> { viewPager.updateLayoutParams<ViewGroup.LayoutParams> {
height = ViewGroup.LayoutParams.MATCH_PARENT height = ViewGroup.LayoutParams.MATCH_PARENT
} }
if (tab.text?.contains("Anime") == true) {
generateRepositoryButton(MediaType.ANIME)
}
if (tab.text?.contains("Manga") == true) {
generateRepositoryButton(MediaType.MANGA)
}
} }
override fun onTabUnselected(tab: TabLayout.Tab) { override fun onTabUnselected(tab: TabLayout.Tab) {
@ -162,10 +189,123 @@ class ExtensionsActivity : AppCompatActivity() {
topMargin = statusBarHeight topMargin = statusBarHeight
bottomMargin = navBarHeight bottomMargin = navBarHeight
} }
} }
private fun processUserInput(input: String, mediaType: MediaType) {
val entry = if (input.endsWith("/") || input.endsWith("index.min.json"))
input.substring(0, input.lastIndexOf("/")) else input
if (mediaType == MediaType.ANIME) {
val anime =
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).plus(entry)
PrefManager.setVal(PrefName.AnimeExtensionRepos, anime)
CoroutineScope(Dispatchers.IO).launch {
animeExtensionManager.findAvailableExtensions()
}
}
if (mediaType == MediaType.MANGA) {
val manga =
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).plus(entry)
PrefManager.setVal(PrefName.MangaExtensionRepos, manga)
CoroutineScope(Dispatchers.IO).launch {
mangaExtensionManager.findAvailableExtensions()
}
}
}
private fun getSavedRepositories(repoInventory: ViewGroup, type: MediaType) {
repoInventory.removeAllViews()
val prefName: PrefName? = when (type) {
MediaType.ANIME -> { PrefName.AnimeExtensionRepos }
MediaType.MANGA -> { PrefName.MangaExtensionRepos }
else -> { null }
}
prefName?.let { repoList ->
PrefManager.getVal<Set<String>>(repoList).forEach { item ->
val view = ItemRepositoryBinding.inflate(
LayoutInflater.from(repoInventory.context), repoInventory, true
)
view.repositoryItem.text = item.removePrefix("https://raw.githubusercontent.com")
view.repositoryItem.setOnClickListener {
AlertDialog.Builder(this@ExtensionsActivity, R.style.MyPopup)
.setTitle(R.string.rem_repository)
.setMessage(item)
.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
val repos = PrefManager.getVal<Set<String>>(prefName).minus(item)
PrefManager.setVal(prefName, repos)
repoInventory.removeView(view.root)
CoroutineScope(Dispatchers.IO).launch {
when (type) {
MediaType.ANIME -> { animeExtensionManager.findAvailableExtensions() }
MediaType.MANGA -> { mangaExtensionManager.findAvailableExtensions() }
else -> { }
}
}
dialog.dismiss()
}
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
.create()
.show()
}
view.repositoryItem.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
copyToClipboard(item, true)
true
}
}
}
}
private fun processEditorAction(editText: EditText, mediaType: MediaType) {
editText.setOnEditorActionListener { textView, action, keyEvent ->
if (action == EditorInfo.IME_ACTION_SEARCH || action == EditorInfo.IME_ACTION_DONE ||
(keyEvent?.action == KeyEvent.ACTION_UP
&& keyEvent.keyCode == KeyEvent.KEYCODE_ENTER)
) {
return@setOnEditorActionListener if (textView.text.isNullOrBlank()) {
false
} else {
processUserInput(textView.text.toString(), mediaType)
true
}
}
false
}
}
private fun generateRepositoryButton(type: MediaType) {
val hintResource: Int? = when (type) {
MediaType.ANIME -> { R.string.anime_add_repository }
MediaType.MANGA -> { R.string.manga_add_repository }
else -> { null }
}
hintResource?.let { res ->
binding.openSettingsButton.setOnClickListener {
val dialogView = DialogRepositoriesBinding.inflate(
LayoutInflater.from(binding.openSettingsButton.context), null, false
)
dialogView.repositoryTextBox.hint = getString(res)
dialogView.repoInventory.apply {
getSavedRepositories(this, type)
}
val alertDialog = AlertDialog.Builder(this@ExtensionsActivity, R.style.MyPopup)
.setTitle(R.string.edit_repositories)
.setView(dialogView.root)
.setPositiveButton(getString(R.string.add)) { dialog, _ ->
if (!dialogView.repositoryTextBox.text.isNullOrBlank())
processUserInput(dialogView.repositoryTextBox.text.toString(), type)
}
.setNegativeButton(getString(R.string.close)) { dialog, _ ->
dialog.dismiss()
}
.create()
processEditorAction(dialogView.repositoryTextBox, type)
alertDialog.show()
alertDialog.window?.setDimAmount(0.8f)
}
}
}
} }
interface SearchQueryHandler { interface SearchQueryHandler {

View file

@ -593,71 +593,50 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
bindingExtensions = ActivitySettingsExtensionsBinding.bind(binding.root).apply { bindingExtensions = ActivitySettingsExtensionsBinding.bind(binding.root).apply {
fun setExtensionOutput() { fun setExtensionOutput(repoInventory: ViewGroup, type: MediaType) {
animeRepoInventory.removeAllViews() repoInventory.removeAllViews()
PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).forEach { item -> val prefName: PrefName? = when (type) {
val view = ItemRepositoryBinding.inflate( MediaType.ANIME -> { PrefName.AnimeExtensionRepos }
LayoutInflater.from(animeRepoInventory.context), animeRepoInventory, true MediaType.MANGA -> { PrefName.MangaExtensionRepos }
) else -> { null }
view.repositoryItem.text = item.replace("https://raw.githubusercontent.com/", "")
view.repositoryItem.setOnClickListener {
AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
.setTitle("Delete Anime Repository")
.setMessage(item)
.setPositiveButton(getString(R.string.ok)) { dialog, _ ->
val anime = PrefManager.getVal<Set<String>>(PrefName.AnimeExtensionRepos).minus(item)
PrefManager.setVal(PrefName.AnimeExtensionRepos, anime)
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
setExtensionOutput()
CoroutineScope(Dispatchers.IO).launch {
animeExtensionManager.findAvailableExtensions()
}
dialog.dismiss()
}
.setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
dialog.dismiss()
}
.create()
.show()
}
view.repositoryItem.setOnLongClickListener {
copyToClipboard(item, true)
true
}
} }
animeRepoInventory.isVisible = animeRepoInventory.childCount > 0 prefName?.let { repoList ->
mangaRepoInventory.removeAllViews() PrefManager.getVal<Set<String>>(repoList).forEach { item ->
PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).forEach { item -> val view = ItemRepositoryBinding.inflate(
val view = ItemRepositoryBinding.inflate( LayoutInflater.from(repoInventory.context), repoInventory, true
LayoutInflater.from(mangaRepoInventory.context), mangaRepoInventory, true )
) view.repositoryItem.text = item.removePrefix("https://raw.githubusercontent.com")
view.repositoryItem.text = item.replace("https://raw.githubusercontent.com/", "") view.repositoryItem.setOnClickListener {
view.repositoryItem.setOnClickListener { AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) .setTitle(R.string.rem_repository)
.setTitle("Delete Manga Repository") .setMessage(item)
.setMessage(item) .setPositiveButton(getString(R.string.ok)) { dialog, _ ->
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> val repos = PrefManager.getVal<Set<String>>(repoList).minus(item)
val manga = PrefManager.getVal<Set<String>>(PrefName.MangaExtensionRepos).minus(item) PrefManager.setVal(repoList, repos)
PrefManager.setVal(PrefName.MangaExtensionRepos, manga) setExtensionOutput(repoInventory, type)
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) CoroutineScope(Dispatchers.IO).launch {
setExtensionOutput() when (type) {
CoroutineScope(Dispatchers.IO).launch { MediaType.ANIME -> { animeExtensionManager.findAvailableExtensions() }
mangaExtensionManager.findAvailableExtensions() MediaType.MANGA -> { mangaExtensionManager.findAvailableExtensions() }
else -> { }
}
}
dialog.dismiss()
} }
dialog.dismiss() .setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
} dialog.dismiss()
.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> }
dialog.dismiss() .create()
} .show()
.create() }
.show() view.repositoryItem.setOnLongClickListener {
} it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
view.repositoryItem.setOnLongClickListener { copyToClipboard(item, true)
copyToClipboard(item, true) true
true }
} }
repoInventory.isVisible = repoInventory.childCount > 0
} }
mangaRepoInventory.isVisible = mangaRepoInventory.childCount > 0
} }
fun processUserInput(input: String, mediaType: MediaType) { fun processUserInput(input: String, mediaType: MediaType) {
@ -670,6 +649,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
animeExtensionManager.findAvailableExtensions() animeExtensionManager.findAvailableExtensions()
} }
setExtensionOutput(animeRepoInventory, MediaType.ANIME)
} }
if (mediaType == MediaType.MANGA) { if (mediaType == MediaType.MANGA) {
val manga = val manga =
@ -678,8 +658,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
mangaExtensionManager.findAvailableExtensions() mangaExtensionManager.findAvailableExtensions()
} }
setExtensionOutput(mangaRepoInventory, MediaType.MANGA)
} }
setExtensionOutput()
} }
fun processEditorAction(dialog: AlertDialog, editText: EditText, mediaType: MediaType) { fun processEditorAction(dialog: AlertDialog, editText: EditText, mediaType: MediaType) {
@ -688,15 +668,20 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
(keyEvent?.action == KeyEvent.ACTION_UP (keyEvent?.action == KeyEvent.ACTION_UP
&& keyEvent.keyCode == KeyEvent.KEYCODE_ENTER) && keyEvent.keyCode == KeyEvent.KEYCODE_ENTER)
) { ) {
processUserInput(textView.text.toString(), mediaType) return@setOnEditorActionListener if (textView.text.isNullOrBlank()) {
dialog.dismiss() false
return@setOnEditorActionListener true } else {
processUserInput(textView.text.toString(), mediaType)
dialog.dismiss()
true
}
} }
false false
} }
} }
setExtensionOutput() setExtensionOutput(animeRepoInventory, MediaType.ANIME)
setExtensionOutput(mangaRepoInventory, MediaType.MANGA)
animeAddRepository.setOnClickListener { animeAddRepository.setOnClickListener {
val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null) val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null)
val editText = val editText =
@ -704,16 +689,11 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
hint = getString(R.string.anime_add_repository) hint = getString(R.string.anime_add_repository)
} }
val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
.setTitle(R.string.add_repository) .setTitle(R.string.anime_add_repository)
.setMessage("Add additional repo for anime extensions")
.setView(dialogView) .setView(dialogView)
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> .setPositiveButton(getString(R.string.ok)) { dialog, _ ->
processUserInput(editText.text.toString(), MediaType.ANIME) if (!editText.text.isNullOrBlank())
dialog.dismiss() processUserInput(editText.text.toString(), MediaType.ANIME)
}
.setNeutralButton(getString(R.string.reset)) { dialog, _ ->
PrefManager.removeVal(PrefName.DefaultUserAgent)
editText.setText("")
dialog.dismiss() dialog.dismiss()
} }
.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> .setNegativeButton(getString(R.string.cancel)) { dialog, _ ->
@ -733,16 +713,11 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene
hint = getString(R.string.manga_add_repository) hint = getString(R.string.manga_add_repository)
} }
val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup)
.setTitle(R.string.add_repository) .setTitle(R.string.manga_add_repository)
.setView(dialogView) .setView(dialogView)
.setMessage("Add additional repo for manga extensions")
.setPositiveButton(getString(R.string.ok)) { dialog, _ -> .setPositiveButton(getString(R.string.ok)) { dialog, _ ->
processUserInput(editText.text.toString(), MediaType.MANGA) if (!editText.text.isNullOrBlank())
dialog.dismiss() processUserInput(editText.text.toString(), MediaType.MANGA)
}
.setNeutralButton(getString(R.string.reset)) { dialog, _ ->
PrefManager.removeVal(PrefName.DefaultUserAgent)
editText.setText("")
dialog.dismiss() dialog.dismiss()
} }
.setNegativeButton(getString(R.string.cancel)) { dialog, _ -> .setNegativeButton(getString(R.string.cancel)) { dialog, _ ->

View file

@ -35,16 +35,31 @@
android:textColor="?attr/colorPrimary" android:textColor="?attr/colorPrimary"
android:textSize="16sp" /> android:textSize="16sp" />
<ImageButton <LinearLayout
android:id="@+id/languageselect" android:layout_width="wrap_content"
android:layout_width="48dp"
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginLeft="16dp" android:layout_marginHorizontal="16dp"
android:layout_marginRight="16dp" android:orientation="horizontal">
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/sort_by" <ImageButton
app:srcCompat="@drawable/ic_round_translate_24" android:id="@+id/openSettingsButton"
app:tint="?attr/colorOnBackground" /> android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/sort_by"
app:srcCompat="@drawable/ic_github"
app:tint="?attr/colorOnBackground" />
<ImageButton
android:id="@+id/languageselect"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/sort_by"
app:srcCompat="@drawable/ic_round_translate_24"
app:tint="?attr/colorOnBackground" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -0,0 +1,26 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<LinearLayout
android:id="@+id/repoInventory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/repositoryTextBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxCornerRadiusBottomEnd="8dp"
app:boxCornerRadiusBottomStart="8dp"
app:boxCornerRadiusTopEnd="8dp"
app:boxCornerRadiusTopStart="8dp"
android:imeOptions="actionDone"
app:hintAnimationEnabled="true" />
</LinearLayout>

View file

@ -14,7 +14,7 @@
<string name="github" translatable="false">https://github.com/rebelonion/Dantotsu</string> <string name="github" translatable="false">https://github.com/rebelonion/Dantotsu</string>
<string name="telegram" translatable="false" tools:ignore="Typos">https://t.me/+gzBCQExtLQo1YTNh </string> <string name="telegram" translatable="false" tools:ignore="Typos">https://t.me/+gzBCQExtLQo1YTNh </string>
<string name="coffee" translatable="false">https://www.buymeacoffee.com/rebelonion</string> <string name="coffee" translatable="false">https://www.buymeacoffee.com/rebelonion</string>
<string name="anilist_link">https://anilist.co/user/%1$s/</string> <string name="anilist_link">https://anilist.co/user/%1$s/</string>
<string name="myanilist_link">https://myanimelist.net/profile/%1$s/</string> <string name="myanilist_link">https://myanimelist.net/profile/%1$s/</string>
<string name="discord_link">https://discord.com/users/%1$s/</string> <string name="discord_link">https://discord.com/users/%1$s/</string>
@ -864,13 +864,11 @@ Non quae tempore quo provident laudantium qui illo dolor vel quia dolor et exerc
<string name="stat_text_color">Stats Text Color</string> <string name="stat_text_color">Stats Text Color</string>
<string name="placeholder">Placeholder</string> <string name="placeholder">Placeholder</string>
<string name="anime_repo_listing">Anime Extension Repos</string>
<string name="anime_add_repository">Add Anime Repo</string> <string name="anime_add_repository">Add Anime Repo</string>
<string name="manga_repo_listing">Manga Extension Repos</string>
<string name="manga_add_repository">Add Manga Repo</string> <string name="manga_add_repository">Add Manga Repo</string>
<string name="add_repository">Add Repo</string> <string name="edit_repositories">Edit repositories</string>
<string name="long_click_delete">Long click to delete</string> <string name="rem_repository">Remove repository?</string>
<string name="trending_movies">Trending Movies</string> <string name="trending_movies">Trending Movies</string>
<string name="include_list">Include list</string> <string name="include_list">Include list</string>
<string name="top_rated">Top rated</string> <string name="top_rated">Top rated</string>