Rolled back ACRA and implemented an uncaught exception handler

This commit is contained in:
Sergey Eremin
2017-02-23 16:33:22 +03:00
parent 9268f28a22
commit dcddab2941
11 changed files with 207 additions and 94 deletions

View File

@@ -24,6 +24,5 @@ android {
}
dependencies {
compile 'ch.acra:acra:4.9.0'
compile 'com.github.yeriomin:play-store-api:556e2bf'
}

View File

@@ -9,7 +9,6 @@
<application
android:allowBackup="false"
android:name=".YalpStoreApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">

View File

@@ -23,6 +23,12 @@ public class AboutActivity extends YalpStoreActivity {
TextView gsfIdView = (TextView) findViewById(R.id.gsf_id);
gsfIdView.setText(gsfId);
gsfIdView.setOnClickListener(new CopyToClipboardListener());
findViewById(R.id.developer_email).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new YalpStoreUncaughtExceptionHandler(AboutActivity.this).send(null);
}
});
}
private class CopyToClipboardListener implements View.OnClickListener {

View File

@@ -171,19 +171,19 @@ public class DetailsActivity extends YalpStoreActivity {
if (null == versionName || versionName.isEmpty()) {
return;
}
String label = getString(R.string.details_versionName, versionName);
if (app.isInstalled()) {
try {
PackageInfo info = getPackageManager().getPackageInfo(app.getPackageName(), 0);
if (info.versionCode != app.getVersionCode()) {
label = getString(R.string.details_versionName_updatable, info.versionName, versionName);
}
} catch (PackageManager.NameNotFoundException e) {
// We've checked for that already
}
}
textView.setText(label);
textView.setText(getString(R.string.details_versionName, versionName));
textView.setVisibility(View.VISIBLE);
if (!app.isInstalled()) {
return;
}
try {
PackageInfo info = getPackageManager().getPackageInfo(app.getPackageName(), 0);
if (info.versionCode != app.getVersionCode()) {
textView.setText(getString(R.string.details_versionName_updatable, info.versionName, versionName));
}
} catch (PackageManager.NameNotFoundException e) {
// We've checked for that already
}
}
private void drawDescription(App app) {

View File

@@ -3,6 +3,7 @@ package com.github.yeriomin.yalpstore;
import android.content.Context;
import android.content.pm.FeatureInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.os.Build;
import android.util.DisplayMetrics;
@@ -22,24 +23,6 @@ public class NativeDeviceInfoProvider implements DeviceInfoProvider {
// Getting this requires a permission and google services to be installed
static private final int GOOGLE_SERVICES_VERSION_CODE = 80711500;
static private final String[] sharedLibraries = new String[] {
"ConnectivityExt",
"activation.jar",
"android-support-v13.jar",
"android-support-v4.jar",
"android-support-v7-recyclerview.jar",
"cloud-common.jar",
"com.android.media.remotedisplay",
"com.android.mediadrm.signer",
"android.test.runner",
"com.android.future.usb.accessory",
"com.android.location.provider",
"com.android.nfc_extras",
"com.google.android.maps",
"com.google.android.media.effects",
"com.google.widevine.software.drm",
"javax.obex"
};
static private final String[] glExtensions = new String[] {
"GL_AMD_compressed_ATC_texture",
"GL_AMD_performance_monitor",
@@ -220,27 +203,43 @@ public class NativeDeviceInfoProvider implements DeviceInfoProvider {
}
public DeviceConfigurationProto getDeviceConfigurationProto() {
DisplayMetrics metrics = this.context.getResources().getDisplayMetrics();
return DeviceConfigurationProto.newBuilder()
.setTouchScreen(3)
.setKeyboard(1)
.setNavigation(1)
.setScreenLayout(2)
.setHasHardKeyboard(false)
.setHasFiveWayNavigation(false)
.setScreenDensity((int) (metrics.density * 160f))
.setScreenWidth(metrics.widthPixels)
.setScreenHeight(metrics.heightPixels)
DeviceConfigurationProto.Builder builder = DeviceConfigurationProto.newBuilder();
addDisplayMetrics(builder);
addConfiguration(builder);
return builder
.addAllNativePlatform(getPlatforms())
.addAllSystemSharedLibrary(Arrays.asList(sharedLibraries))
.addAllSystemAvailableFeature(getFeatures())
.addAllSystemSharedLibrary(getSharedLibraries(context))
.addAllSystemAvailableFeature(getFeatures(context))
.addAllSystemSupportedLocale(getLocales())
.setGlEsVersion(196609) // Getting this and next list requires messing with ndk
.addAllGlExtension(Arrays.asList(glExtensions))
.build();
}
private List<String> getPlatforms() {
private DeviceConfigurationProto.Builder addDisplayMetrics(DeviceConfigurationProto.Builder builder) {
DisplayMetrics metrics = this.context.getResources().getDisplayMetrics();
builder
.setScreenDensity((int) (metrics.density * 160f))
.setScreenWidth(metrics.widthPixels)
.setScreenHeight(metrics.heightPixels)
;
return builder;
}
private DeviceConfigurationProto.Builder addConfiguration(DeviceConfigurationProto.Builder builder) {
Configuration config = this.context.getResources().getConfiguration();
builder
.setTouchScreen(config.touchscreen)
.setKeyboard(config.keyboard)
.setNavigation(config.navigation)
.setScreenLayout(config.screenLayout & 15)
.setHasHardKeyboard(config.keyboard == Configuration.KEYBOARD_QWERTY)
.setHasFiveWayNavigation(config.navigation == Configuration.NAVIGATIONHIDDEN_YES)
;
return builder;
}
static public List<String> getPlatforms() {
List<String> platforms = new ArrayList<>();
if (Build.VERSION.SDK_INT >= 21) {
platforms = Arrays.asList(Build.SUPPORTED_ABIS);
@@ -255,8 +254,8 @@ public class NativeDeviceInfoProvider implements DeviceInfoProvider {
return platforms;
}
private List<String> getFeatures() {
PackageManager packageManager = this.context.getPackageManager();
static public List<String> getFeatures(Context context) {
PackageManager packageManager = context.getPackageManager();
FeatureInfo[] featuresList = packageManager.getSystemAvailableFeatures();
List<String> featureStringList = new ArrayList<>();
for (FeatureInfo feature : featuresList) {
@@ -267,7 +266,7 @@ public class NativeDeviceInfoProvider implements DeviceInfoProvider {
return featureStringList;
}
private List<String> getLocales() {
static public List<String> getLocales() {
List<String> localeStringList = new ArrayList<>();
for (Locale locale : Locale.getAvailableLocales()) {
String localeString = locale.toString();
@@ -277,4 +276,8 @@ public class NativeDeviceInfoProvider implements DeviceInfoProvider {
}
return localeStringList;
}
static public List<String> getSharedLibraries(Context context) {
return Arrays.asList(context.getPackageManager().getSystemSharedLibraryNames());
}
}

View File

@@ -16,6 +16,7 @@ public abstract class YalpStoreActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
logout = false;
ThemeManager.setTheme(this);
Thread.setDefaultUncaughtExceptionHandler(new YalpStoreUncaughtExceptionHandler(this));
super.onCreate(savedInstanceState);
}

View File

@@ -1,42 +0,0 @@
package com.github.yeriomin.yalpstore;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import org.acra.ACRA;
import org.acra.ReportField;
import org.acra.ReportingInteractionMode;
import org.acra.config.ACRAConfiguration;
import org.acra.config.ACRAConfigurationException;
import org.acra.config.ConfigurationBuilder;
public class YalpStoreApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
ACRAConfiguration config = new ConfigurationBuilder(this)
.setMailTo(getString(R.string.about_developer_email))
.setReportingInteractionMode(ReportingInteractionMode.DIALOG)
.setResDialogText(R.string.application_crashed)
.setCustomReportContent(
ReportField.ANDROID_VERSION,
ReportField.STACK_TRACE_HASH,
ReportField.STACK_TRACE,
ReportField.BUILD,
ReportField.BUILD_CONFIG,
ReportField.DISPLAY,
ReportField.DEVICE_FEATURES
)
.build();
ACRA.init(this, config);
} catch (ACRAConfigurationException e) {
Log.e(getClass().getName(), "Could not configure ACRA: " + e.getMessage());
} catch (Throwable e) {
Log.e(getClass().getName(), "Unknown problem with ACRA: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,145 @@
package com.github.yeriomin.yalpstore;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Build;
import android.os.Looper;
import android.text.ClipboardManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import java.util.LinkedHashMap;
import java.util.Map;
class YalpStoreUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private Activity activity;
public YalpStoreUncaughtExceptionHandler(Activity activity) {
this.activity = activity;
}
@Override
public void uncaughtException(Thread t, final Throwable e) {
e.printStackTrace();
Thread thread = new Thread() {
@Override
public void run() {
Looper.prepare();
showCrashDialog(e);
Looper.loop();
}
};
try {
thread.start();
} catch (Throwable ee) {
Log.e(getClass().getName(), "Failed to process an uncaught exception: " + ee.getMessage());
System.exit(1);
}
}
public AlertDialog showCrashDialog(final Throwable e) {
return new AlertDialog.Builder(activity)
.setTitle(activity.getString(R.string.dialog_title_application_crashed))
.setMessage(activity.getString(R.string.dialog_message_application_crashed))
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
System.exit(1);
}
})
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
send(e);
dialog.dismiss();
System.exit(1);
}
})
.show()
;
}
public void send(Throwable e) {
Intent emailIntent = new Intent(Intent.ACTION_SENDTO);
emailIntent.setData(Uri.fromParts("mailto", activity.getString(R.string.about_developer_email), null));
emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, activity.getPackageName() + " Crash Report");
emailIntent.putExtra(Intent.EXTRA_TEXT, buildBody(e));
try {
activity.startActivity(emailIntent);
} catch (ActivityNotFoundException ee) {
Log.e(getClass().getName(), ee.getClass().toString() + " " + ee.getMessage());
((ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE)).setText(buildBody(e));
}
}
private String buildBody(Throwable e) {
StringBuilder body = new StringBuilder();
if (null != e) {
body.append("\n\n").append(Log.getStackTraceString(e)).append("\n\n");
}
Map<String, String> values = new LinkedHashMap<>();
values.putAll(getBuildValues());
values.putAll(getConfigurationValues());
values.putAll(getDisplayMetricsValues());
values.putAll(getPackageManagerValues());
for (String key : values.keySet()) {
body.append(key).append(" = ").append(values.get(key)).append("\n");
}
return body.toString();
}
private Map<String, String> getBuildValues() {
Map<String, String> values = new LinkedHashMap<>();
values.put("Build.FINGERPRINT", Build.FINGERPRINT);
values.put("Build.HARDWARE", Build.HARDWARE);
values.put("Build.BRAND", Build.BRAND);
values.put("Build.RADIO", Build.RADIO);
values.put("Build.BOOTLOADER", Build.BOOTLOADER);
values.put("Build.DEVICE", Build.DEVICE);
values.put("Build.VERSION.SDK_INT", Integer.toString(Build.VERSION.SDK_INT));
values.put("Build.MODEL", Build.MODEL);
values.put("Build.MANUFACTURER", Build.MANUFACTURER);
values.put("Build.PRODUCT", Build.PRODUCT);
return values;
}
private Map<String, String> getConfigurationValues() {
Map<String, String> values = new LinkedHashMap<>();
Configuration config = activity.getResources().getConfiguration();
values.put("TouchScreen", Integer.toString(config.touchscreen));
values.put("Keyboard", Integer.toString(config.keyboard));
values.put("Navigation", Integer.toString(config.navigation));
values.put("ScreenLayout", Integer.toString(config.screenLayout & 15));
values.put("HasHardKeyboard", Boolean.toString(config.keyboard == Configuration.KEYBOARD_QWERTY));
values.put("HasFiveWayNavigation", Boolean.toString(config.navigation == Configuration.NAVIGATIONHIDDEN_YES));
return values;
}
private Map<String, String> getDisplayMetricsValues() {
Map<String, String> values = new LinkedHashMap<>();
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
values.put("Screen.Density", Integer.toString((int) (metrics.density * 160f)));
values.put("Screen.Width", Integer.toString(metrics.widthPixels));
values.put("Screen.Height", Integer.toString(metrics.heightPixels));
return values;
}
private Map<String, String> getPackageManagerValues() {
Map<String, String> values = new LinkedHashMap<>();
values.put("Platforms", TextUtils.join(",", NativeDeviceInfoProvider.getPlatforms()));
values.put("SharedLibraries", TextUtils.join(",", NativeDeviceInfoProvider.getSharedLibraries(activity)));
values.put("Features", TextUtils.join(",", activity.getPackageManager().getSystemSharedLibraryNames()));
values.put("Locales", TextUtils.join(",", NativeDeviceInfoProvider.getLocales()));
return values;
}
}

View File

@@ -39,7 +39,7 @@
android:id="@+id/developer_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:autoLink="email"
android:textColor="#5555ff"
android:textSize="18sp"
android:text="@string/about_developer_email" />

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="application_crashed">В Yalp Store произошёл сбой.\n\nВы можете помочь разработчику исправить это, отправив email с подробностями об ошибке. Нажмите OK чтобы открыть ваше email-приложение с письмом для разработчика, готовым к отправке.\n\nНикакой чувствительной и личной информации отправлено не будет, проверьте текст письма, если сомеваетесь.</string>
<string name="about_developer_email_label">Почта разработчика:</string>
<string name="about_logged_in">Ваш текущий аккаунт:</string>
<string name="about_copied_to_clipboard">Скопировано в буфер обмена</string>
@@ -9,6 +8,8 @@
<string name="list_empty_updates">Обновлений нет</string>
<string name="list_empty_search">Ничего не найдено</string>
<string name="list_incompatible">несовместимо</string>
<string name="dialog_title_application_crashed">В Yalp Store произошёл сбой</string>
<string name="dialog_message_application_crashed">Вы можете помочь разработчику исправить это, отправив email с подробностями об ошибке. Нажмите OK чтобы открыть ваше email-приложение с письмом для разработчика, готовым к отправке.\n\nЕсли хотите, опишите проблему сами или добавьте любое другое сообщение.\n\nНикакой чувствительной и личной информации отправлено не будет, проверьте текст письма, если сомеваетесь.</string>
<string name="dialog_two_factor_cancel">Позднее</string>
<string name="dialog_two_factor_create_password">Создать пароль</string>
<string name="dialog_title_two_factor">Двухэтапная аутентификация</string>

View File

@@ -4,7 +4,6 @@
<string name="star_empty" translatable="false"></string>
<string name="about_developer_email" translatable="false">yalp.store.dev@gmail.com</string>
<string name="about_gsf_id" translatable="false">Google Services Framework id:</string>
<string name="application_crashed">Sorry, Yalp Store crashed.\n\nYou can help fix this by sending error data to the developer. Clicking OK will take you to your default email app with a complete email ready to be sent to the developer.\n\nNo private or sensitive information is collected, please review the letter content if you doubt it.</string>
<string name="about_developer_email_label">Developer\'s email:</string>
<string name="about_logged_in">You are logged in as:</string>
<string name="about_copied_to_clipboard">Copied to clipboard</string>
@@ -24,6 +23,8 @@
<string name="action_unwhitelist">Ignore updates</string>
<string name="search_filter">All apps</string>
<string name="search_title">Search applications</string>
<string name="dialog_title_application_crashed">Sorry, Yalp Store crashed</string>
<string name="dialog_message_application_crashed">You can help fix this by sending error data to the developer. Clicking OK will take you to your default email app with a complete email ready to be sent to the developer.\n\nFeel free to add your own description of the problem or any other message.\n\nNo private or sensitive information is collected, please review the letter content if you doubt it.</string>
<string name="dialog_two_factor_cancel">Not now</string>
<string name="dialog_two_factor_create_password">Create password</string>
<string name="dialog_title_two_factor">2-Step Verification</string>