Skip to content

Commit bdc83bc

Browse files
Main: Implemented crash reporting
Signed-off-by: sunilpaulmathew <sunil.kde@gmail.com>
1 parent 989774d commit bdc83bc

22 files changed

Lines changed: 389 additions & 0 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
</intent-filter>
3030
</activity>
3131

32+
<activity android:name=".activities.CrashReportActivity"
33+
android:exported="false" />
34+
3235
<activity
3336
android:enabled="false"
3437
android:name=".activities.InstallerActivity"

app/src/main/java/in/sunilpaulmathew/appvaultx/activities/AppVaultXActivity.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import in.sunilpaulmathew.appvaultx.fragments.InstalledPackagesFragment;
2222
import in.sunilpaulmathew.appvaultx.fragments.RemovedPackagesFragment;
2323
import in.sunilpaulmathew.appvaultx.fragments.SettingsFragment;
24+
import in.sunilpaulmathew.appvaultx.utils.CrashReport;
2425
import in.sunilpaulmathew.appvaultx.utils.Settings;
2526
import in.sunilpaulmathew.appvaultx.utils.ShizukuPermissionChecker;
2627
import in.sunilpaulmathew.appvaultx.utils.ShizukuShell;
@@ -43,6 +44,9 @@ protected void onCreate(Bundle savedInstanceState) {
4344

4445
setContentView(R.layout.activity_main);
4546

47+
// Initialize Crash Reporter
48+
new CrashReport(this).initialize();
49+
4650
updateInstallerActivityState();
4751

4852
BottomNavigationView bottomNav = findViewById(R.id.bottom_navigation);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package in.sunilpaulmathew.appvaultx.activities;
2+
3+
import android.content.ClipData;
4+
import android.content.ClipboardManager;
5+
import android.content.Context;
6+
import android.content.Intent;
7+
import android.net.Uri;
8+
import android.os.Build;
9+
import android.os.Bundle;
10+
import android.widget.Toast;
11+
12+
import androidx.annotation.Nullable;
13+
import androidx.appcompat.app.AppCompatActivity;
14+
import androidx.appcompat.widget.AppCompatImageButton;
15+
import androidx.core.graphics.Insets;
16+
import androidx.core.view.ViewCompat;
17+
import androidx.core.view.WindowInsetsCompat;
18+
19+
import com.google.android.material.button.MaterialButton;
20+
import com.google.android.material.textfield.MaterialAutoCompleteTextView;
21+
import com.google.android.material.textview.MaterialTextView;
22+
23+
import in.sunilpaulmathew.appvaultx.BuildConfig;
24+
import in.sunilpaulmathew.appvaultx.R;
25+
26+
/*
27+
* Created by sunilpaulmathew <sunil.kde@gmail.com> on May 05, 2026
28+
*/
29+
public class CrashReportActivity extends AppCompatActivity {
30+
31+
@Override
32+
protected void onCreate(@Nullable Bundle savedInstanceState) {
33+
super.onCreate(savedInstanceState);
34+
setContentView(R.layout.activity_crashreport);
35+
36+
AppCompatImageButton mCopyButton = findViewById(R.id.copy);
37+
MaterialAutoCompleteTextView mCrashSteps = findViewById(R.id.crash_steps);
38+
MaterialButton mCancelButton = findViewById(R.id.cancel_button);
39+
MaterialButton mReportButton = findViewById(R.id.report_button);
40+
MaterialTextView mRequestMessage = findViewById(R.id.request_message);
41+
MaterialTextView mCrashLog = findViewById(R.id.crash_log);
42+
43+
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.layout_root), (view, insets) -> {
44+
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
45+
46+
view.setPadding(
47+
systemBars.left,
48+
systemBars.top,
49+
systemBars.right,
50+
systemBars.bottom
51+
);
52+
53+
return insets;
54+
});
55+
56+
mRequestMessage.setText(getString(R.string.crash_report_message));
57+
58+
if (getIntent().getStringExtra("crashLog") != null) {
59+
String mCrashText = "App Name: " + getString(R.string.app_name) +
60+
"\nPackage Name: " + getPackageName() + "\nApp Version: " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")" +
61+
"\nSDK Version: " + Build.VERSION.SDK_INT + "\n\n==========\n Stacktrace\n==========\n\n" + getIntent().getStringExtra("crashLog");
62+
mCrashLog.setText(mCrashText);
63+
}
64+
65+
mCancelButton.setOnClickListener(view -> finish());
66+
67+
mCopyButton.setOnClickListener(v -> {
68+
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
69+
ClipData clip = ClipData.newPlainText("Copied to clipboard", mCrashLog.getText() +
70+
"\n\n" + getString(R.string.crash_reproduce_steps) + ": " + mCrashSteps.getText());
71+
clipboard.setPrimaryClip(clip);
72+
Toast.makeText(this, "Copied to clipboard", Toast.LENGTH_SHORT).show();
73+
});
74+
75+
mReportButton.setOnClickListener(view -> {
76+
String mSteps = "";
77+
if (mCrashSteps.getText() != null && !mCrashSteps.getText().toString().trim().isEmpty()) {
78+
mSteps = mCrashSteps.getText().toString();
79+
}
80+
Intent share_log = new Intent(Intent.ACTION_SENDTO);
81+
share_log.setData(Uri.parse("mailto:"));
82+
share_log.putExtra(Intent.EXTRA_EMAIL, new String[] {
83+
"smartpack.org@gmail.com"
84+
});
85+
share_log.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crash_report) + "/" + getString(R.string.app_name));
86+
share_log.putExtra(Intent.EXTRA_TEXT, mCrashLog.getText() +
87+
"\n\n" + getString(R.string.crash_reproduce_steps) + ": " + mSteps);
88+
Intent shareIntent = Intent.createChooser(share_log, "Share");
89+
startActivity(shareIntent);
90+
finish();
91+
});
92+
}
93+
94+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package in.sunilpaulmathew.appvaultx.utils;
2+
3+
import android.annotation.SuppressLint;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
7+
import androidx.annotation.NonNull;
8+
9+
import java.io.File;
10+
import java.io.PrintWriter;
11+
import java.io.StringWriter;
12+
import java.io.Writer;
13+
import java.text.SimpleDateFormat;
14+
import java.util.Date;
15+
16+
import in.sunilpaulmathew.appvaultx.activities.CrashReportActivity;
17+
18+
/*
19+
* Created by sunilpaulmathew <sunil.kde@gmail.com> on May 05, 2026
20+
* Ref: https://stackoverflow.com/questions/601503/how-do-i-obtain-crash-data-from-my-android-application
21+
*/
22+
public class CrashReport implements Thread.UncaughtExceptionHandler {
23+
24+
private final Context mContext;
25+
private final Thread.UncaughtExceptionHandler mDefaultUEH;
26+
27+
public CrashReport(Context context) {
28+
this.mContext = context;
29+
this.mDefaultUEH = Thread.getDefaultUncaughtExceptionHandler();
30+
}
31+
32+
@SuppressLint("SimpleDateFormat")
33+
public void uncaughtException(@NonNull Thread t, Throwable e) {
34+
final Writer result = new StringWriter();
35+
final PrintWriter printWriter = new PrintWriter(result);
36+
e.printStackTrace(printWriter);
37+
String stacktrace = result.toString();
38+
printWriter.close();
39+
Utils.create(stacktrace, new File(mContext.getExternalFilesDir("logs"), "crashLog_" + new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date())));
40+
Intent intent = new Intent(mContext, CrashReportActivity.class);
41+
intent.putExtra("crashLog", stacktrace);
42+
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
43+
mContext.startActivity(intent);
44+
mDefaultUEH.uncaughtException(t, e);
45+
}
46+
47+
public void initialize() {
48+
Thread.setDefaultUncaughtExceptionHandler(new CrashReport(mContext));
49+
}
50+
51+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<!-- drawable/share_outline.xml -->
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
4+
<path android:fillColor="?attr/colorControlNormal" android:pathData="M14,5V9C7,10 4,15 3,20C5.5,16.5 9,14.9 14,14.9V19L21,12L14,5M16,9.83L18.17,12L16,14.17V12.9H14C11.93,12.9 10.07,13.28 8.34,13.85C9.74,12.46 11.54,11.37 14.28,11L16,10.73V9.83Z" />
5+
</vector>
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
<androidx.appcompat.widget.LinearLayoutCompat
2+
android:id="@+id/layout_root"
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
xmlns:app="http://schemas.android.com/apk/res-auto"
5+
android:layout_height="match_parent"
6+
android:layout_width="match_parent"
7+
android:orientation="vertical" >
8+
9+
<FrameLayout
10+
android:layout_width="match_parent"
11+
android:layout_height="wrap_content"
12+
android:padding="8dp">
13+
14+
<com.google.android.material.textview.MaterialTextView
15+
android:text="@string/crash_report"
16+
android:layout_width="match_parent"
17+
android:layout_height="wrap_content"
18+
android:textColor="?attr/colorPrimary"
19+
style="@style/AppTheme.Title" />
20+
21+
<androidx.appcompat.widget.AppCompatImageButton
22+
android:id="@+id/copy"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_gravity="end|center_vertical"
26+
android:background="?attr/selectableItemBackgroundBorderless"
27+
android:layout_marginEnd="10dp"
28+
android:src="@drawable/ic_copy"
29+
app:tint="?attr/colorPrimary" />
30+
</FrameLayout>
31+
32+
<com.google.android.material.textview.MaterialTextView
33+
android:id="@+id/request_message"
34+
android:layout_width="wrap_content"
35+
android:layout_height="wrap_content"
36+
android:paddingHorizontal="10sp"
37+
android:layout_margin="10dp"
38+
android:textStyle="italic"
39+
android:textSize="15sp" />
40+
41+
<androidx.core.widget.NestedScrollView
42+
android:layout_width="match_parent"
43+
android:layout_height="0dp"
44+
android:layout_weight="1"
45+
android:fillViewport="true">
46+
47+
<androidx.appcompat.widget.LinearLayoutCompat
48+
android:layout_width="match_parent"
49+
android:layout_height="wrap_content"
50+
android:orientation="vertical">
51+
52+
<com.google.android.material.card.MaterialCardView
53+
android:layout_width="match_parent"
54+
android:layout_height="wrap_content"
55+
android:layout_margin="10dp"
56+
app:cardCornerRadius="10dp">
57+
58+
<HorizontalScrollView
59+
android:layout_width="match_parent"
60+
android:layout_height="wrap_content"
61+
android:layout_margin="10dp" >
62+
63+
<com.google.android.material.textview.MaterialTextView
64+
android:id="@+id/crash_log"
65+
android:layout_width="wrap_content"
66+
android:layout_height="wrap_content"
67+
android:textIsSelectable="true"
68+
android:textStyle="bold|italic"
69+
android:gravity="start" />
70+
</HorizontalScrollView>
71+
</com.google.android.material.card.MaterialCardView>
72+
</androidx.appcompat.widget.LinearLayoutCompat>
73+
</androidx.core.widget.NestedScrollView>
74+
75+
<androidx.appcompat.widget.LinearLayoutCompat
76+
android:layout_width="match_parent"
77+
android:layout_height="wrap_content"
78+
android:background="?android:attr/windowBackground"
79+
android:orientation="vertical"
80+
android:padding="10dp" >
81+
82+
<com.google.android.material.textfield.TextInputLayout
83+
android:layout_width="match_parent"
84+
android:layout_height="wrap_content"
85+
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
86+
android:hint="@string/crash_reproduce_steps"
87+
android:layout_marginBottom="10dp" >
88+
89+
<com.google.android.material.textfield.MaterialAutoCompleteTextView
90+
android:id="@+id/crash_steps"
91+
android:layout_width="match_parent"
92+
android:layout_height="wrap_content"
93+
android:textStyle="bold|italic"
94+
android:textSize="15sp"
95+
android:padding="15dp" />
96+
</com.google.android.material.textfield.TextInputLayout>
97+
98+
<androidx.appcompat.widget.LinearLayoutCompat
99+
android:layout_width="match_parent"
100+
android:layout_height="wrap_content"
101+
android:orientation="horizontal"
102+
android:layout_gravity="center"
103+
android:gravity="center" >
104+
105+
<com.google.android.material.button.MaterialButton
106+
android:id="@+id/cancel_button"
107+
android:layout_height="wrap_content"
108+
android:layout_width="wrap_content"
109+
style="@style/Widget.Material3.Button.TonalButton"
110+
app:iconTint="?attr/colorSecondary"
111+
android:paddingHorizontal="15sp"
112+
android:layout_gravity="center"
113+
app:icon="@drawable/ic_cancel"
114+
android:text="@string/cancel"
115+
android:textStyle="bold"
116+
android:gravity="center"
117+
app:iconGravity="start"
118+
app:iconPadding="10sp"
119+
android:padding="10sp"/>
120+
121+
<View
122+
android:layout_width="0dp"
123+
android:layout_height="0dp"
124+
android:layout_weight="1" />
125+
126+
<com.google.android.material.button.MaterialButton
127+
android:id="@+id/report_button"
128+
android:layout_height="wrap_content"
129+
android:layout_width="wrap_content"
130+
style="@style/Widget.Material3.Button.TonalButton"
131+
android:textColor="?attr/colorSecondary"
132+
android:layout_gravity="center"
133+
app:icon="@drawable/ic_share"
134+
android:text="@string/report"
135+
android:textStyle="bold"
136+
android:gravity="center"
137+
app:iconGravity="start"
138+
app:iconPadding="10sp"
139+
android:padding="10sp" />
140+
</androidx.appcompat.widget.LinearLayoutCompat>
141+
</androidx.appcompat.widget.LinearLayoutCompat>
142+
</androidx.appcompat.widget.LinearLayoutCompat>

