Send crash reports to the server (#65)

Adds ability to send app crash reports to the last connected server. The
app prompts you before sending. There is an advanced settings to disable
this as well.

Additionally, there is an advanced settings button to send the app logs
to the current server.

The resulting reports can be found on the server's web UI under
Dashboard->Logs

If sharing the logs, make sure there's no private information present!
This commit is contained in:
damontecres 2025-10-26 07:35:42 -04:00 committed by GitHub
parent 05f3811149
commit 2667090485
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 278 additions and 41 deletions

View file

@ -0,0 +1,70 @@
package com.github.damontecres.wholphin.util
import android.content.Context
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.hilt.AppModule
import com.google.auto.service.AutoService
import kotlinx.coroutines.runBlocking
import okhttp3.OkHttpClient
import org.acra.config.CoreConfiguration
import org.acra.data.CrashReportData
import org.acra.sender.ReportSender
import org.acra.sender.ReportSenderException
import org.acra.sender.ReportSenderFactory
import org.jellyfin.sdk.Jellyfin
import org.jellyfin.sdk.api.client.extensions.clientLogApi
import org.jellyfin.sdk.api.okhttp.OkHttpFactory
import org.jellyfin.sdk.createJellyfin
import timber.log.Timber
@AutoService(ReportSenderFactory::class)
class CrashReportSenderFactory : ReportSenderFactory {
override fun create(
context: Context,
config: CoreConfiguration,
): ReportSender = CrashReportSender()
override fun enabled(config: CoreConfiguration): Boolean = true
}
class CrashReportSender : ReportSender {
override fun send(
context: Context,
errorContent: CrashReportData,
) {
Timber.v("Attempting to send crash report")
val prefs = ServerRepository.getServerSharedPreferences(context)
val serverUrl = prefs.getString(ServerRepository.SERVER_URL_KEY, null)
val accessToken = prefs.getString(ServerRepository.ACCESS_TOKEN_KEY, null)
if (serverUrl != null && accessToken != null) {
try {
val okHttpClient =
OkHttpClient
.Builder()
.build()
val okHttpFactory = OkHttpFactory(okHttpClient)
val api =
createJellyfin {
this.context = context
clientInfo = AppModule.clientInfo()
deviceInfo = AppModule.deviceInfo(context)
apiClientFactory = okHttpFactory
socketConnectionFactory = okHttpFactory
minimumServerVersion = Jellyfin.minimumVersion
}.createApi(baseUrl = serverUrl, accessToken = accessToken)
runBlocking {
val filename =
api.clientLogApi
.logFile(errorContent.toJSON())
.content.fileName
Timber.i("Sent report to $serverUrl, filename=$filename")
}
} catch (ex: Exception) {
throw ReportSenderException("Exception while sending crash report", ex)
}
} else {
throw ReportSenderException("Could not find valid server and/or credentials to use")
}
}
}