diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt index 34b29ac5..766c6a97 100644 --- a/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt +++ b/app/src/main/java/ani/dantotsu/settings/SettingsActivity.kt @@ -23,22 +23,16 @@ import android.view.animation.AnimationUtils import android.view.inputmethod.EditorInfo import android.widget.ArrayAdapter import android.widget.EditText -import android.widget.RadioButton -import android.widget.RadioGroup import android.widget.TextView 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 android.view.HapticFeedbackConstants -import androidx.core.view.ViewCompat.performHapticFeedback import androidx.core.view.isVisible 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.connections.anilist.Anilist @@ -88,9 +82,7 @@ import ani.dantotsu.themes.ThemeManager import ani.dantotsu.toast import ani.dantotsu.util.LauncherWrapper import ani.dantotsu.util.Logger -import ani.dantotsu.util.StoragePermissions.Companion.accessAlertDialog import ani.dantotsu.util.StoragePermissions.Companion.downloadsPermission -import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.textfield.TextInputEditText import eltos.simpledialogfragment.SimpleDialog import eltos.simpledialogfragment.SimpleDialog.OnDialogResultListener.BUTTON_POSITIVE @@ -100,8 +92,8 @@ import eu.kanade.tachiyomi.extension.anime.AnimeExtensionManager import eu.kanade.tachiyomi.extension.manga.MangaExtensionManager import io.noties.markwon.Markwon import io.noties.markwon.SoftBreakAddsNewLinePlugin -import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay @@ -130,7 +122,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene private var cursedCounter = 0 private val animeExtensionManager: AnimeExtensionManager by injectLazy() private val mangaExtensionManager: MangaExtensionManager by injectLazy() - + @kotlin.OptIn(DelicateCoroutinesApi::class) @OptIn(UnstableApi::class) override fun onCreate(savedInstanceState: Bundle?) { @@ -233,7 +225,10 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene settingsAnilistAvatar.loadImage(Anilist.avatar) settingsAnilistAvatar.setOnClickListener { it.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) - val anilistLink = getString(R.string.anilist_link, PrefManager.getVal(PrefName.AnilistUserName)) + val anilistLink = getString( + R.string.anilist_link, + PrefManager.getVal(PrefName.AnilistUserName) + ) openLinkInBrowser(anilistLink) true } @@ -649,14 +644,16 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene 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>(PrefName.AnimeExtensionRepos).plus(entry) + val anime = + PrefManager.getVal>(PrefName.AnimeExtensionRepos).plus(entry) PrefManager.setVal(PrefName.AnimeExtensionRepos, anime) CoroutineScope(Dispatchers.IO).launch { animeExtensionManager.findAvailableExtensions() } } if (mediaType == MediaType.MANGA) { - val manga = PrefManager.getVal>(PrefName.MangaExtensionRepos).plus(entry) + val manga = + PrefManager.getVal>(PrefName.MangaExtensionRepos).plus(entry) PrefManager.setVal(PrefName.MangaExtensionRepos, manga) CoroutineScope(Dispatchers.IO).launch { mangaExtensionManager.findAvailableExtensions() @@ -669,7 +666,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene 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)) { + && keyEvent.keyCode == KeyEvent.KEYCODE_ENTER) + ) { processUserInput(textView.text.toString(), mediaType) dialog.dismiss() return@setOnEditorActionListener true @@ -681,9 +679,10 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene setExtensionOutput() animeAddRepository.setOnClickListener { val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null) - val editText = dialogView.findViewById(R.id.userAgentTextBox).apply { - hint = getString(R.string.anime_add_repository) - } + val editText = + dialogView.findViewById(R.id.userAgentTextBox).apply { + hint = getString(R.string.anime_add_repository) + } val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) .setTitle(R.string.anime_add_repository) .setView(dialogView) @@ -708,9 +707,10 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene mangaAddRepository.setOnClickListener { val dialogView = layoutInflater.inflate(R.layout.dialog_user_agent, null) - val editText = dialogView.findViewById(R.id.userAgentTextBox).apply { - hint = getString(R.string.manga_add_repository) - } + val editText = + dialogView.findViewById(R.id.userAgentTextBox).apply { + hint = getString(R.string.manga_add_repository) + } val alertDialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) .setTitle(R.string.manga_add_repository) .setView(dialogView) @@ -867,7 +867,13 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene } settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)]) - settingsExtensionDns.setAdapter(ArrayAdapter(this@SettingsActivity, R.layout.item_dropdown, exDns)) + settingsExtensionDns.setAdapter( + ArrayAdapter( + this@SettingsActivity, + R.layout.item_dropdown, + exDns + ) + ) settingsExtensionDns.setOnItemClickListener { _, _, i, _ -> PrefManager.setVal(PrefName.DohProvider, i) settingsExtensionDns.clearFocus() @@ -959,7 +965,12 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene } settingsUi.setOnClickListener { - startActivity(Intent(this@SettingsActivity, UserInterfaceSettingsActivity::class.java)) + startActivity( + Intent( + this@SettingsActivity, + UserInterfaceSettingsActivity::class.java + ) + ) } } @@ -983,7 +994,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene getString(R.string.subscriptions_checking_time_s, timeNames[i]) PrefManager.setVal(PrefName.SubscriptionNotificationInterval, curTime) dialog.dismiss() - TaskScheduler.create(this@SettingsActivity, + TaskScheduler.create( + this@SettingsActivity, PrefManager.getVal(PrefName.UseAlarmManager) ).scheduleAllTasks(this@SettingsActivity) }.show() @@ -991,7 +1003,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene } settingsSubscriptionsTime.setOnLongClickListener { - TaskScheduler.create(this@SettingsActivity, + TaskScheduler.create( + this@SettingsActivity, PrefManager.getVal(PrefName.UseAlarmManager) ).scheduleAllTasks(this@SettingsActivity) true @@ -1005,7 +1018,10 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene else getString(R.string.do_not_update) } settingsAnilistSubscriptionsTime.text = - getString(R.string.anilist_notifications_checking_time, aItems[PrefManager.getVal(PrefName.AnilistNotificationInterval)]) + getString( + R.string.anilist_notifications_checking_time, + aItems[PrefManager.getVal(PrefName.AnilistNotificationInterval)] + ) settingsAnilistSubscriptionsTime.setOnClickListener { val selected = PrefManager.getVal(PrefName.AnilistNotificationInterval) @@ -1016,7 +1032,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene settingsAnilistSubscriptionsTime.text = getString(R.string.anilist_notifications_checking_time, aItems[i]) dialog.dismiss() - TaskScheduler.create(this@SettingsActivity, + TaskScheduler.create( + this@SettingsActivity, PrefManager.getVal(PrefName.UseAlarmManager) ).scheduleAllTasks(this@SettingsActivity) } @@ -1027,7 +1044,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene settingsAnilistNotifications.setOnClickListener { val types = NotificationType.entries.map { it.name } - val filteredTypes = PrefManager.getVal>(PrefName.AnilistFilteredTypes).toMutableSet() + val filteredTypes = + PrefManager.getVal>(PrefName.AnilistFilteredTypes).toMutableSet() val selected = types.map { filteredTypes.contains(it) }.toBooleanArray() val dialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) .setTitle(R.string.anilist_notification_filters) @@ -1054,7 +1072,10 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene } settingsCommentSubscriptionsTime.text = - getString(R.string.comment_notification_checking_time, cItems[PrefManager.getVal(PrefName.CommentNotificationInterval)]) + getString( + R.string.comment_notification_checking_time, + cItems[PrefManager.getVal(PrefName.CommentNotificationInterval)] + ) settingsCommentSubscriptionsTime.setOnClickListener { val selected = PrefManager.getVal(PrefName.CommentNotificationInterval) val dialog = AlertDialog.Builder(this@SettingsActivity, R.style.MyPopup) @@ -1064,7 +1085,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene settingsCommentSubscriptionsTime.text = getString(R.string.comment_notification_checking_time, cItems[i]) dialog.dismiss() - TaskScheduler.create(this@SettingsActivity, + TaskScheduler.create( + this@SettingsActivity, PrefManager.getVal(PrefName.UseAlarmManager) ).scheduleAllTasks(this@SettingsActivity) } @@ -1095,7 +1117,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene PrefManager.setVal(PrefName.UseAlarmManager, true) if (SDK_INT >= Build.VERSION_CODES.S) { if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) { - val intent = Intent("android.settings.REQUEST_SCHEDULE_EXACT_ALARM") + val intent = + Intent("android.settings.REQUEST_SCHEDULE_EXACT_ALARM") startActivity(intent) settingsNotificationsCheckingSubscriptions.isChecked = true } @@ -1113,7 +1136,8 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene } else { PrefManager.setVal(PrefName.UseAlarmManager, false) TaskScheduler.create(this@SettingsActivity, true).cancelAllTasks() - TaskScheduler.create(this@SettingsActivity, false).scheduleAllTasks(this@SettingsActivity) + TaskScheduler.create(this@SettingsActivity, false) + .scheduleAllTasks(this@SettingsActivity) } } } @@ -1285,6 +1309,7 @@ class SettingsActivity : AppCompatActivity(), SimpleDialog.OnDialogResultListene callback(null) } .create() + fun handleOkAction() { val editText = dialog.findViewById(R.id.userAgentTextBox) if (editText?.text?.isNotBlank() == true) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/api/AnimeExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/api/AnimeExtensionGithubApi.kt index 6215daed..0c0fd25e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/anime/api/AnimeExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/anime/api/AnimeExtensionGithubApi.kt @@ -39,15 +39,33 @@ internal class AnimeExtensionGithubApi { val extensions: ArrayList = arrayListOf() - PrefManager.getVal>(PrefName.AnimeExtensionRepos).forEach { + val repos = + PrefManager.getVal>(PrefName.AnimeExtensionRepos).toMutableList() + if (repos.isEmpty()) { + repos.add("https://raw.githubusercontent.com/aniyomiorg/aniyomi-extensions/repo") + PrefManager.setVal(PrefName.AnimeExtensionRepos, repos.toSet()) + } + + repos.forEach { try { - val githubResponse = + val githubResponse = try { networkService.client .newCall(GET("${it}/index.min.json")) .awaitSuccess() + } catch (e: Throwable) { + Logger.log("Failed to get repo: $it") + Logger.log(e) + null + } + + val response = githubResponse ?: run { + networkService.client + .newCall(GET(fallbackRepoUrl(it) + "/index.min.json")) + .awaitSuccess() + } val repoExtensions = with(json) { - githubResponse + response .parseAs>() .toExtensions(it) } @@ -61,6 +79,7 @@ internal class AnimeExtensionGithubApi { extensions.addAll(repoExtensions) } catch (e: Throwable) { Logger.log("Failed to get extensions from GitHub") + Logger.log(e) } } @@ -146,6 +165,26 @@ internal class AnimeExtensionGithubApi { fun getApkUrl(extension: AnimeExtension.Available): String { return "${extension.repository}/apk/${extension.apkName}" } + + private fun fallbackRepoUrl(repoUrl: String): String? { + var fallbackRepoUrl = "https://gcore.jsdelivr.net/gh/" + val strippedRepoUrl = + repoUrl.removePrefix("https://").removePrefix("http://").removeSuffix("/") + val repoUrlParts = strippedRepoUrl.split("/") + if (repoUrlParts.size < 3) { + return null + } + val repoOwner = repoUrlParts[1] + val repoName = repoUrlParts[2] + fallbackRepoUrl += "$repoOwner/$repoName" + val repoBranch = if (repoUrlParts.size > 3) { + repoUrlParts[3] + } else { + "main" + } + fallbackRepoUrl += "@$repoBranch" + return fallbackRepoUrl + } } private fun AnimeExtensionJsonObject.extractLibVersion(): Double { diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/api/MangaExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/api/MangaExtensionGithubApi.kt index 39322259..8791f572 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/manga/api/MangaExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/manga/api/MangaExtensionGithubApi.kt @@ -39,16 +39,33 @@ internal class MangaExtensionGithubApi { val extensions: ArrayList = arrayListOf() + val repos = + PrefManager.getVal>(PrefName.MangaExtensionRepos).toMutableList() + if (repos.isEmpty()) { + repos.add("https://raw.githubusercontent.com/keiyoushi/extensions/main") + PrefManager.setVal(PrefName.MangaExtensionRepos, repos.toSet()) + } - PrefManager.getVal>(PrefName.MangaExtensionRepos).forEach { + repos.forEach { try { - val githubResponse = + val githubResponse = try { networkService.client .newCall(GET("${it}/index.min.json")) .awaitSuccess() + } catch (e: Throwable) { + Logger.log("Failed to get repo: $it") + Logger.log(e) + null + } + + val response = githubResponse ?: run { + networkService.client + .newCall(GET(fallbackRepoUrl(it) + "/index.min.json")) + .awaitSuccess() + } val repoExtensions = with(json) { - githubResponse + response .parseAs>() .toExtensions(it) } @@ -62,6 +79,7 @@ internal class MangaExtensionGithubApi { extensions.addAll(repoExtensions) } catch (e: Throwable) { Logger.log("Failed to get extensions from GitHub") + Logger.log(e) } } @@ -150,6 +168,26 @@ internal class MangaExtensionGithubApi { private fun ExtensionJsonObject.extractLibVersion(): Double { return version.substringBeforeLast('.').toDouble() } + + private fun fallbackRepoUrl(repoUrl: String): String? { + var fallbackRepoUrl = "https://gcore.jsdelivr.net/gh/" + val strippedRepoUrl = + repoUrl.removePrefix("https://").removePrefix("http://").removeSuffix("/") + val repoUrlParts = strippedRepoUrl.split("/") + if (repoUrlParts.size < 3) { + return null + } + val repoOwner = repoUrlParts[1] + val repoName = repoUrlParts[2] + fallbackRepoUrl += "$repoOwner/$repoName" + val repoBranch = if (repoUrlParts.size > 3) { + repoUrlParts[3] + } else { + "main" + } + fallbackRepoUrl += "@$repoBranch" + return fallbackRepoUrl + } } @Serializable