optimizations

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 3 years ago
parent 99afdcccb2
commit 5dd98d480b

@ -38,8 +38,8 @@ android {
applicationId = "com.fox2code.mmm"
minSdk = 24
targetSdk = 33
versionCode = 74
versionName = "2.1.1"
versionCode = 75
versionName = "2.1.2"
vectorDrawables {
useSupportLibrary = true
}

@ -195,11 +195,13 @@
android:value="false" />
<meta-data
android:name="io.sentry.dsn"
android:value="https://f35f7f369a254b2ca854cf9593c99da2@sentry.androidacy.com/7" />
android:value="https://e199c2a54a1f49b1b5beb3244f2c2358@sentry.androidacy.com/77" />
<!-- enable view hierarchy for crashes -->
<meta-data android:name="io.sentry.attach-view-hierarchy" android:value="true" />
<!-- Sane value, but feel free to lower it -->
<meta-data
android:name="io.sentry.traces.sample-rate"
android:value="0.25" />
android:value="0.2" />
<!-- Doesn't actually monitor anything, just used to get the activities the user went through -->
<meta-data
android:name="io.sentry.traces.user-interaction.enable"
@ -215,7 +217,7 @@
<!-- Performance profiling -->
<meta-data
android:name="io.sentry.traces.profiling.sample-rate"
android:value="0.25" />
android:value="0.2" />
</application>
</manifest>

@ -1,174 +1,199 @@
package com.fox2code.mmm;
package com.fox2code.mmm
import android.annotation.SuppressLint;
import android.content.ClipboardManager;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.fox2code.foxcompat.app.FoxActivity;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.textview.MaterialTextView;
import java.io.PrintWriter;
import java.io.StringWriter;
import io.sentry.Sentry;
import io.sentry.UserFeedback;
import timber.log.Timber;
public class CrashHandler extends FoxActivity {
import android.annotation.SuppressLint
import android.content.ClipData
import android.content.ClipboardManager
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.Toast
import com.fox2code.foxcompat.app.FoxActivity
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.textview.MaterialTextView
import io.sentry.Sentry
import io.sentry.UserFeedback
import timber.log.Timber
import java.io.PrintWriter
import java.io.StringWriter
class CrashHandler : FoxActivity() {
@Suppress("DEPRECATION", "KotlinConstantConditions")
@SuppressLint("RestrictedApi")
@Override
protected void onCreate(Bundle savedInstanceState) {
Timber.i("CrashHandler.onCreate(%s)", savedInstanceState);
override fun onCreate(savedInstanceState: Bundle?) {
Timber.i("CrashHandler.onCreate(%s)", savedInstanceState)
// log intent with extras
Timber.d("CrashHandler.onCreate: intent=%s", getIntent());
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_crash_handler);
// unlock webview
Timber.d("CrashHandler.onCreate: intent=%s", intent)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crash_handler)
// set crash_details MaterialTextView to the exception passed in the intent or unknown if null
// convert stacktrace from array to string, and pretty print it (first line is the exception, the rest is the stacktrace, with each line indented by 4 spaces)
MaterialTextView crashDetails = findViewById(R.id.crash_details);
crashDetails.setText("");
val crashDetails = findViewById<MaterialTextView>(R.id.crash_details)
crashDetails.text = ""
// get the exception from the intent
Throwable exception = (Throwable) getIntent().getSerializableExtra("exception");
val exception = intent.getSerializableExtra("exception") as Throwable?
// get the crashReportingEnabled from the intent
boolean crashReportingEnabled = getIntent().getBooleanExtra("crashReportingEnabled", false);
val crashReportingEnabled = intent.getBooleanExtra("crashReportingEnabled", false)
// if the exception is null, set the crash details to "Unknown"
if (exception == null) {
crashDetails.setText(R.string.crash_details);
crashDetails.setText(R.string.crash_details)
} else {
// if the exception is not null, set the crash details to the exception and stacktrace
// stacktrace is an StacktraceElement, so convert it to a string and replace the commas with newlines
StringWriter stringWriter = new StringWriter();
exception.printStackTrace(new PrintWriter(stringWriter));
String stacktrace = stringWriter.toString();
stacktrace = stacktrace.replace(",", "\n ");
crashDetails.setText(getString(R.string.crash_full_stacktrace, stacktrace));
val stringWriter = StringWriter()
exception.printStackTrace(PrintWriter(stringWriter))
var stacktrace = stringWriter.toString()
stacktrace = stacktrace.replace(",", "\n ")
crashDetails.text = getString(R.string.crash_full_stacktrace, stacktrace)
}
String lastEventId = getIntent().getStringExtra("lastEventId");
Timber.d("CrashHandler.onCreate: lastEventId=%s, crashReportingEnabled=%s", lastEventId, crashReportingEnabled);
val lastEventId = intent.getStringExtra("lastEventId")
Timber.d(
"CrashHandler.onCreate: lastEventId=%s, crashReportingEnabled=%s",
lastEventId,
crashReportingEnabled
)
if (lastEventId == null && crashReportingEnabled) {
// if lastEventId is null, hide the feedback button
findViewById(R.id.feedback).setVisibility(View.GONE);
Timber.d("CrashHandler.onCreate: lastEventId is null but crash reporting is enabled. This may indicate a bug in the crash reporting system.");
findViewById<View>(R.id.feedback).visibility = View.GONE
Timber.d("CrashHandler.onCreate: lastEventId is null but crash reporting is enabled. This may indicate a bug in the crash reporting system.")
} else {
// if lastEventId is not null, show the feedback button
findViewById(R.id.feedback).setVisibility(View.VISIBLE);
findViewById<View>(R.id.feedback).visibility = View.VISIBLE
}
// disable feedback if sentry is disabled
//noinspection ConstantConditions
if (crashReportingEnabled && lastEventId != null) {
// get name, email, and message fields
EditText name = findViewById(R.id.feedback_name);
EditText email = findViewById(R.id.feedback_email);
EditText description = findViewById(R.id.feedback_message);
val name = findViewById<EditText>(R.id.feedback_name)
val email = findViewById<EditText>(R.id.feedback_email)
val description = findViewById<EditText>(R.id.feedback_message)
// get submit button
findViewById(R.id.feedback_submit).setOnClickListener(v -> {
findViewById<View>(R.id.feedback_submit).setOnClickListener { _: View? ->
// require the feedback_message, rest is optional
if (description.getText().toString().equals("")) {
Toast.makeText(this, R.string.sentry_dialogue_empty_message, Toast.LENGTH_LONG).show();
return;
if (description.text.toString() == "") {
Toast.makeText(this, R.string.sentry_dialogue_empty_message, Toast.LENGTH_LONG)
.show()
return@setOnClickListener
}
// if email or name is empty, use "Anonymous"
final String[] nameString = {name.getText().toString().equals("") ? "Anonymous" : name.getText().toString()};
final String[] emailString = {email.getText().toString().equals("") ? "Anonymous" : email.getText().toString()};
val nameString =
arrayOf(if (name.text.toString() == "") "Anonymous" else name.text.toString())
val emailString =
arrayOf(if (email.text.toString() == "") "Anonymous" else email.text.toString())
// get sentryException passed in intent
Throwable sentryException = (Throwable) getIntent().getSerializableExtra("sentryException");
new Thread(() -> {
val sentryException = intent.getSerializableExtra("sentryException") as Throwable?
Thread {
try {
UserFeedback userFeedback;
val userFeedback: UserFeedback
if (sentryException != null) {
userFeedback = new UserFeedback(Sentry.captureException(sentryException));
userFeedback = UserFeedback(Sentry.captureException(sentryException))
// Setups the JSON body
if (nameString[0].equals("")) nameString[0] = "Anonymous";
if (emailString[0].equals("")) emailString[0] = "Anonymous";
userFeedback.setName(nameString[0]);
userFeedback.setEmail(emailString[0]);
userFeedback.setComments(description.getText().toString());
Sentry.captureUserFeedback(userFeedback);
if (nameString[0] == "") nameString[0] = "Anonymous"
if (emailString[0] == "") emailString[0] = "Anonymous"
userFeedback.name = nameString[0]
userFeedback.email = emailString[0]
userFeedback.comments = description.text.toString()
Sentry.captureUserFeedback(userFeedback)
}
Timber.i(
"Submitted user feedback: name %s email %s comment %s",
nameString[0],
emailString[0],
description.text.toString()
)
runOnUiThread {
Toast.makeText(
this,
R.string.sentry_dialogue_success,
Toast.LENGTH_LONG
).show()
}
Timber.i("Submitted user feedback: name %s email %s comment %s", nameString[0], emailString[0], description.getText().toString());
runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_success, Toast.LENGTH_LONG).show());
// Close the activity
finish();
finish()
// start the main activity
startActivity(getPackageManager().getLaunchIntentForPackage(getPackageName()));
} catch (Exception e) {
Timber.e(e, "Failed to submit user feedback");
startActivity(packageManager.getLaunchIntentForPackage(packageName))
} catch (e: Exception) {
Timber.e(e, "Failed to submit user feedback")
// Show a toast if the user feedback could not be submitted
runOnUiThread(() -> Toast.makeText(this, R.string.sentry_dialogue_failed_toast, Toast.LENGTH_LONG).show());
runOnUiThread {
Toast.makeText(
this,
R.string.sentry_dialogue_failed_toast,
Toast.LENGTH_LONG
).show()
}
}
}).start();
});
}.start()
}
// get restart button
findViewById(R.id.restart).setOnClickListener(v -> {
findViewById<View>(R.id.restart).setOnClickListener { _: View? ->
// Restart the app and submit sans feedback
Throwable sentryException = (Throwable) getIntent().getSerializableExtra("sentryException");
if (crashReportingEnabled) Sentry.captureException(sentryException);
finish();
startActivity(getPackageManager().getLaunchIntentForPackage(getPackageName()));
});
val sentryException = intent.getSerializableExtra("sentryException") as Throwable?
if (crashReportingEnabled) Sentry.captureException(sentryException!!)
finish()
startActivity(packageManager.getLaunchIntentForPackage(packageName))
}
} else {
// disable feedback if sentry is disabled
findViewById(R.id.feedback_name).setEnabled(false);
findViewById(R.id.feedback_email).setEnabled(false);
findViewById(R.id.feedback_message).setEnabled(false);
findViewById<View>(R.id.feedback_name).isEnabled = false
findViewById<View>(R.id.feedback_email).isEnabled = false
findViewById<View>(R.id.feedback_message).isEnabled = false
// fade out all the fields
findViewById(R.id.feedback_name).setAlpha(0.5f);
findViewById(R.id.feedback_email).setAlpha(0.5f);
findViewById(R.id.feedback_message).setAlpha(0.5f);
findViewById<View>(R.id.feedback_name).alpha = 0.5f
findViewById<View>(R.id.feedback_email).alpha = 0.5f
findViewById<View>(R.id.feedback_message).alpha = 0.5f
// fade out the submit button
findViewById(R.id.feedback_submit).setAlpha(0.5f);
findViewById<View>(R.id.feedback_submit).alpha = 0.5f
// set feedback_text to "Crash reporting is disabled"
((MaterialTextView) findViewById(R.id.feedback_text)).setText(R.string.sentry_enable_nag);
findViewById(R.id.feedback_submit).setOnClickListener(v -> Toast.makeText(this, R.string.sentry_dialogue_disabled, Toast.LENGTH_LONG).show());
(findViewById<View>(R.id.feedback_text) as MaterialTextView).setText(R.string.sentry_enable_nag)
findViewById<View>(R.id.feedback_submit).setOnClickListener { _: View? ->
Toast.makeText(
this,
R.string.sentry_dialogue_disabled,
Toast.LENGTH_LONG
).show()
}
// handle restart button
// we have to explicitly enable it because it's disabled by default
findViewById(R.id.restart).setEnabled(true);
findViewById(R.id.restart).setOnClickListener(v -> {
findViewById<View>(R.id.restart).isEnabled = true
findViewById<View>(R.id.restart).setOnClickListener { _: View? ->
// Restart the app
finish();
startActivity(getPackageManager().getLaunchIntentForPackage(getPackageName()));
});
finish()
startActivity(packageManager.getLaunchIntentForPackage(packageName))
}
}
// handle reset button
findViewById(R.id.reset).setOnClickListener(v -> {
findViewById<View>(R.id.reset).setOnClickListener { _: View? ->
// show a confirmation material dialog
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
builder.setTitle(R.string.reset_app);
builder.setMessage(R.string.reset_app_confirmation);
builder.setPositiveButton(R.string.reset, (dialog, which) -> {
val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(R.string.reset_app)
builder.setMessage(R.string.reset_app_confirmation)
builder.setPositiveButton(R.string.reset) { _: DialogInterface?, _: Int ->
// reset the app
MainApplication.getINSTANCE().resetApp();
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> {
// do nothing
});
builder.show();
});
MainApplication.getINSTANCE().resetApp()
}
builder.setNegativeButton(R.string.cancel) { _: DialogInterface?, _: Int -> }
builder.show()
}
}
public void copyCrashDetails(View view) {
fun copyCrashDetails(view: View) {
// change view to a checkmark
view.setBackgroundResource(R.drawable.baseline_check_24);
view.setBackgroundResource(R.drawable.baseline_check_24)
// copy crash_details to clipboard
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
String crashDetails = ((MaterialTextView) findViewById(R.id.crash_details)).getText().toString();
clipboard.setPrimaryClip(android.content.ClipData.newPlainText("crash_details", crashDetails));
val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
val crashDetails =
(findViewById<View>(R.id.crash_details) as MaterialTextView).text.toString()
clipboard.setPrimaryClip(ClipData.newPlainText("crash_details", crashDetails))
// show a toast
Toast.makeText(this, R.string.crash_details_copied, Toast.LENGTH_LONG).show();
Toast.makeText(this, R.string.crash_details_copied, Toast.LENGTH_LONG).show()
// after 1 second, change the view back to a copy button
new Thread(() -> {
Thread {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Thread.sleep(1000)
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
}
runOnUiThread(() -> view.setBackgroundResource(R.drawable.baseline_copy_all_24));
}).start();
runOnUiThread { view.setBackgroundResource(R.drawable.baseline_copy_all_24) }
}.start()
}
}
}

@ -31,14 +31,16 @@
android:text="@string/crash_text"
android:textSize="20sp"
android:textStyle="bold" />
<!-- smaller details may be found below header -->
<!-- textview suggesting user use the submit feedback button instead of manually copying the crash details -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/crash_details_suggestion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:text="@string/more_details"
android:textSize="16sp" />
android:gravity="fill"
android:text="@string/crash_details_suggestion"
android:textSize="14sp" />
<!-- copyable crash_details body with copy button in top right corner -->
<FrameLayout

@ -427,4 +427,5 @@
<string name="androidacy_thanks_up_title">You\'ve already upgraded!</string>
<string name="upgrade_snackbar">Androidacy Premium offers faster downloads, an ad-free experience, and more!</string>
<string name="upgrade_now">Upgrade</string>
<string name="crash_details_suggestion">The stacktrace may be found below. However, we <b>strongly</b> recommend you to use the feedback form below to submit feedback instead. This way, instead of manually copying the stacktrace, it will send it to us automatically. It also is deobfuscated that way and additional details are reported automatically.</string>
</resources>

Loading…
Cancel
Save