app/src/main/res/values-ar/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation" >
22

33
<string name="cancel">"إلغاء"</string>
4+
<string name="crash_report">تقرير الأعطال</string>
5+
<string name="crash_report_message">"تم الكشف عن عطل أثناء الاستخدام الأخير لهذا التطبيق. لمساعدة المطور على إصلاح هذه المشكلة ، يرجى مراعاة الإبلاغ عن سجل الأعطال هذا إلى
6+
7+
البريد الإلكتروني: smartpack.org@gmail.com"</string>
8+
<string name="crash_reproduce_steps">خطوات إعادة إنتاج التعطل (اختياري)</string>
49
<string name="press_back">"اضغط زر الرجوع مرة أخرى للخروج"</string>
510
<string name="request_permission">"طلب الاذن"</string>
611
<string name="start">"ابدأ"</string>
@@ -16,6 +21,7 @@
1621
<string name="general">"عام"</string>
1722
<string name="privacy_policy">"سياسة الخصوصية"</string>
1823
<string name="privacy_policy_description">"خصوصيتك مهمة: اعرف المزيد"</string>
24+
<string name="report">تقرير</string>
1925
<string name="settings">"الإعدادات"</string>
2026
<string name="shizuku_access_denied_title">"رُفض الوصول إلى Shizuku لـ aShell"</string>
2127
<string name="shizuku_learn">"تعلم Shizuku"</string>

