@@ -2,15 +2,29 @@ package com.github.droidworksstudio.mlauncher
22
33import android.content.Context
44import android.content.Intent
5+ import android.net.ConnectivityManager
6+ import android.net.NetworkCapabilities
57import android.net.Uri
68import android.os.Bundle
9+ import android.util.Base64
710import androidx.appcompat.app.AppCompatActivity
811import androidx.core.net.toUri
12+ import androidx.lifecycle.lifecycleScope
913import com.github.droidworksstudio.common.getLocalizedString
1014import com.github.droidworksstudio.mlauncher.helper.emptyString
1115import com.github.droidworksstudio.mlauncher.helper.getDeviceInfo
16+ import com.github.droidworksstudio.mlauncher.helper.getDeviceInfoJson
1217import com.github.droidworksstudio.mlauncher.helper.utils.SimpleEmailSender
1318import com.google.android.material.dialog.MaterialAlertDialogBuilder
19+ import com.squareup.moshi.JsonAdapter
20+ import com.squareup.moshi.Moshi
21+ import com.squareup.moshi.Types
22+ import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
23+ import kotlinx.coroutines.Dispatchers
24+ import kotlinx.coroutines.launch
25+ import org.json.JSONObject
26+ import java.net.HttpURLConnection
27+ import java.net.URL
1428
1529class CrashReportActivity : AppCompatActivity () {
1630 private var pkgName: String = emptyString()
@@ -24,6 +38,11 @@ class CrashReportActivity : AppCompatActivity() {
2438 0
2539 ).versionName.toString()
2640
41+ // Check for internet connection before sending crash report
42+ if (isInternetAvailable()) {
43+ sendCrashReportNative()
44+ }
45+
2746 // Show a dialog to ask if the user wants to report the crash
2847 MaterialAlertDialogBuilder (this )
2948 .setTitle(getLocalizedString(R .string.acra_crash))
@@ -38,6 +57,88 @@ class CrashReportActivity : AppCompatActivity() {
3857 .show()
3958 }
4059
60+ // Function to check internet connectivity
61+ private fun isInternetAvailable (): Boolean {
62+ val connectivityManager = getSystemService(CONNECTIVITY_SERVICE ) as ConnectivityManager
63+ val network = connectivityManager.activeNetwork ? : return false
64+ val capabilities = connectivityManager.getNetworkCapabilities(network) ? : return false
65+ return capabilities.hasCapability(NetworkCapabilities .NET_CAPABILITY_INTERNET )
66+ }
67+
68+ private fun sendCrashReportNative () {
69+ lifecycleScope.launch(Dispatchers .IO ) {
70+ try {
71+ val crashFileUri: Uri ? = intent.getStringExtra(" crash_log_uri" )?.toUri()
72+ val crashFileUris: List <Uri > = crashFileUri?.let { listOf (it) } ? : emptyList()
73+
74+ val logContent = readFirstCrashFile(this @CrashReportActivity, crashFileUris)
75+
76+ // Example device info JSON string (replace with getDeviceInfoJson() if dynamic)
77+ val jsonDeviceInfo = getDeviceInfoJson(this @CrashReportActivity)
78+
79+ // Parse device info JSON into Map<String, Any>
80+ val moshi = Moshi .Builder ()
81+ .add(KotlinJsonAdapterFactory ())
82+ .build()
83+
84+ val type = Types .newParameterizedType(Map ::class .java, String ::class .java, Any ::class .java)
85+ val deviceAdapter: JsonAdapter <Map <String , Any >> = moshi.adapter(type)
86+ val deviceMap: Map <String , Any > = deviceAdapter.fromJson(jsonDeviceInfo) ? : emptyMap()
87+
88+
89+ val logBase64 = logContent?.toByteArray()?.let {
90+ Base64 .encodeToString(it, Base64 .NO_WRAP )
91+ } ? : " "
92+
93+ // Build crash JSON
94+ val crashJson = JSONObject ().apply {
95+ put(" thread" , " main" )
96+ put(" message" , " App crashed" )
97+ put(" stackTrace" , logContent ? : " No stack trace available" )
98+ put(" device" , JSONObject (deviceMap))
99+ put(" timestamp" , System .currentTimeMillis())
100+ put(" logFileBase64" , logBase64)
101+ }.toString()
102+
103+ val url = URL (" https://crash-worker.wayne6324.workers.dev" )
104+ val connection = url.openConnection() as HttpURLConnection
105+ connection.requestMethod = " POST"
106+ connection.setRequestProperty(" Content-Type" , " application/json; charset=UTF-8" )
107+ connection.doOutput = true
108+
109+ // Send JSON
110+ connection.outputStream.use { it.write(crashJson.toByteArray(Charsets .UTF_8 )) }
111+
112+ val responseCode = connection.responseCode
113+
114+ // Read inputStream if 2xx, else errorStream
115+ val responseStream = if (responseCode in 200 .. 299 ) connection.inputStream else connection.errorStream
116+ val responseMessage = responseStream.bufferedReader().use { it.readText() }
117+
118+ if (responseCode in 200 .. 299 ) {
119+ println (" Crash report sent successfully: $responseMessage " )
120+ } else {
121+ println (" Failed to send crash report: $responseCode $responseMessage " )
122+ }
123+
124+ connection.disconnect()
125+ } catch (e: Exception ) {
126+ e.printStackTrace()
127+ }
128+ }
129+ }
130+
131+ fun readFirstCrashFile (context : Context , crashFileUris : List <Uri >): String? {
132+ val firstUri = crashFileUris.firstOrNull() ? : return null
133+ return try {
134+ context.contentResolver.openInputStream(firstUri)?.bufferedReader().use { it?.readText() }
135+ } catch (e: Exception ) {
136+ e.printStackTrace()
137+ null
138+ }
139+ }
140+
141+
41142 private fun sendCrashReport (context : Context ) {
42143 // Use the latest crash log URI generated by CrashHandler
43144 val crashFileUri: Uri ? = intent.getStringExtra(" crash_log_uri" )?.toUri()
0 commit comments