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