app/src/main/res/values-de/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation">
22

33
<string name="cancel">"Abbruch"</string>
4+
<string name="crash_report">"Unfallbericht"</string>
5+
<string name="crash_report_message">"Während der letzten Verwendung dieser Anwendung wurde ein Absturz festgestellt. Um dem Entwickler zu helfen, dieses Problem zu beheben, sende bitte dieses Absturzprotokoll an
6+
7+
E-Mail-Adresse: smartpack.org@gmail.com"</string>
8+
<string name="crash_reproduce_steps">"Schritte zur Reproduktion des Absturzes"</string>
49
<string name="press_back">"Drücken Sie erneut auf zurück, um die App zu schließen"</string>
510
<string name="request_permission">"Berechtigung abfragen"</string>
611
<string name="start">"Start"</string>
@@ -16,6 +21,7 @@
1621
<string name="general">"Allgemein"</string>
1722
<string name="privacy_policy">"Datenschutzrichtlinie"</string>
1823
<string name="privacy_policy_description">"Ihre Privatsphäre ist wichtig: Erfahren Sie mehr"</string>
24+
<string name="report">"Bericht"</string>
1925
<string name="settings">"Einstellungen"</string>
2026
<string name="shizuku_access_denied_title">"Shizuku-Zugriff für aShell verweigert"</string>
2127
<string name="shizuku_learn">"Shizuku lernen"</string>

