feat(settings): fully fledged AniList settings (#453)
This commit is contained in:
parent
7366aa1bf2
commit
04b9b9e7ff
17 changed files with 1345 additions and 99 deletions
|
@ -0,0 +1,340 @@
|
|||
package ani.dantotsu.settings
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.anilist.Anilist.ScoreFormat
|
||||
import ani.dantotsu.connections.anilist.Anilist.activityMergeTimeMap
|
||||
import ani.dantotsu.connections.anilist.Anilist.rowOrderMap
|
||||
import ani.dantotsu.connections.anilist.Anilist.staffNameLang
|
||||
import ani.dantotsu.connections.anilist.Anilist.titleLang
|
||||
import ani.dantotsu.connections.anilist.AnilistMutations
|
||||
import ani.dantotsu.databinding.ActivitySettingsAnilistBinding
|
||||
import ani.dantotsu.initActivity
|
||||
import ani.dantotsu.navBarHeight
|
||||
import ani.dantotsu.restartApp
|
||||
import ani.dantotsu.statusBarHeight
|
||||
import ani.dantotsu.themes.ThemeManager
|
||||
import ani.dantotsu.toast
|
||||
import ani.dantotsu.util.customAlertDialog
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AnilistSettingsActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingsAnilistBinding
|
||||
private lateinit var anilistMutations: AnilistMutations
|
||||
|
||||
enum class FormatLang {
|
||||
ENGLISH,
|
||||
ROMAJI,
|
||||
NATIVE
|
||||
}
|
||||
|
||||
enum class FormatScore {
|
||||
POINT_100,
|
||||
POINT_10_DECIMAL,
|
||||
POINT_10,
|
||||
POINT_5,
|
||||
POINT_3,
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
ThemeManager(this).applyTheme()
|
||||
initActivity(this)
|
||||
val context = this
|
||||
binding = ActivitySettingsAnilistBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
anilistMutations = AnilistMutations()
|
||||
|
||||
binding.apply {
|
||||
settingsAnilistLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = statusBarHeight
|
||||
bottomMargin = navBarHeight
|
||||
}
|
||||
binding.anilistSettingsBack.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
val currentTitleLang = Anilist.titleLanguage
|
||||
val titleFormat = FormatLang.entries.firstOrNull { it.name == currentTitleLang } ?: FormatLang.ENGLISH
|
||||
|
||||
settingsAnilistTitleLanguage.setText(titleLang[titleFormat.ordinal])
|
||||
settingsAnilistTitleLanguage.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, titleLang)
|
||||
)
|
||||
settingsAnilistTitleLanguage.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedLanguage = when (i) {
|
||||
0 -> "ENGLISH"
|
||||
1 -> "ROMAJI"
|
||||
2 -> "NATIVE"
|
||||
else -> "ENGLISH"
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(titleLanguage = selectedLanguage)
|
||||
Anilist.titleLanguage = selectedLanguage
|
||||
restartApp()
|
||||
}
|
||||
settingsAnilistTitleLanguage.clearFocus()
|
||||
}
|
||||
|
||||
val currentStaffNameLang = Anilist.staffNameLanguage
|
||||
val staffNameFormat = FormatLang.entries.firstOrNull { it.name == currentStaffNameLang } ?: FormatLang.ENGLISH
|
||||
|
||||
settingsAnilistStaffLanguage.setText(staffNameLang[staffNameFormat.ordinal])
|
||||
settingsAnilistStaffLanguage.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, staffNameLang)
|
||||
)
|
||||
settingsAnilistStaffLanguage.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedLanguage = when (i) {
|
||||
0 -> "ENGLISH"
|
||||
1 -> "ROMAJI"
|
||||
2 -> "NATIVE"
|
||||
else -> "ENGLISH"
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(staffNameLanguage = selectedLanguage)
|
||||
Anilist.staffNameLanguage = selectedLanguage
|
||||
restartApp()
|
||||
}
|
||||
settingsAnilistStaffLanguage.clearFocus()
|
||||
}
|
||||
|
||||
val currentMergeTimeDisplay = activityMergeTimeMap.entries.firstOrNull { it.value == Anilist.activityMergeTime }?.key
|
||||
?: "${Anilist.activityMergeTime} mins"
|
||||
settingsAnilistActivityMergeTime.setText(currentMergeTimeDisplay)
|
||||
settingsAnilistActivityMergeTime.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, activityMergeTimeMap.keys.toList())
|
||||
)
|
||||
settingsAnilistActivityMergeTime.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedDisplayTime = activityMergeTimeMap.keys.toList()[i]
|
||||
val selectedApiTime = activityMergeTimeMap[selectedDisplayTime] ?: 0
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(activityMergeTime = selectedApiTime)
|
||||
Anilist.activityMergeTime = selectedApiTime
|
||||
restartApp()
|
||||
}
|
||||
settingsAnilistActivityMergeTime.clearFocus()
|
||||
}
|
||||
|
||||
val currentScoreFormat = Anilist.scoreFormat
|
||||
val scoreFormat = FormatScore.entries.firstOrNull{ it.name == currentScoreFormat } ?: FormatScore.POINT_100
|
||||
settingsAnilistScoreFormat.setText(ScoreFormat[scoreFormat.ordinal])
|
||||
settingsAnilistScoreFormat.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, ScoreFormat)
|
||||
)
|
||||
settingsAnilistScoreFormat.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedFormat = when (i) {
|
||||
0 -> "POINT_100"
|
||||
1 -> "POINT_10_DECIMAL"
|
||||
2 -> "POINT_10"
|
||||
3 -> "POINT_5"
|
||||
4 -> "POINT_3"
|
||||
else -> "POINT_100"
|
||||
}
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(scoreFormat = selectedFormat)
|
||||
Anilist.scoreFormat = selectedFormat
|
||||
restartApp()
|
||||
}
|
||||
settingsAnilistScoreFormat.clearFocus()
|
||||
}
|
||||
|
||||
val currentRowOrder = rowOrderMap.entries.firstOrNull { it.value == Anilist.rowOrder }?.key ?: "Score"
|
||||
settingsAnilistRowOrder.setText(currentRowOrder)
|
||||
settingsAnilistRowOrder.setAdapter(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, rowOrderMap.keys.toList())
|
||||
)
|
||||
settingsAnilistRowOrder.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedDisplayOrder = rowOrderMap.keys.toList()[i]
|
||||
val selectedApiOrder = rowOrderMap[selectedDisplayOrder] ?: "score"
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(rowOrder = selectedApiOrder)
|
||||
Anilist.rowOrder = selectedApiOrder
|
||||
restartApp()
|
||||
}
|
||||
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(
|
||||
ArrayAdapter(context, R.layout.item_dropdown, Anilist.timeZone)
|
||||
)
|
||||
settingsAnilistTimezone.setOnItemClickListener { _, _, i, _ ->
|
||||
val selectedTimezone = Anilist.timeZone[i]
|
||||
val apiTimezone = Anilist.getApiTimezone(selectedTimezone)
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(timezone = apiTimezone)
|
||||
Anilist.timezone = apiTimezone
|
||||
restartApp()
|
||||
}
|
||||
settingsAnilistTimezone.clearFocus()
|
||||
}
|
||||
|
||||
val displayAdultContent = Anilist.adult
|
||||
val airingNotifications = Anilist.airingNotifications
|
||||
|
||||
binding.settingsRecyclerView1.adapter = SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.airing_notifications),
|
||||
desc = getString(R.string.airing_notifications_desc),
|
||||
icon = R.drawable.ic_round_notifications_active_24,
|
||||
isChecked = airingNotifications,
|
||||
switch = { isChecked, _ ->
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(airingNotifications = isChecked)
|
||||
Anilist.airingNotifications = isChecked
|
||||
restartApp()
|
||||
}
|
||||
}
|
||||
),
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.display_adult_content),
|
||||
desc = getString(R.string.display_adult_content_desc),
|
||||
icon = R.drawable.ic_round_nsfw_24,
|
||||
isChecked = displayAdultContent,
|
||||
switch = { isChecked, _ ->
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(displayAdultContent = isChecked)
|
||||
Anilist.adult = isChecked
|
||||
restartApp()
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
binding.settingsRecyclerView1.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
}
|
||||
|
||||
binding.settingsRecyclerView2.adapter = SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 2,
|
||||
name = getString(R.string.restrict_messages),
|
||||
desc = getString(R.string.restrict_messages_desc),
|
||||
icon = R.drawable.ic_round_lock_open_24,
|
||||
isChecked = Anilist.restrictMessagesToFollowing,
|
||||
switch = { isChecked, _ ->
|
||||
lifecycleScope.launch {
|
||||
anilistMutations.updateSettings(restrictMessagesToFollowing = isChecked)
|
||||
Anilist.restrictMessagesToFollowing = isChecked
|
||||
restartApp()
|
||||
}
|
||||
}
|
||||
),
|
||||
)
|
||||
)
|
||||
binding.settingsRecyclerView2.layoutManager =
|
||||
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) {
|
||||
customAlertDialog().apply {
|
||||
setTitle(getString(R.string.delete_custom_list))
|
||||
setMessage(getString(R.string.delete_custom_list_confirm, name))
|
||||
setPosButton(getString(R.string.delete)) {
|
||||
deleteCustomList(name, isAnime)
|
||||
container.removeView(customListItemView)
|
||||
}
|
||||
setNegButton(getString(R.string.cancel))
|
||||
}.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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -321,7 +320,7 @@ class ExtensionsActivity : AppCompatActivity() {
|
|||
customAlertDialog().apply {
|
||||
setTitle(R.string.edit_repositories)
|
||||
setCustomView(dialogView.root)
|
||||
setPosButton(R.string.add) {
|
||||
setPosButton(R.string.add_list) {
|
||||
if (!dialogView.repositoryTextBox.text.isNullOrBlank()) {
|
||||
processUserInput(dialogView.repositoryTextBox.text.toString(), type)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ani.dantotsu.settings
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.View
|
||||
|
@ -9,6 +10,8 @@ import android.widget.TextView
|
|||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import ani.dantotsu.R
|
||||
import ani.dantotsu.connections.anilist.Anilist
|
||||
import ani.dantotsu.connections.discord.Discord
|
||||
|
@ -26,6 +29,7 @@ import ani.dantotsu.statusBarHeight
|
|||
import ani.dantotsu.themes.ThemeManager
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.SoftBreakAddsNewLinePlugin
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class SettingsAccountActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivitySettingsAccountsBinding
|
||||
|
@ -111,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)
|
||||
|
@ -142,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
|
||||
|
@ -150,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 -> {
|
||||
|
@ -181,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)
|
||||
|
@ -202,6 +207,25 @@ class SettingsAccountActivity : AppCompatActivity() {
|
|||
}
|
||||
reload()
|
||||
}
|
||||
}
|
||||
binding.settingsRecyclerView.adapter = SettingsAdapter(
|
||||
arrayListOf(
|
||||
Settings(
|
||||
type = 1,
|
||||
name = getString(R.string.anilist_settings),
|
||||
desc = getString(R.string.alsettings_desc),
|
||||
icon = R.drawable.ic_anilist,
|
||||
onClick = {
|
||||
lifecycleScope.launch {
|
||||
Anilist.query.getUserData()
|
||||
startActivity(Intent(context, AnilistSettingsActivity::class.java))
|
||||
}
|
||||
},
|
||||
isActivity = true
|
||||
),
|
||||
)
|
||||
)
|
||||
binding.settingsRecyclerView.layoutManager =
|
||||
LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue