feat(ALsettings): customList editor

This commit is contained in:
sneazy-ibo
2024-07-07 23:06:34 +02:00
parent ef712ee29d
commit 8e58b20bab
13 changed files with 269 additions and 21 deletions

View File

@@ -47,6 +47,10 @@ object Anilist {
var rowOrder: String? = null
var activityMergeTime: Int? = null
var timezone: String? = null
var animeCustomLists: List<String>? = null
var mangaCustomLists: List<String>? = null
var animeSplitCompletedSectionByFormat: Boolean = false
var mangaSplitCompletedSectionByFormat: Boolean = false
val sortBy = listOf(
"SCORE_DESC",
@@ -183,7 +187,8 @@ object Anilist {
"2 days" to 2880,
"3 days" to 4320,
"1 week" to 10080,
"Always" to 20160
"2 weeks" to 20160,
"Always" to 29160
)
private val cal: Calendar = Calendar.getInstance()

View File

@@ -127,6 +127,51 @@ class AnilistMutations {
ANIME, MANGA, CHARACTER, STAFF, STUDIO
}
suspend fun deleteCustomList(name: String, type: String): Boolean {
val query = """
mutation (${"$"}name: String, ${"$"}type: MediaType) {
DeleteCustomList(customList: ${"$"}name, type: ${"$"}type) {
deleted
}
}
""".trimIndent()
val variables = """
{
"name": "$name",
"type": "$type"
}
""".trimIndent()
val result = executeQuery<JsonObject>(query, variables)
return result?.get("errors") == null
}
suspend fun updateCustomLists(animeCustomLists: List<String>?, mangaCustomLists: List<String>?): Boolean {
val query = """
mutation (${"$"}animeListOptions: MediaListOptionsInput, ${"$"}mangaListOptions: MediaListOptionsInput) {
UpdateUser(animeListOptions: ${"$"}animeListOptions, mangaListOptions: ${"$"}mangaListOptions) {
mediaListOptions {
animeList {
customLists
}
mangaList {
customLists
}
}
}
}
""".trimIndent()
val variables = """
{
${animeCustomLists?.let { """"animeListOptions": {"customLists": ${Gson().toJson(it)}}""" } ?: ""}
${if (animeCustomLists != null && mangaCustomLists != null) "," else ""}
${mangaCustomLists?.let { """"mangaListOptions": {"customLists": ${Gson().toJson(it)}}""" } ?: ""}
}
""".trimIndent().replace("\n", "").replace(""" """, "").replace(",}", "}")
val result = executeQuery<JsonObject>(query, variables)
return result?.get("errors") == null
}
suspend fun editList(
mediaID: Int,
progress: Int? = null,
@@ -237,7 +282,8 @@ class AnilistMutations {
}
suspend fun toggleFollow(id: Int): Query.ToggleFollow? {
return executeQuery<Query.ToggleFollow>("""
return executeQuery<Query.ToggleFollow>(
"""
mutation {
ToggleFollow(userId: $id) {
id
@@ -249,7 +295,8 @@ class AnilistMutations {
}
suspend fun toggleLike(id: Int, type: String): ToggleLike? {
return executeQuery<ToggleLike>("""
return executeQuery<ToggleLike>(
"""
mutation Like {
ToggleLikeV2(id: $id, type: $type) {
__typename

View File

@@ -61,8 +61,8 @@ class AnilistQueries {
mediaListOptions {
scoreFormat
rowOrder
animeList { sectionOrder customLists }
mangaList { sectionOrder customLists }
animeList { sectionOrder customLists splitCompletedSectionByFormat }
mangaList { sectionOrder customLists splitCompletedSectionByFormat }
}
statistics {
anime { episodesWatched }
@@ -100,6 +100,16 @@ class AnilistQueries {
user.mediaListOptions?.let {
Anilist.scoreFormat = it.scoreFormat.toString()
Anilist.rowOrder = it.rowOrder
it.animeList?.let { animeList ->
Anilist.animeCustomLists = animeList.customLists
Anilist.animeSplitCompletedSectionByFormat = animeList.splitCompletedSectionByFormat ?: false
}
it.mangaList?.let { mangaList ->
Anilist.mangaCustomLists = mangaList.customLists
Anilist.mangaSplitCompletedSectionByFormat = mangaList.splitCompletedSectionByFormat ?: false
}
}
return true
}

View File

@@ -293,7 +293,7 @@ class MediaDetailsActivity : AppCompatActivity(), AppBarLayout.OnOffsetChangedLi
binding.mediaTotal.visibility = View.VISIBLE
binding.mediaAddToList.text = userStatus
} else {
binding.mediaAddToList.setText(R.string.add)
binding.mediaAddToList.setText(R.string.add_list)
}
total()
binding.mediaAddToList.setOnClickListener {

View File

@@ -21,7 +21,6 @@ import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import ani.dantotsu.R
import ani.dantotsu.copyToClipboard
import ani.dantotsu.currContext
import ani.dantotsu.databinding.ActivityExtensionsBinding
import ani.dantotsu.databinding.DialogRepositoriesBinding
import ani.dantotsu.databinding.ItemRepositoryBinding
@@ -327,7 +326,7 @@ class ExtensionsActivity : AppCompatActivity() {
val alertDialog = AlertDialog.Builder(this@ExtensionsActivity, R.style.MyPopup)
.setTitle(R.string.edit_repositories)
.setView(dialogView.root)
.setPositiveButton(getString(R.string.add)) { _, _ ->
.setPositiveButton(getString(R.string.add_list)) { _, _ ->
if (!dialogView.repositoryTextBox.text.isNullOrBlank())
processUserInput(dialogView.repositoryTextBox.text.toString(), type)
}

View File

@@ -4,7 +4,6 @@ import android.content.Intent
import android.os.Bundle
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.ArrayAdapter
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import android.widget.TextView
@@ -23,7 +22,6 @@ import ani.dantotsu.loadImage
import ani.dantotsu.navBarHeight
import ani.dantotsu.openLinkInBrowser
import ani.dantotsu.others.CustomBottomDialog
import ani.dantotsu.restartApp
import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.startMainActivity
@@ -117,6 +115,7 @@ class SettingsAccountActivity : AppCompatActivity() {
} else {
settingsAnilistAvatar.setImageResource(R.drawable.ic_round_person_24)
settingsAnilistUsername.visibility = View.GONE
settingsRecyclerView.visibility = View.GONE
settingsAnilistLogin.setText(R.string.login)
settingsAnilistLogin.setOnClickListener {
Anilist.loginIntent(context)
@@ -148,7 +147,7 @@ class SettingsAccountActivity : AppCompatActivity() {
reload()
}
settingsImageSwitcher.visibility = View.VISIBLE
settingsPresenceSwitcher.visibility = View.VISIBLE
var initialStatus = when (PrefManager.getVal<String>(PrefName.DiscordStatus)) {
"online" -> R.drawable.discord_status_online
"idle" -> R.drawable.discord_status_idle
@@ -156,11 +155,11 @@ class SettingsAccountActivity : AppCompatActivity() {
"invisible" -> R.drawable.discord_status_invisible
else -> R.drawable.discord_status_online
}
settingsImageSwitcher.setImageResource(initialStatus)
settingsPresenceSwitcher.setImageResource(initialStatus)
val zoomInAnimation =
AnimationUtils.loadAnimation(context, R.anim.bounce_zoom)
settingsImageSwitcher.setOnClickListener {
settingsPresenceSwitcher.setOnClickListener {
var status = "online"
initialStatus = when (initialStatus) {
R.drawable.discord_status_online -> {
@@ -187,16 +186,16 @@ class SettingsAccountActivity : AppCompatActivity() {
}
PrefManager.setVal(PrefName.DiscordStatus, status)
settingsImageSwitcher.setImageResource(initialStatus)
settingsImageSwitcher.startAnimation(zoomInAnimation)
settingsPresenceSwitcher.setImageResource(initialStatus)
settingsPresenceSwitcher.startAnimation(zoomInAnimation)
}
settingsImageSwitcher.setOnLongClickListener {
settingsPresenceSwitcher.setOnLongClickListener {
it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
DiscordDialogFragment().show(supportFragmentManager, "dialog")
true
}
} else {
settingsImageSwitcher.visibility = View.GONE
settingsPresenceSwitcher.visibility = View.GONE
settingsDiscordAvatar.setImageResource(R.drawable.ic_round_person_24)
settingsDiscordUsername.visibility = View.GONE
settingsDiscordLogin.setText(R.string.login)

View File

@@ -3,7 +3,10 @@ package ani.dantotsu.settings
import android.os.Bundle
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
@@ -21,6 +24,9 @@ import ani.dantotsu.navBarHeight
import ani.dantotsu.restartApp
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
import ani.dantotsu.toast
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import kotlinx.coroutines.launch
class SettingsAnilistActivity : AppCompatActivity() {
@@ -160,6 +166,26 @@ class SettingsAnilistActivity : AppCompatActivity() {
settingsAnilistRowOrder.clearFocus()
}
val containers = listOf(binding.animeCustomListsContainer, binding.mangaCustomListsContainer)
val customLists = listOf(Anilist.animeCustomLists, Anilist.mangaCustomLists)
val buttons = listOf(binding.addAnimeListButton, binding.addMangaListButton)
containers.forEachIndexed { index, container ->
customLists[index]?.forEach { listName ->
addCustomListItem(listName, container, index == 0)
}
}
buttons.forEachIndexed { index, button ->
button.setOnClickListener {
addCustomListItem("", containers[index], index == 0)
}
}
binding.SettingsAnilistCustomListSave.setOnClickListener {
saveCustomLists()
}
val currentTimezone = Anilist.timezone?.let { Anilist.getDisplayTimezone(it) } ?: "(GMT+00:00) London"
settingsAnilistTimezone.setText(currentTimezone)
settingsAnilistTimezone.setAdapter(
@@ -238,4 +264,76 @@ class SettingsAnilistActivity : AppCompatActivity() {
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
}
private fun addCustomListItem(listName: String, container: LinearLayout, isAnime: Boolean) {
val customListItemView = layoutInflater.inflate(R.layout.item_custom_list, container, false)
val textInputLayout = customListItemView.findViewById<TextInputLayout>(R.id.customListItem)
val editText = textInputLayout.editText as? TextInputEditText
editText?.setText(listName)
textInputLayout.setEndIconOnClickListener {
val name = editText?.text.toString()
if (name.isNotEmpty()) {
val listExists = if (isAnime) {
Anilist.animeCustomLists?.contains(name) ?: false
} else {
Anilist.mangaCustomLists?.contains(name) ?: false
}
if (listExists) {
AlertDialog.Builder(this@SettingsAnilistActivity, R.style.MyPopup)
.setTitle(getString(R.string.delete_custom_list))
.setMessage(getString(R.string.delete_custom_list_confirm, name))
.setPositiveButton(getString(R.string.delete)) { _, _ ->
deleteCustomList(name, isAnime)
container.removeView(customListItemView)
}
.setNegativeButton(getString(R.string.cancel), null)
.show()
} else {
container.removeView(customListItemView)
}
} else {
container.removeView(customListItemView)
}
}
container.addView(customListItemView)
}
private fun deleteCustomList(name: String, isAnime: Boolean) {
lifecycleScope.launch {
val type = if (isAnime) "ANIME" else "MANGA"
val success = anilistMutations.deleteCustomList(name, type)
if (success) {
if (isAnime) {
Anilist.animeCustomLists = Anilist.animeCustomLists?.filter { it != name }
} else {
Anilist.mangaCustomLists = Anilist.mangaCustomLists?.filter { it != name }
}
toast("Custom list deleted")
} else {
toast("Failed to delete custom list")
}
}
}
private fun saveCustomLists() {
val animeCustomLists = binding.animeCustomListsContainer.children
.mapNotNull { (it.findViewById<TextInputLayout>(R.id.customListItem).editText as? TextInputEditText)?.text?.toString() }
.filter { it.isNotEmpty() }
.toList()
val mangaCustomLists = binding.mangaCustomListsContainer.children
.mapNotNull { (it.findViewById<TextInputLayout>(R.id.customListItem).editText as? TextInputEditText)?.text?.toString() }
.filter { it.isNotEmpty() }
.toList()
lifecycleScope.launch {
val success = anilistMutations.updateCustomLists(animeCustomLists, mangaCustomLists)
if (success) {
Anilist.animeCustomLists = animeCustomLists
Anilist.mangaCustomLists = mangaCustomLists
toast("Custom lists saved")
} else {
toast("Failed to save custom lists")
}
}
}
}

View File

@@ -122,7 +122,7 @@
android:marqueeRepeatLimit="marquee_forever"
android:padding="8dp"
android:singleLine="true"
android:text="@string/add"
android:text="@string/add_list"
android:textAllCaps="true"
android:textColor="?attr/colorSecondary"
android:textSize="14sp"

View File

@@ -121,7 +121,7 @@
android:marqueeRepeatLimit="marquee_forever"
android:padding="8dp"
android:singleLine="true"
android:text="@string/add"
android:text="@string/add_list"
android:textAllCaps="true"
android:textColor="?attr/colorSecondary"
android:textSize="14sp"

View File

@@ -265,7 +265,7 @@
</LinearLayout>
<ImageView
android:id="@+id/settingsImageSwitcher"
android:id="@+id/settingsPresenceSwitcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/discord_rich_presence"

View File

@@ -292,6 +292,69 @@
android:textSize="14sp"
tools:ignore="LabelFor,TextContrastCheck,DuplicateSpeakableTextCheck" />
</com.google.android.material.textfield.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/custom_anime_list" />
<LinearLayout
android:id="@+id/animeCustomListsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:orientation="vertical" />
<ImageButton
android:id="@+id/addAnimeListButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/custom_anime_list"
android:layout_marginHorizontal="36dp"
android:layout_marginVertical="12dp"
android:src="@drawable/ic_circle_add"
android:background="?android:attr/selectableItemBackground"
app:tint="?attr/colorPrimary"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:alpha="0.58"
android:fontFamily="@font/poppins_bold"
android:text="@string/custom_manga_list" />
<LinearLayout
android:id="@+id/mangaCustomListsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:orientation="vertical" />
<ImageButton
android:id="@+id/addMangaListButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/custom_manga_list"
android:layout_marginHorizontal="36dp"
android:layout_marginVertical="12dp"
android:src="@drawable/ic_circle_add"
android:background="?android:attr/selectableItemBackground"
app:tint="?attr/colorPrimary"/>
<Button
android:id="@+id/SettingsAnilistCustomListSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginHorizontal="32dp"
android:layout_gravity="end"
android:fontFamily="@font/poppins_bold"
android:text="@string/save" />
</ani.dantotsu.others.Xpandable>
<TextView

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
style="?attr/textInputFilledStyle"
android:id="@+id/customListItem"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/colorSurface"
app:boxStrokeColor="?attr/colorPrimaryContainer"
app:endIconMode ="custom"
app:endIconDrawable ="@drawable/ic_round_delete_24">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View File

@@ -66,7 +66,7 @@
<string name="empty">All caught up, when New?</string>
<string name="action_settings">Settings</string>
<string name="add">Add to List</string>
<string name="add_list">Add to List</string>
<string name="list_editor">List Editor</string>
<string name="add_fav">Add to Favourites</string>
<string name="notifications">Notifications</string>
@@ -337,6 +337,10 @@
<string name="selected_score_format">Scoring System</string>
<string name="selected_time_zone">Timezone</string>
<string name="selected_row_order">Default List Order</string>
<string name="custom_anime_list">Custom Anime Lists</string>
<string name="custom_manga_list">Custom Manga Lists</string>
<string name="delete_custom_list">Delete Custom List</string>
<string name="delete_custom_list_confirm">Are you sure you want to delete %1$s ?</string>
<string name="keep_screen_on">Keep Screen On</string>
<string name="layout">Layout</string>
<string name="spaced_pages">Spaced Pages</string>