package ani.dantotsu.settings import android.annotation.SuppressLint import android.app.AlertDialog import android.content.Intent import android.graphics.drawable.Animatable import android.os.Build.BRAND import android.os.Build.DEVICE import android.os.Build.SUPPORTED_ABIS import android.os.Build.VERSION.CODENAME import android.os.Build.VERSION.RELEASE import android.os.Build.VERSION.SDK_INT import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.AnimationUtils import android.view.inputmethod.EditorInfo import android.widget.ArrayAdapter import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.OptIn import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.view.updateLayoutParams import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.lifecycleScope import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.offline.DownloadService import ani.dantotsu.BuildConfig import ani.dantotsu.R import ani.dantotsu.Refresh import ani.dantotsu.connections.anilist.Anilist import ani.dantotsu.connections.discord.Discord import ani.dantotsu.connections.mal.MAL import ani.dantotsu.copyToClipboard import ani.dantotsu.currContext import ani.dantotsu.databinding.ActivitySettingsBinding import ani.dantotsu.download.DownloadedType import ani.dantotsu.download.DownloadsManager import ani.dantotsu.download.video.ExoplayerDownloadService import ani.dantotsu.downloadsPermission import ani.dantotsu.initActivity import ani.dantotsu.loadImage import ani.dantotsu.logger import ani.dantotsu.navBarHeight import ani.dantotsu.openLinkInBrowser import ani.dantotsu.others.AppUpdater import ani.dantotsu.others.CustomBottomDialog import ani.dantotsu.pop import ani.dantotsu.savePrefsToDownloads import ani.dantotsu.setSafeOnClickListener import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.settings.saving.internal.Location import ani.dantotsu.settings.saving.internal.PreferenceKeystore import ani.dantotsu.settings.saving.internal.PreferencePackager import ani.dantotsu.snackString import ani.dantotsu.startMainActivity import ani.dantotsu.statusBarHeight import ani.dantotsu.subcriptions.Notifications import ani.dantotsu.subcriptions.Notifications.Companion.openSettings import ani.dantotsu.subcriptions.Subscription.Companion.defaultTime import ani.dantotsu.subcriptions.Subscription.Companion.startSubscription import ani.dantotsu.subcriptions.Subscription.Companion.timeMinutes import ani.dantotsu.themes.ThemeManager import ani.dantotsu.toast import com.google.android.material.snackbar.Snackbar import com.google.android.material.textfield.TextInputEditText import eltos.simpledialogfragment.SimpleDialog import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE import eltos.simpledialogfragment.color.SimpleColorDialog import eu.kanade.domain.base.BasePreferences import io.noties.markwon.Markwon import io.noties.markwon.SoftBreakAddsNewLinePlugin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import kotlin.random.Random class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListener { private val restartMainActivity = object : OnBackPressedCallback(false) { override fun handleOnBackPressed() = startMainActivity(this@SettingsActivity) } lateinit var binding: ActivitySettingsBinding private val extensionInstaller = Injekt.get().extensionInstaller() private var cursedCounter = 0 @OptIn(UnstableApi::class) @SuppressLint("SetTextI18n", "Recycle") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ThemeManager(this).applyTheme() binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) initActivity(this) val openDocumentLauncher = registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri -> if (uri != null) { try { val jsonString = contentResolver.openInputStream(uri)?.readBytes() ?: throw Exception("Error reading file") val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings" //.sani is encrypted, .ani is not if (name.endsWith(".sani")) { passwordAlertDialog(false) { password -> if (password != null) { val salt = jsonString.copyOfRange(0, 16) val encrypted = jsonString.copyOfRange(16, jsonString.size) val decryptedJson = try { PreferenceKeystore.decryptWithPassword( password, encrypted, salt ) } catch (e: Exception) { toast("Incorrect password") return@passwordAlertDialog } if (PreferencePackager.unpack(decryptedJson)) restartApp() } else { toast("Password cannot be empty") } } } else if (name.endsWith(".ani")) { val decryptedJson = jsonString.toString(Charsets.UTF_8) if (PreferencePackager.unpack(decryptedJson)) restartApp() } else { toast("Unknown file type") } } catch (e: Exception) { e.printStackTrace() toast("Error importing settings") } } } binding.settingsVersion.text = getString(R.string.version_current, BuildConfig.VERSION_NAME) binding.settingsVersion.setOnLongClickListener { copyToClipboard(getDeviceInfo(), false) toast(getString(R.string.copied_device_info)) return@setOnLongClickListener true } binding.settingsContainer.updateLayoutParams { topMargin = statusBarHeight bottomMargin = navBarHeight } onBackPressedDispatcher.addCallback(this, restartMainActivity) binding.settingsBack.setOnClickListener { onBackPressedDispatcher.onBackPressed() } binding.settingsUseMaterialYou.isChecked = PrefManager.getVal(PrefName.UseMaterialYou) binding.settingsUseMaterialYou.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.UseMaterialYou, isChecked) if (isChecked) binding.settingsUseCustomTheme.isChecked = false restartApp() } binding.settingsUseCustomTheme.isChecked = PrefManager.getVal(PrefName.UseCustomTheme) binding.settingsUseCustomTheme.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.UseCustomTheme, isChecked) if (isChecked) { binding.settingsUseMaterialYou.isChecked = false } restartApp() } binding.settingsUseSourceTheme.isChecked = PrefManager.getVal(PrefName.UseSourceTheme) binding.settingsUseSourceTheme.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.UseSourceTheme, isChecked) restartApp() } binding.settingsUseOLED.isChecked = PrefManager.getVal(PrefName.UseOLED) binding.settingsUseOLED.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.UseOLED, isChecked) restartApp() } val themeString: String = PrefManager.getVal(PrefName.Theme) binding.themeSwitcher.setText( themeString.substring(0, 1) + themeString.substring(1).lowercase() ) binding.themeSwitcher.setAdapter( ArrayAdapter( this, R.layout.item_dropdown, ThemeManager.Companion.Theme.entries .map { it.theme.substring(0, 1) + it.theme.substring(1).lowercase() }) ) binding.themeSwitcher.setOnItemClickListener { _, _, i, _ -> PrefManager.setVal(PrefName.Theme, ThemeManager.Companion.Theme.entries[i].theme) //ActivityHelper.shouldRefreshMainActivity = true binding.themeSwitcher.clearFocus() restartApp() } binding.customTheme.setOnClickListener { val originalColor: Int = PrefManager.getVal(PrefName.CustomThemeInt) class CustomColorDialog : SimpleColorDialog() { //idk where to put it override fun onPositiveButtonClick() { restartApp() super.onPositiveButtonClick() } } val tag = "colorPicker" CustomColorDialog().title("Custom Theme") .colorPreset(originalColor) .colors(this, SimpleColorDialog.MATERIAL_COLOR_PALLET) .allowCustom(true) .showOutline(0x46000000) .gridNumColumn(5) .choiceMode(SimpleColorDialog.SINGLE_CHOICE) .neg() .show(this, tag) } binding.settingsPlayer.setOnClickListener { startActivity(Intent(this, PlayerSettingsActivity::class.java)) } val managers = arrayOf("Default", "1DM", "ADM") val downloadManagerDialog = AlertDialog.Builder(this, R.style.MyPopup).setTitle("Download Manager") var downloadManager: Int = PrefManager.getVal(PrefName.DownloadManager) binding.settingsDownloadManager.setOnClickListener { val dialog = downloadManagerDialog.setSingleChoiceItems( managers, downloadManager ) { dialog, count -> downloadManager = count PrefManager.setVal(PrefName.DownloadManager, downloadManager) dialog.dismiss() }.show() dialog.window?.setDimAmount(0.8f) } binding.importExportSettings.setOnClickListener { downloadsPermission(this) val selectedArray = mutableListOf(false) val filteredLocations = Location.entries.filter { it.exportable } selectedArray.addAll(List(filteredLocations.size - 1) { false }) val dialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Import/Export Settings") .setMultiChoiceItems( filteredLocations.map { it.name }.toTypedArray(), selectedArray.toBooleanArray() ) { _, which, isChecked -> selectedArray[which] = isChecked } .setPositiveButton("Import...") { dialog, _ -> openDocumentLauncher.launch(arrayOf("*/*")) dialog.dismiss() } .setNegativeButton("Export...") { dialog, _ -> if (!selectedArray.contains(true)) { toast("No location selected") return@setNegativeButton } dialog.dismiss() val selected = filteredLocations.filterIndexed { index, _ -> selectedArray[index] } if (selected.contains(Location.Protected)) { passwordAlertDialog(true) { password -> if (password != null) { savePrefsToDownloads( "DantotsuSettings", PrefManager.exportAllPrefs(selected), this@SettingsActivity, password ) } else { toast("Password cannot be empty") } } } else { savePrefsToDownloads( "DantotsuSettings", PrefManager.exportAllPrefs(selected), this@SettingsActivity, null ) } } .setNeutralButton("Cancel") { dialog, _ -> dialog.dismiss() } .create() dialog.window?.setDimAmount(0.8f) dialog.show() } binding.purgeAnimeDownloads.setOnClickListener { val dialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Purge Anime Downloads") .setMessage("Are you sure you want to purge all anime downloads?") .setPositiveButton("Yes") { dialog, _ -> val downloadsManager = Injekt.get() downloadsManager.purgeDownloads(DownloadedType.Type.ANIME) DownloadService.sendRemoveAllDownloads( this, ExoplayerDownloadService::class.java, false ) dialog.dismiss() } .setNegativeButton("No") { dialog, _ -> dialog.dismiss() } .create() dialog.window?.setDimAmount(0.8f) dialog.show() } binding.purgeMangaDownloads.setOnClickListener { val dialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Purge Manga Downloads") .setMessage("Are you sure you want to purge all manga downloads?") .setPositiveButton("Yes") { dialog, _ -> val downloadsManager = Injekt.get() downloadsManager.purgeDownloads(DownloadedType.Type.MANGA) dialog.dismiss() } .setNegativeButton("No") { dialog, _ -> dialog.dismiss() } .create() dialog.window?.setDimAmount(0.8f) dialog.show() } binding.purgeNovelDownloads.setOnClickListener { val dialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Purge Novel Downloads") .setMessage("Are you sure you want to purge all novel downloads?") .setPositiveButton("Yes") { dialog, _ -> val downloadsManager = Injekt.get() downloadsManager.purgeDownloads(DownloadedType.Type.NOVEL) dialog.dismiss() } .setNegativeButton("No") { dialog, _ -> dialog.dismiss() } .create() dialog.window?.setDimAmount(0.8f) dialog.show() } binding.settingsForceLegacyInstall.isChecked = extensionInstaller.get() == BasePreferences.ExtensionInstaller.LEGACY binding.settingsForceLegacyInstall.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { extensionInstaller.set(BasePreferences.ExtensionInstaller.LEGACY) } else { extensionInstaller.set(BasePreferences.ExtensionInstaller.PACKAGEINSTALLER) } } binding.skipExtensionIcons.isChecked = PrefManager.getVal(PrefName.SkipExtensionIcons) binding.skipExtensionIcons.setOnCheckedChangeListener { _, isChecked -> PrefManager.getVal(PrefName.SkipExtensionIcons, isChecked) } binding.NSFWExtension.isChecked = PrefManager.getVal(PrefName.NSFWExtension) binding.NSFWExtension.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.NSFWExtension, isChecked) } binding.userAgent.setOnClickListener { val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null) val editText = dialogView.findViewById(R.id.userAgentTextBox) editText.setText(PrefManager.getVal(PrefName.DefaultUserAgent)) val alertDialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("User Agent") .setView(dialogView) .setPositiveButton("OK") { dialog, _ -> PrefManager.setVal(PrefName.DefaultUserAgent, editText.text.toString()) dialog.dismiss() } .setNeutralButton("Reset") { dialog, _ -> PrefManager.removeVal(PrefName.DefaultUserAgent) editText.setText("") dialog.dismiss() } .setNegativeButton("Cancel") { dialog, _ -> dialog.dismiss() } .create() alertDialog.show() alertDialog.window?.setDimAmount(0.8f) } val exDns = listOf( "None", "Cloudflare", "Google", "AdGuard", "Quad9", "AliDNS", "DNSPod", "360", "Quad101", "Mullvad", "Controld", "Njalla", "Shecan", "Libre" ) binding.settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)]) binding.settingsExtensionDns.setAdapter(ArrayAdapter(this, R.layout.item_dropdown, exDns)) binding.settingsExtensionDns.setOnItemClickListener { _, _, i, _ -> PrefManager.setVal(PrefName.DohProvider, i) binding.settingsExtensionDns.clearFocus() Toast.makeText(this, "Restart app to apply changes", Toast.LENGTH_LONG).show() } binding.settingsDownloadInSd.isChecked = PrefManager.getVal(PrefName.SdDl) binding.settingsDownloadInSd.setOnCheckedChangeListener { _, isChecked -> if (isChecked) { val arrayOfFiles = ContextCompat.getExternalFilesDirs(this, null) if (arrayOfFiles.size > 1 && arrayOfFiles[1] != null) { PrefManager.setVal(PrefName.SdDl, true) } else { binding.settingsDownloadInSd.isChecked = false PrefManager.setVal(PrefName.SdDl, true) snackString(getString(R.string.noSdFound)) } } else PrefManager.setVal(PrefName.SdDl, true) } binding.settingsContinueMedia.isChecked = PrefManager.getVal(PrefName.ContinueMedia) binding.settingsContinueMedia.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.ContinueMedia, isChecked) } binding.settingsRecentlyListOnly.isChecked = PrefManager.getVal(PrefName.RecentlyListOnly) binding.settingsRecentlyListOnly.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.RecentlyListOnly, isChecked) } binding.settingsPreferDub.isChecked = PrefManager.getVal(PrefName.SettingsPreferDub) binding.settingsPreferDub.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.SettingsPreferDub, isChecked) } binding.settingsReader.setOnClickListener { startActivity(Intent(this, ReaderSettingsActivity::class.java)) } var previous: View = when (PrefManager.getVal(PrefName.DarkMode)) { 0 -> binding.settingsUiAuto 1 -> binding.settingsUiLight 2 -> binding.settingsUiDark else -> binding.settingsUiAuto } previous.alpha = 1f fun uiTheme(mode: Int, current: View) { previous.alpha = 0.33f previous = current current.alpha = 1f PrefManager.setVal(PrefName.DarkMode, mode) Refresh.all() finish() startActivity(Intent(this, SettingsActivity::class.java)) initActivity(this) } binding.settingsUiAuto.setOnClickListener { uiTheme(0, it) } binding.settingsUiLight.setOnClickListener { binding.settingsUseOLED.isChecked = false uiTheme(1, it) } binding.settingsUiDark.setOnClickListener { uiTheme(2, it) } var previousStart: View = when (PrefManager.getVal(PrefName.DefaultStartUpTab)) { 0 -> binding.uiSettingsAnime 1 -> binding.uiSettingsHome 2 -> binding.uiSettingsManga else -> binding.uiSettingsHome } previousStart.alpha = 1f fun uiDefault(mode: Int, current: View) { previousStart.alpha = 0.33f previousStart = current current.alpha = 1f PrefManager.setVal(PrefName.DefaultStartUpTab, mode) initActivity(this) } binding.uiSettingsAnime.setOnClickListener { uiDefault(0, it) } binding.uiSettingsHome.setOnClickListener { uiDefault(1, it) } binding.uiSettingsManga.setOnClickListener { uiDefault(2, it) } binding.settingsShowYt.isChecked = PrefManager.getVal(PrefName.ShowYtButton) binding.settingsShowYt.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.ShowYtButton, isChecked) } var previousEp: View = when (PrefManager.getVal(PrefName.AnimeDefaultView)) { 0 -> binding.settingsEpList 1 -> binding.settingsEpGrid 2 -> binding.settingsEpCompact else -> binding.settingsEpList } previousEp.alpha = 1f fun uiEp(mode: Int, current: View) { previousEp.alpha = 0.33f previousEp = current current.alpha = 1f PrefManager.setVal(PrefName.AnimeDefaultView, mode) } binding.settingsEpList.setOnClickListener { uiEp(0, it) } binding.settingsEpGrid.setOnClickListener { uiEp(1, it) } binding.settingsEpCompact.setOnClickListener { uiEp(2, it) } var previousChp: View = when (PrefManager.getVal(PrefName.MangaDefaultView)) { 0 -> binding.settingsChpList 1 -> binding.settingsChpCompact else -> binding.settingsChpList } previousChp.alpha = 1f fun uiChp(mode: Int, current: View) { previousChp.alpha = 0.33f previousChp = current current.alpha = 1f PrefManager.setVal(PrefName.MangaDefaultView, mode) } binding.settingsChpList.setOnClickListener { uiChp(0, it) } binding.settingsChpCompact.setOnClickListener { uiChp(1, it) } binding.settingBuyMeCoffee.setOnClickListener { lifecycleScope.launch { it.pop() } openLinkInBrowser("https://www.buymeacoffee.com/rebelonion") } lifecycleScope.launch { binding.settingBuyMeCoffee.pop() } binding.loginDiscord.setOnClickListener { openLinkInBrowser(getString(R.string.discord)) } binding.loginGithub.setOnClickListener { openLinkInBrowser(getString(R.string.github)) } binding.loginTelegram.setOnClickListener { openLinkInBrowser(getString(R.string.telegram)) } binding.settingsUi.setOnClickListener { startActivity(Intent(this, UserInterfaceSettingsActivity::class.java)) } binding.settingsFAQ.setOnClickListener { startActivity(Intent(this, FAQActivity::class.java)) } (binding.settingsLogo.drawable as Animatable).start() val array = resources.getStringArray(R.array.tips) binding.settingsLogo.setSafeOnClickListener { cursedCounter++ (binding.settingsLogo.drawable as Animatable).start() if (cursedCounter % 7 == 0) { Toast.makeText(this, "youwu have been cuwsed :pwayge:", Toast.LENGTH_LONG).show() val url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" openLinkInBrowser(url) //PrefManager.setVal(PrefName.SomethingSpecial, !PrefManager.getVal(PrefName.SomethingSpecial, false)) } else { snackString(array[(Math.random() * array.size).toInt()], this) } } binding.settingsDev.setOnClickListener { DevelopersDialogFragment().show(supportFragmentManager, "dialog") } binding.settingsForks.setOnClickListener { ForksDialogFragment().show(supportFragmentManager, "dialog") } binding.settingsDisclaimer.setOnClickListener { val title = getString(R.string.disclaimer) val text = TextView(this) text.setText(R.string.full_disclaimer) CustomBottomDialog.newInstance().apply { setTitleText(title) addView(text) setNegativeButton(currContext()!!.getString(R.string.close)) { dismiss() } show(supportFragmentManager, "dialog") } } var curTime = PrefManager.getVal(PrefName.SubscriptionsTimeS, defaultTime) val timeNames = timeMinutes.map { val mins = it % 60 val hours = it / 60 if (it > 0) "${if (hours > 0) "$hours hrs " else ""}${if (mins > 0) "$mins mins" else ""}" else getString(R.string.do_not_update) }.toTypedArray() binding.settingsSubscriptionsTime.text = getString(R.string.subscriptions_checking_time_s, timeNames[curTime]) val speedDialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle(R.string.subscriptions_checking_time) binding.settingsSubscriptionsTime.setOnClickListener { val dialog = speedDialog.setSingleChoiceItems(timeNames, curTime) { dialog, i -> curTime = i binding.settingsSubscriptionsTime.text = getString(R.string.subscriptions_checking_time_s, timeNames[i]) PrefManager.setVal(PrefName.SubscriptionsTimeS, curTime) dialog.dismiss() startSubscription(true) }.show() dialog.window?.setDimAmount(0.8f) } binding.settingsSubscriptionsTime.setOnLongClickListener { startSubscription(true) true } binding.settingsNotificationsCheckingSubscriptions.isChecked = PrefManager.getVal(PrefName.SubscriptionCheckingNotifications) binding.settingsNotificationsCheckingSubscriptions.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.SubscriptionCheckingNotifications, isChecked) if (isChecked) Notifications.createChannel( this, null, "subscription_checking", getString(R.string.checking_subscriptions), false ) else Notifications.deleteChannel(this, "subscription_checking") } binding.settingsNotificationsCheckingSubscriptions.setOnLongClickListener { openSettings(this, null) } if (!BuildConfig.FLAVOR.contains("fdroid")) { binding.settingsLogo.setOnLongClickListener { lifecycleScope.launch(Dispatchers.IO) { AppUpdater.check(this@SettingsActivity, true) } true } binding.settingsCheckUpdate.isChecked = PrefManager.getVal(PrefName.CheckUpdate) binding.settingsCheckUpdate.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.CheckUpdate, isChecked) if (!isChecked) { snackString(getString(R.string.long_click_to_check_update)) } } binding.settingsCheckUpdate.setOnLongClickListener { lifecycleScope.launch(Dispatchers.IO) { AppUpdater.check(this@SettingsActivity, true) } true } binding.settingsShareUsername.isChecked = PrefManager.getVal(PrefName.SharedUserID) binding.settingsShareUsername.setOnCheckedChangeListener { _, isChecked -> PrefManager.setVal(PrefName.SharedUserID, isChecked) } } else { binding.settingsCheckUpdate.visibility = View.GONE binding.settingsShareUsername.visibility = View.GONE binding.settingsCheckUpdate.isEnabled = false binding.settingsShareUsername.isEnabled = false binding.settingsCheckUpdate.isChecked = false binding.settingsShareUsername.isChecked = false } binding.settingsAccountHelp.setOnClickListener { val title = getString(R.string.account_help) val full = getString(R.string.full_account_help) CustomBottomDialog.newInstance().apply { setTitleText(title) addView( TextView(it.context).apply { val markWon = Markwon.builder(it.context) .usePlugin(SoftBreakAddsNewLinePlugin.create()).build() markWon.setMarkdown(this, full) } ) }.show(supportFragmentManager, "dialog") } fun reload() { if (Anilist.token != null) { binding.settingsAnilistLogin.setText(R.string.logout) binding.settingsAnilistLogin.setOnClickListener { Anilist.removeSavedToken() restartMainActivity.isEnabled = true reload() } binding.settingsAnilistUsername.visibility = View.VISIBLE binding.settingsAnilistUsername.text = Anilist.username binding.settingsAnilistAvatar.loadImage(Anilist.avatar) binding.settingsMALLoginRequired.visibility = View.GONE binding.settingsMALLogin.visibility = View.VISIBLE binding.settingsMALUsername.visibility = View.VISIBLE if (MAL.token != null) { binding.settingsMALLogin.setText(R.string.logout) binding.settingsMALLogin.setOnClickListener { MAL.removeSavedToken(it.context) restartMainActivity.isEnabled = true reload() } binding.settingsMALUsername.visibility = View.VISIBLE binding.settingsMALUsername.text = MAL.username binding.settingsMALAvatar.loadImage(MAL.avatar) } else { binding.settingsMALAvatar.setImageResource(R.drawable.ic_round_person_24) binding.settingsMALUsername.visibility = View.GONE binding.settingsMALLogin.setText(R.string.login) binding.settingsMALLogin.setOnClickListener { MAL.loginIntent(this) } } } else { binding.settingsAnilistAvatar.setImageResource(R.drawable.ic_round_person_24) binding.settingsAnilistUsername.visibility = View.GONE binding.settingsAnilistLogin.setText(R.string.login) binding.settingsAnilistLogin.setOnClickListener { Anilist.loginIntent(this) } binding.settingsMALLoginRequired.visibility = View.VISIBLE binding.settingsMALLogin.visibility = View.GONE binding.settingsMALUsername.visibility = View.GONE } if (Discord.token != null) { val id = PrefManager.getVal(PrefName.DiscordId, null as String?) val avatar = PrefManager.getVal(PrefName.DiscordAvatar, null as String?) val username = PrefManager.getVal(PrefName.DiscordUserName, null as String?) if (id != null && avatar != null) { binding.settingsDiscordAvatar.loadImage("https://cdn.discordapp.com/avatars/$id/$avatar.png") } binding.settingsDiscordUsername.visibility = View.VISIBLE binding.settingsDiscordUsername.text = username ?: Discord.token?.replace(Regex("."), "*") binding.settingsDiscordLogin.setText(R.string.logout) binding.settingsDiscordLogin.setOnClickListener { Discord.removeSavedToken(this) restartMainActivity.isEnabled = true reload() } binding.imageSwitcher.visibility = View.VISIBLE var initialStatus = when (PrefManager.getVal(PrefName.DiscordStatus)) { "online" -> R.drawable.discord_status_online "idle" -> R.drawable.discord_status_idle "dnd" -> R.drawable.discord_status_dnd else -> R.drawable.discord_status_online } binding.imageSwitcher.setImageResource(initialStatus) val zoomInAnimation = AnimationUtils.loadAnimation(this, R.anim.bounce_zoom) binding.imageSwitcher.setOnClickListener { var status = "online" initialStatus = when (initialStatus) { R.drawable.discord_status_online -> { status = "idle" R.drawable.discord_status_idle } R.drawable.discord_status_idle -> { status = "dnd" R.drawable.discord_status_dnd } R.drawable.discord_status_dnd -> { status = "online" R.drawable.discord_status_online } else -> R.drawable.discord_status_online } PrefManager.setVal(PrefName.DiscordStatus, status) binding.imageSwitcher.setImageResource(initialStatus) binding.imageSwitcher.startAnimation(zoomInAnimation) } } else { binding.imageSwitcher.visibility = View.GONE binding.settingsDiscordAvatar.setImageResource(R.drawable.ic_round_person_24) binding.settingsDiscordUsername.visibility = View.GONE binding.settingsDiscordLogin.setText(R.string.login) binding.settingsDiscordLogin.setOnClickListener { Discord.warning(this).show(supportFragmentManager, "dialog") } } } reload() lifecycleScope.launch(Dispatchers.IO) { delay(2000) runOnUiThread { if (Random.nextInt(0, 100) > 69) { CustomBottomDialog.newInstance().apply { title = "Enjoying the App?" addView(TextView(this@SettingsActivity).apply { text = "Consider donating!" }) setNegativeButton("no moners :(") { snackString("That's alright, you'll be a rich man soon :prayge:") dismiss() } setPositiveButton("denote :)") { binding.settingBuyMeCoffee.performClick() dismiss() } show(supportFragmentManager, "dialog") } } } } } override fun onResult(dialogTag: String, which: Int, extras: Bundle): Boolean { if (which == BUTTON_POSITIVE) { if (dialogTag == "colorPicker") { val color = extras.getInt(SimpleColorDialog.COLOR) PrefManager.setVal(PrefName.CustomThemeInt, color) logger("Custom Theme: $color") } } return true } private fun restartApp() { Snackbar.make( binding.root, R.string.restart_app, Snackbar.LENGTH_SHORT ).apply { val mainIntent = Intent.makeRestartActivityTask( context.packageManager.getLaunchIntentForPackage( context.packageName )!!.component ) setAction("Do it!") { context.startActivity(mainIntent) Runtime.getRuntime().exit(0) } show() } } private fun passwordAlertDialog(isExporting: Boolean, callback: (CharArray?) -> Unit) { val password = CharArray(16).apply { fill('0') } // Inflate the dialog layout val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog_user_agent, null) val box = dialogView.findViewById(R.id.userAgentTextBox) box?.hint = "Password" box?.setSingleLine() val dialog = AlertDialog.Builder(this, R.style.MyPopup) .setTitle("Enter Password") .setView(dialogView) .setPositiveButton("OK", null) .setNegativeButton("Cancel") { dialog, _ -> password.fill('0') dialog.dismiss() callback(null) } .create() fun handleOkAction() { val editText = dialog.findViewById(R.id.userAgentTextBox) if (editText?.text?.isNotBlank() == true) { editText.text?.toString()?.trim()?.toCharArray(password) dialog.dismiss() callback(password) } else { toast("Password cannot be empty") } } box?.setOnEditorActionListener { _, actionId, _ -> if (actionId == EditorInfo.IME_ACTION_DONE) { handleOkAction() true } else { false } } val subtitleTextView = dialogView.findViewById(R.id.subtitle) subtitleTextView?.visibility = View.VISIBLE if (!isExporting) subtitleTextView?.text = "Enter your password to decrypt the file" dialog.window?.setDimAmount(0.8f) dialog.show() // Override the positive button here dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener { handleOkAction() } } companion object { fun getDeviceInfo(): String { return """ dantotsu Version: ${BuildConfig.VERSION_NAME} Device: $BRAND $DEVICE Architecture: ${getArch()} OS Version: $CODENAME $RELEASE ($SDK_INT) """.trimIndent() } private fun getArch(): String { SUPPORTED_ABIS.forEach { when (it) { "arm64-v8a" -> return "aarch64" "armeabi-v7a" -> return "arm" "x86_64" -> return "x86_64" "x86" -> return "i686" } } return System.getProperty("os.arch") ?: System.getProperty("os.product.cpu.abi") ?: "Unknown Architecture" } } }