app/src/main/res/values-es/strings.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation" >
22

33
<string name="cancel">"Cancelar"</string>
4+
<string name="crash_report">"Informe del accidente"</string>
5+
<string name="crash_report_message">"Se detectó un bloqueo durante el último uso de esta aplicación. Para ayudar al desarrollador a solucionar este problema, considere informar este registro de fallas a
6+
7+
Correo electrónico: smartpack.org@gmail.com"</string>
8+
<string name="crash_reproduce_steps">"Pasos para reproducir el bloqueo"</string>
49
<string name="press_back">"Presione atrás de nuevo para salir"</string>
510
<string name="request_permission">"Solicitar permiso"</string>
611
<string name="start">"Comenzar"</string>
@@ -16,6 +21,7 @@
1621
<string name="general">"General"</string>
1722
<string name="privacy_policy">"Política de privacidad"</string>
1823
<string name="privacy_policy_description">"Tu privacidad es importante: Más información"</string>
24+
<string name="report">"Reporte"</string>
1925
<string name="settings">"Ajustes"</string>
2026
<string name="shizuku_access_denied_title">"Acceso denegado a Shizuku para aShell"</string>
2127
<string name="shizuku_learn">"Aprende shizuku"</string>

app/src/main/res/values-fr/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="MissingTranslation" >
22

33
<string name="cancel">"Annuler"</string>
4+
<string name="crash_report">"Rapport de crash"</string>
5+
<string name="crash_report_message">"Un crash a été détecté lors de la dernière utilisation de l'appli. Pour aider le développeur à corriger ce problème, rapportez ce journal de crash à
6+
7+
E-mail : smartpack.org@gmail.com"</string>
8+
<string name="crash_reproduce_steps">"Étapes pour reproduire le crash"</string>
49
<string name="press_back">"Appuyez à nouveau sur la touche arrière pour sortir"</string>
510
<string name="request_permission">"Demander l'autorisation"</string>
611
<string name="start">"Commencer"</string>

0 commit comments

Comments
 (0)