From 8a0224e6b0ede961fcb5c2412544979fbfa6e8ee Mon Sep 17 00:00:00 2001 From: rebelonion <87634197+rebelonion@users.noreply.github.com> Date: Wed, 1 May 2024 14:45:08 -0500 Subject: [PATCH] feat: crash report | various small fixes --- app/src/main/AndroidManifest.xml | 5 + app/src/main/java/ani/dantotsu/App.kt | 12 +- .../connections/anilist/AnilistQueries.kt | 2 +- .../connections/comments/CommentsAPI.kt | 50 +++++-- .../ani/dantotsu/media/MediaInfoFragment.kt | 3 +- .../dantotsu/media/MediaListViewActivity.kt | 20 +-- .../comment/CommentNotificationTask.kt | 2 +- .../java/ani/dantotsu/others/CrashActivity.kt | 30 +++++ app/src/main/java/ani/dantotsu/util/Logger.kt | 124 +++++++++++------- app/src/main/res/layout/activity_crash.xml | 75 +++++++++++ app/src/main/res/layout/fragment_offline.xml | 12 +- app/src/main/res/values/strings.xml | 3 + 12 files changed, 250 insertions(+), 88 deletions(-) create mode 100644 app/src/main/java/ani/dantotsu/others/CrashActivity.kt create mode 100644 app/src/main/res/layout/activity_crash.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ab023cfe..ef57b17a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -227,6 +227,11 @@ android:windowSoftInputMode="adjustResize|stateHidden" /> + (PrefName.UseAlarmManager) val scheduler = TaskScheduler.create(this@App, useAlarmManager) - scheduler.scheduleAllTasks(this@App) + try { + scheduler.scheduleAllTasks(this@App) + } catch (e: IllegalStateException) { + Logger.log("Failed to schedule tasks") + Logger.log(e) + } } } @@ -159,7 +165,7 @@ class App : MultiDexApplication() { } companion object { - private var instance: App? = null + var instance: App? = null /** Reference to the application context. * diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt index 41f9026b..e85b7815 100644 --- a/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt +++ b/app/src/main/java/ani/dantotsu/connections/anilist/AnilistQueries.kt @@ -77,7 +77,7 @@ class AnilistQueries { """{Media(id:${media.id}){id favourites popularity episodes chapters mediaListEntry{id status score(format:POINT_100)progress private notes repeat customLists updatedAt startedAt{year month day}completedAt{year month day}}isFavourite siteUrl idMal nextAiringEpisode{episode airingAt}source countryOfOrigin format duration season seasonYear startDate{year month day}endDate{year month day}genres studios(isMain:true){nodes{id name siteUrl}}description trailer{site id}synonyms tags{name rank isMediaSpoiler}characters(sort:[ROLE,FAVOURITES_DESC],perPage:25,page:1){edges{role voiceActors { id name { first middle last full native userPreferred } image { large medium } languageV2 } node{id image{medium}name{userPreferred}isFavourite}}}relations{edges{relationType(version:2)node{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}popularity meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}staffPreview:staff(perPage:8,sort:[RELEVANCE,ID]){edges{role node{id image{large medium}name{userPreferred}}}}recommendations(sort:RATING_DESC){nodes{mediaRecommendation{id idMal mediaListEntry{progress private score(format:POINT_100)status}episodes chapters nextAiringEpisode{episode}meanScore isAdult isFavourite format title{english romaji userPreferred}type status(version:2)bannerImage coverImage{large}}}}externalLinks{url site}}Page(page:1){pageInfo{total perPage currentPage lastPage hasNextPage}mediaList(isFollowing:true,sort:[STATUS],mediaId:${media.id}){id status score(format: POINT_100) progress progressVolumes user{id name avatar{large medium}}}}}""" runBlocking { val anilist = async { - var response = executeQuery(query, force = true, show = true) + var response = executeQuery(query, force = true) if (response != null) { fun parse() { val fetchedMedia = response?.data?.media ?: return diff --git a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt index ea62d728..ba18cb80 100644 --- a/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt +++ b/app/src/main/java/ani/dantotsu/connections/comments/CommentsAPI.kt @@ -1,10 +1,13 @@ package ani.dantotsu.connections.comments +import android.content.Context import ani.dantotsu.connections.anilist.Anilist +import ani.dantotsu.isOnline import ani.dantotsu.settings.saving.PrefManager import ani.dantotsu.settings.saving.PrefName import ani.dantotsu.snackString import ani.dantotsu.toast +import ani.dantotsu.util.Logger import com.lagradost.nicehttp.NiceResponse import com.lagradost.nicehttp.Requests import eu.kanade.tachiyomi.network.NetworkHelper @@ -25,6 +28,7 @@ import uy.kohesive.injekt.api.get object CommentsAPI { private const val ADDRESS: String = "https://1224665.xyz:443" + private var isOnline: Boolean = true var authToken: String? = null var userId: String? = null var isBanned: Boolean = false @@ -49,7 +53,8 @@ object CommentsAPI { val json = try { request.get(url) } catch (e: IOException) { - snackString("Failed to fetch comments") + Logger.log(e) + errorMessage("Failed to fetch comments") return null } if (!json.text.startsWith("{")) return null @@ -71,7 +76,8 @@ object CommentsAPI { val json = try { request.get(url) } catch (e: IOException) { - snackString("Failed to fetch comments") + Logger.log(e) + errorMessage("Failed to fetch comments") return null } if (!json.text.startsWith("{")) return null @@ -93,7 +99,8 @@ object CommentsAPI { val json = try { request.get(url) } catch (e: IOException) { - snackString("Failed to fetch comment") + Logger.log(e) + errorMessage("Failed to fetch comment") return null } if (!json.text.startsWith("{")) return null @@ -115,7 +122,8 @@ object CommentsAPI { val json = try { request.post(url) } catch (e: IOException) { - snackString("Failed to vote") + Logger.log(e) + errorMessage("Failed to vote") return false } val res = json.code == 200 @@ -141,7 +149,8 @@ object CommentsAPI { val json = try { request.post(url, requestBody = body.build()) } catch (e: IOException) { - snackString("Failed to comment") + Logger.log(e) + errorMessage("Failed to comment") return null } val res = json.code == 200 @@ -152,7 +161,8 @@ object CommentsAPI { val parsed = try { Json.decodeFromString(json.text) } catch (e: Exception) { - snackString("Failed to parse comment") + Logger.log(e) + errorMessage("Failed to parse comment") return null } return Comment( @@ -179,7 +189,8 @@ object CommentsAPI { val json = try { request.delete(url) } catch (e: IOException) { - snackString("Failed to delete comment") + Logger.log(e) + errorMessage("Failed to delete comment") return false } val res = json.code == 200 @@ -198,7 +209,8 @@ object CommentsAPI { val json = try { request.put(url, requestBody = body) } catch (e: IOException) { - snackString("Failed to edit comment") + Logger.log(e) + errorMessage("Failed to edit comment") return false } val res = json.code == 200 @@ -214,7 +226,8 @@ object CommentsAPI { val json = try { request.post(url) } catch (e: IOException) { - snackString("Failed to ban user") + Logger.log(e) + errorMessage("Failed to ban user") return false } val res = json.code == 200 @@ -241,7 +254,8 @@ object CommentsAPI { val json = try { request.post(url, requestBody = body) } catch (e: IOException) { - snackString("Failed to report comment") + Logger.log(e) + errorMessage("Failed to report comment") return false } val res = json.code == 200 @@ -296,7 +310,8 @@ object CommentsAPI { return null } - suspend fun fetchAuthToken(client: OkHttpClient? = null) { + suspend fun fetchAuthToken(context: Context, client: OkHttpClient? = null) { + isOnline = isOnline(context) if (authToken != null) return val MAX_RETRIES = 5 val tokenLifetime: Long = 1000 * 60 * 60 * 24 * 6 // 6 days @@ -325,7 +340,8 @@ object CommentsAPI { val parsed = try { Json.decodeFromString(json.text) } catch (e: Exception) { - snackString("Failed to login to comments API: ${e.printStackTrace()}") + Logger.log(e) + errorMessage("Failed to login to comments API: ${e.printStackTrace()}") return } PrefManager.setVal(PrefName.CommentAuthResponse, parsed) @@ -345,12 +361,18 @@ object CommentsAPI { return } } catch (e: IOException) { - snackString("Failed to login to comments API") + Logger.log(e) + errorMessage("Failed to login to comments API") return } kotlinx.coroutines.delay(60000) } - snackString("Failed to login after multiple attempts") + errorMessage("Failed to login after multiple attempts") + } + + private fun errorMessage(reason: String) { + Logger.log(reason) + if (isOnline) snackString(reason) } fun logout() { diff --git a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt index c9ecf6f9..7c2c5883 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaInfoFragment.kt @@ -38,6 +38,7 @@ import ani.dantotsu.databinding.ItemTitleSearchBinding import ani.dantotsu.databinding.ItemTitleTextBinding import ani.dantotsu.databinding.ItemTitleTrailerBinding import ani.dantotsu.displayTimer +import ani.dantotsu.isOnline import ani.dantotsu.loadImage import ani.dantotsu.navBarHeight import ani.dantotsu.profile.User @@ -80,7 +81,7 @@ class MediaInfoFragment : Fragment() { @SuppressLint("SetJavaScriptEnabled") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val model: MediaDetailsViewModel by activityViewModels() - val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode) + val offline: Boolean = PrefManager.getVal(PrefName.OfflineMode) || !isOnline(requireContext()) binding.mediaInfoProgressBar.isGone = loaded binding.mediaInfoContainer.isVisible = loaded binding.mediaInfoContainer.updateLayoutParams { bottomMargin += 128f.px + navBarHeight } diff --git a/app/src/main/java/ani/dantotsu/media/MediaListViewActivity.kt b/app/src/main/java/ani/dantotsu/media/MediaListViewActivity.kt index 2cbf7396..4e329603 100644 --- a/app/src/main/java/ani/dantotsu/media/MediaListViewActivity.kt +++ b/app/src/main/java/ani/dantotsu/media/MediaListViewActivity.kt @@ -27,16 +27,6 @@ class MediaListViewActivity: AppCompatActivity() { binding = ActivityMediaListViewBinding.inflate(layoutInflater) ThemeManager(this).applyTheme() initActivity(this) - setContentView(binding.root) - - val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface) - val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary) - val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline) - - window.statusBarColor = primaryColor - window.navigationBarColor = primaryColor - binding.listAppBar.setBackgroundColor(primaryColor) - binding.listTitle.setTextColor(primaryTextColor) if (!PrefManager.getVal(PrefName.ImmersiveMode)) { this.window.statusBarColor = ContextCompat.getColor(this, R.color.nav_bg_inv) @@ -50,6 +40,16 @@ class MediaListViewActivity: AppCompatActivity() { topMargin = statusBarHeight } } + setContentView(binding.root) + + val primaryColor = getThemeColor(com.google.android.material.R.attr.colorSurface) + val primaryTextColor = getThemeColor(com.google.android.material.R.attr.colorPrimary) + val secondaryTextColor = getThemeColor(com.google.android.material.R.attr.colorOutline) + + window.statusBarColor = primaryColor + window.navigationBarColor = primaryColor + binding.listAppBar.setBackgroundColor(primaryColor) + binding.listTitle.setTextColor(primaryTextColor) val screenWidth = resources.displayMetrics.run { widthPixels / density } binding.listTitle.text = intent.getStringExtra("title") binding.mediaRecyclerView.adapter = MediaAdaptor(0, mediaList, this) diff --git a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt index f6839558..1856ac71 100644 --- a/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt +++ b/app/src/main/java/ani/dantotsu/notifications/comment/CommentNotificationTask.kt @@ -30,7 +30,7 @@ class CommentNotificationTask : Task { withContext(Dispatchers.IO) { PrefManager.init(context) //make sure prefs are initialized val client = OkHttpClient() - CommentsAPI.fetchAuthToken(client) + CommentsAPI.fetchAuthToken(context, client) val notificationResponse = CommentsAPI.getNotifications(client) var notifications = notificationResponse?.notifications?.toMutableList() //if we have at least one reply notification, we need to fetch the media titles diff --git a/app/src/main/java/ani/dantotsu/others/CrashActivity.kt b/app/src/main/java/ani/dantotsu/others/CrashActivity.kt new file mode 100644 index 00000000..6067d03d --- /dev/null +++ b/app/src/main/java/ani/dantotsu/others/CrashActivity.kt @@ -0,0 +1,30 @@ +package ani.dantotsu.others + +import android.os.Bundle +import android.text.InputType +import android.view.View +import android.widget.Button +import android.widget.EditText +import androidx.appcompat.app.AppCompatActivity +import ani.dantotsu.R +import eu.kanade.tachiyomi.util.system.copyToClipboard + + +class CrashActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_crash) + + val stackTrace = intent.getStringExtra("stackTrace") ?: "No stack trace available" + val reportView = findViewById(R.id.crashReportView) + reportView.setText(stackTrace) + reportView.setOnKeyListener(View.OnKeyListener { _, _, _ -> + true // Blocks input from hardware keyboards. + }) + + val copyButton = findViewById