From 808b0dc6ad195733e10fd604402308596b31dd03 Mon Sep 17 00:00:00 2001 From: Janne Koschinski <janne@kuschku.de> Date: Sun, 1 Oct 2017 04:40:26 +0200 Subject: [PATCH] Performance improvements, implemented a custom crash reporter --- app/build.gradle.kts | 1 + app/src/main/AndroidManifest.xml | 1 - .../kuschku/quasseldroid_ng/QuasseldroidNG.kt | 2 + .../quasseldroid_ng/service/QuasselService.kt | 10 +- app/src/main/res/layout/activity_main.xml | 3 +- app/src/main/res/layout/content_chat_list.xml | 3 - app/src/main/res/layout/content_main.xml | 1 + app/src/main/res/layout/content_nick_list.xml | 2 +- app/src/main/res/values/themes_base.xml | 4 + app/src/main/res/values/themes_quassel.xml | 6 - app/src/main/res/values/themes_solarized.xml | 6 - .../de/kuschku/libquassel/session/Backend.kt | 5 +- .../libquassel/session/SessionManager.kt | 73 +++- malheur/build.gradle.kts | 401 ++++++++++++++++++ malheur/proguard-rules.pro | 21 + malheur/src/main/AndroidManifest.xml | 6 + .../java/de/kuschku/malheur/CrashHandler.kt | 166 ++++++++ .../java/de/kuschku/malheur/data/AppInfo.kt | 7 + .../java/de/kuschku/malheur/data/CrashInfo.kt | 9 + .../de/kuschku/malheur/data/DeviceInfo.kt | 8 + .../de/kuschku/malheur/data/DisplayInfo.kt | 24 ++ .../de/kuschku/malheur/data/ExceptionInfo.kt | 19 + .../de/kuschku/malheur/data/MetricsInfo.kt | 21 + .../java/de/kuschku/malheur/data/Report.kt | 10 + .../de/kuschku/malheur/data/RuntimeInfo.kt | 31 ++ .../de/kuschku/malheur/data/ThreadInfo.kt | 21 + .../de/kuschku/malheur/data/TraceElement.kt | 17 + .../de/kuschku/malheur/util/DisplayHelper.kt | 10 + .../de/kuschku/malheur/util/LogCatHelper.kt | 9 + .../de/kuschku/malheur/util/ProcInfohelper.kt | 13 + .../kuschku/malheur/util/ReflectionHelper.kt | 26 ++ .../kuschku/malheur/util/ThrowableHelper.kt | 11 + settings.gradle | 2 +- 33 files changed, 899 insertions(+), 50 deletions(-) create mode 100644 malheur/build.gradle.kts create mode 100644 malheur/proguard-rules.pro create mode 100644 malheur/src/main/AndroidManifest.xml create mode 100644 malheur/src/main/java/de/kuschku/malheur/CrashHandler.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/AppInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/CrashInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/DeviceInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/DisplayInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/MetricsInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/Report.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/RuntimeInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/ThreadInfo.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/data/TraceElement.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/util/LogCatHelper.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/util/ProcInfohelper.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/util/ReflectionHelper.kt create mode 100644 malheur/src/main/java/de/kuschku/malheur/util/ThrowableHelper.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7f60f209e..1af10372b 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -116,6 +116,7 @@ dependencies { kapt("com.jakewharton:butterknife-compiler:8.7.0") implementation(project(":lib")) + implementation(project(":malheur")) testImplementation("android.arch.persistence.room:testing:1.0.0-alpha9") testImplementation("junit:junit:4.12") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4bc36f436..4e3e33c81 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,7 +3,6 @@ package="de.kuschku.quasseldroid_ng"> <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.READ_LOGS" /> <application android:name=".QuasseldroidNG" diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/QuasseldroidNG.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/QuasseldroidNG.kt index 0be1ee8a0..78add5c87 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/QuasseldroidNG.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/QuasseldroidNG.kt @@ -6,6 +6,7 @@ import android.content.pm.ShortcutInfo import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build +import de.kuschku.malheur.CrashHandler import de.kuschku.quasseldroid_ng.util.compatibility.AndroidCompatibilityUtils import de.kuschku.quasseldroid_ng.util.compatibility.AndroidLoggingHandler import de.kuschku.quasseldroid_ng.util.compatibility.AndroidStreamChannelFactory @@ -17,6 +18,7 @@ class QuasseldroidNG : Application() { } override fun onCreate() { + CrashHandler.init(this, buildConfig = BuildConfig::class.java) super.onCreate() // Init compatibility utils diff --git a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt index 4d0f7ceba..50a88bf38 100644 --- a/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt +++ b/app/src/main/java/de/kuschku/quasseldroid_ng/service/QuasselService.kt @@ -5,10 +5,7 @@ import android.arch.lifecycle.Observer import android.content.Intent import android.os.Binder import de.kuschku.libquassel.protocol.* -import de.kuschku.libquassel.session.Backend -import de.kuschku.libquassel.session.ISession -import de.kuschku.libquassel.session.SessionManager -import de.kuschku.libquassel.session.SocketAddress +import de.kuschku.libquassel.session.* import de.kuschku.quasseldroid_ng.BuildConfig import de.kuschku.quasseldroid_ng.R import de.kuschku.quasseldroid_ng.persistence.QuasselDatabase @@ -114,8 +111,9 @@ class QuasselService : LifecycleService() { supportedProtocols = listOf(Protocol.Datastream) ) sessionManager.state - .distinctUntilChanged() - .debounce(50, TimeUnit.MILLISECONDS) + .filter { it == ConnectionState.DISCONNECTED } + .delay(200, TimeUnit.MILLISECONDS) + .throttleFirst(1, TimeUnit.SECONDS) .toLiveData() .observe(this, Observer { sessionManager.reconnect() diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 98d830b5d..b5f90adae 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -21,7 +21,6 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" app:contentInsetStartWithNavigation="0dp" app:popupTheme="@style/Widget.PopupOverlay"> @@ -94,7 +93,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="start" - android:background="?android:attr/windowBackground" + android:background="?attr/colorBackground" android:fitsSystemWindows="true" app:insetForeground="?attr/colorPrimaryDark"> diff --git a/app/src/main/res/layout/content_chat_list.xml b/app/src/main/res/layout/content_chat_list.xml index a5bf5a98e..228f6f5e9 100644 --- a/app/src/main/res/layout/content_chat_list.xml +++ b/app/src/main/res/layout/content_chat_list.xml @@ -13,8 +13,6 @@ android:id="@+id/chatListToolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" - android:background="?attr/colorPrimary" - android:theme="?attr/actionBarTheme" app:contentInsetStartWithNavigation="0dp" app:popupTheme="@style/Widget.PopupOverlay"> @@ -22,7 +20,6 @@ android:id="@+id/chatListSpinner" android:layout_width="fill_parent" android:layout_height="match_parent" - android:theme="?attr/actionBarTheme" app:popupTheme="@style/Widget.PopupOverlay" /> </android.support.v7.widget.Toolbar> diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index 18b4eb99c..4c0a9bf4f 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -2,6 +2,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" + android:background="?attr/colorBackground" tools:showIn="@layout/activity_main"> <LinearLayout diff --git a/app/src/main/res/layout/content_nick_list.xml b/app/src/main/res/layout/content_nick_list.xml index 78c1b3ab1..eba412b14 100644 --- a/app/src/main/res/layout/content_nick_list.xml +++ b/app/src/main/res/layout/content_nick_list.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="end" - android:background="?android:attr/windowBackground" + android:background="?attr/colorBackground" android:clipToPadding="false" android:fitsSystemWindows="true" tools:showIn="@layout/activity_main" /> diff --git a/app/src/main/res/values/themes_base.xml b/app/src/main/res/values/themes_base.xml index 9ca5f8a3c..2c4ea184d 100644 --- a/app/src/main/res/values/themes_base.xml +++ b/app/src/main/res/values/themes_base.xml @@ -36,6 +36,8 @@ <item name="actionBarTheme">@style/Widget.AppBarOverlay</item> <item name="formatBarTheme">@style/Widget.AppBarOverlay</item> + <item name="android:windowBackground">@null</item> + <item name="windowActionModeOverlay">true</item> <item name="colorDivider">#1FFFFFFF</item> @@ -68,6 +70,8 @@ <item name="actionBarTheme">@style/Widget.AppBarOverlay</item> <item name="formatBarTheme">@style/Widget.AppBarOverlay.Light</item> + <item name="android:windowBackground">@null</item> + <item name="windowActionModeOverlay">true</item> <item name="colorDivider">#1F000000</item>> diff --git a/app/src/main/res/values/themes_quassel.xml b/app/src/main/res/values/themes_quassel.xml index 1c6cac46f..00e32688d 100644 --- a/app/src/main/res/values/themes_quassel.xml +++ b/app/src/main/res/values/themes_quassel.xml @@ -31,7 +31,6 @@ <item name="colorForegroundMirc">0x1</item> <item name="colorBackground">#FAFAFA</item> - <item name="android:windowBackground">@color/quasselLight_background</item> <item name="colorBackgroundHighlight">#ff8811</item> <item name="colorBackgroundSecondary">@null</item> <item name="colorBackgroundCard">#FFFFFF</item> @@ -42,8 +41,6 @@ <item name="colorTintHighlight">#ff8811</item> </style> - <color name="quasselLight_background">#FAFAFA</color> - <string name="themeQuasselDarkName">Quasselâ„¢ (Dark)</string> <string name="themeQuasselDarkId">QUASSEL_DARK</string> @@ -74,7 +71,6 @@ <item name="colorForegroundMirc">0x0</item> <item name="colorBackground">#303030</item> - <item name="android:windowBackground">@color/quasselDark_background</item> <item name="colorBackgroundHighlight">#ff8811</item> <item name="colorBackgroundSecondary">@null</item> <item name="colorBackgroundCard">#424242</item> @@ -84,6 +80,4 @@ <item name="colorTintMessage">#2277dd</item> <item name="colorTintHighlight">#ff8811</item> </style> - - <color name="quasselDark_background">#303030</color> </resources> diff --git a/app/src/main/res/values/themes_solarized.xml b/app/src/main/res/values/themes_solarized.xml index 8d4d23520..12973f3c5 100644 --- a/app/src/main/res/values/themes_solarized.xml +++ b/app/src/main/res/values/themes_solarized.xml @@ -37,7 +37,6 @@ <item name="colorForegroundMirc">0xF</item> <item name="colorBackground">#FDF6E3</item> - <item name="android:windowBackground">@color/solarizedLight_background</item> <item name="colorBackgroundHighlight">#268bd2</item> <item name="colorBackgroundSecondary">@null</item> <item name="colorBackgroundCard">#EEE8D5</item> @@ -48,8 +47,6 @@ <item name="colorTintHighlight">#ff8811</item> </style> - <color name="solarizedLight_background">#FDF6E3</color> - <string name="themeSolarizedDarkName">Solarized (Dark)</string> <string name="themeSolarizedDarkId">SOLARIZED_DARK</string> @@ -84,7 +81,6 @@ <item name="colorForegroundMirc">0xF</item> <item name="colorBackground">#002B36</item> - <item name="android:windowBackground">@color/solarizedDark_background</item> <item name="colorBackgroundHighlight">#268bd2</item> <item name="colorBackgroundSecondary">@null</item> <item name="colorBackgroundCard">#073642</item> @@ -94,6 +90,4 @@ <item name="colorTintMessage">#2277dd</item> <item name="colorTintHighlight">#ff8811</item> </style> - - <color name="solarizedDark_background">#002B36</color> </resources> diff --git a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt index 634d1da45..ff51c4acb 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/Backend.kt @@ -1,8 +1,9 @@ package de.kuschku.libquassel.session interface Backend { - fun connectUnlessConnected(address: SocketAddress, user: String, pass: String) - fun connect(address: SocketAddress, user: String, pass: String) + fun connectUnlessConnected(address: SocketAddress, user: String, pass: String, reconnect: Boolean) + fun connect(address: SocketAddress, user: String, pass: String, reconnect: Boolean) + fun reconnect() fun disconnect() fun sessionManager(): SessionManager } diff --git a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt index 0729334f2..cb4006c0a 100644 --- a/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt +++ b/lib/src/main/java/de/kuschku/libquassel/session/SessionManager.kt @@ -13,35 +13,33 @@ import io.reactivex.Observable import io.reactivex.subjects.BehaviorSubject import javax.net.ssl.X509TrustManager -class SessionManager( - private val offlineSession: ISession -) : ISession { +class SessionManager(offlineSession: ISession) : ISession { override val aliasManager: AliasManager? - get() = session.or(offlineSession).aliasManager + get() = session.or(lastSession).aliasManager override val backlogManager: BacklogManager? - get() = session.or(offlineSession).backlogManager + get() = session.or(lastSession).backlogManager override val bufferSyncer: BufferSyncer? - get() = session.or(offlineSession).bufferSyncer + get() = session.or(lastSession).bufferSyncer override val bufferViewManager: BufferViewManager? - get() = session.or(offlineSession).bufferViewManager + get() = session.or(lastSession).bufferViewManager override val certManagers: Map<IdentityId, CertManager> - get() = session.or(offlineSession).certManagers + get() = session.or(lastSession).certManagers override val coreInfo: CoreInfo? - get() = session.or(offlineSession).coreInfo + get() = session.or(lastSession).coreInfo override val dccConfig: DccConfig? - get() = session.or(offlineSession).dccConfig + get() = session.or(lastSession).dccConfig override val identities: Map<IdentityId, Identity> - get() = session.or(offlineSession).identities + get() = session.or(lastSession).identities override val ignoreListManager: IgnoreListManager? - get() = session.or(offlineSession).ignoreListManager + get() = session.or(lastSession).ignoreListManager override val ircListHelper: IrcListHelper? - get() = session.or(offlineSession).ircListHelper + get() = session.or(lastSession).ircListHelper override val networks: Map<NetworkId, Network> - get() = session.or(offlineSession).networks + get() = session.or(lastSession).networks override val networkConfig: NetworkConfig? - get() = session.or(offlineSession).networkConfig + get() = session.or(lastSession).networkConfig - override fun close() = session.or(offlineSession).close() + override fun close() = session.or(lastSession).close() init { log(LoggingHandler.LogLevel.INFO, "Session", "Session created") @@ -60,25 +58,56 @@ class SessionManager( clientData: ClientData, trustManager: X509TrustManager, address: SocketAddress, - handlerService: HandlerService, - userData: Pair<String, String> + handlerService: () -> HandlerService, + userData: Pair<String, String>, + shouldReconnect: Boolean = false ) { inProgressSession.value.close() - inProgressSession.onNext(Session(clientData, trustManager, address, handlerService, userData)) + lastClientData = clientData + lastTrustManager = trustManager + lastAddress = address + lastHandlerService = handlerService + lastUserData = userData + lastShouldReconnect = shouldReconnect + inProgressSession.onNext(Session(clientData, trustManager, address, handlerService(), userData)) + } + + private var lastClientData: ClientData? = null + private var lastTrustManager: X509TrustManager? = null + private var lastAddress: SocketAddress? = null + private var lastHandlerService: (() -> HandlerService)? = null + private var lastUserData: Pair<String, String>? = null + private var lastShouldReconnect = false + + fun reconnect() { + if (lastShouldReconnect) { + val clientData = lastClientData + val trustManager = lastTrustManager + val address = lastAddress + val handlerService = lastHandlerService + val userData = lastUserData + + if (clientData != null && trustManager != null && address != null && handlerService != null && userData != null) { + ifDisconnected { + connect(clientData, trustManager, address, handlerService, userData) + } + } + } } fun disconnect() { inProgressSession.value inProgressSession.value.close() - inProgressSession.onNext(offlineSession) + inProgressSession.onNext(ISession.NULL) } - private var inProgressSession = BehaviorSubject.createDefault(offlineSession) + private var inProgressSession = BehaviorSubject.createDefault(ISession.NULL) + private var lastSession: ISession = offlineSession override val state: Observable<ConnectionState> = inProgressSession.switchMap { it.state } val session: Observable<ISession> = state.map { connectionState -> if (connectionState == ConnectionState.CONNECTED) inProgressSession.value else - offlineSession + lastSession } } diff --git a/malheur/build.gradle.kts b/malheur/build.gradle.kts new file mode 100644 index 000000000..558449777 --- /dev/null +++ b/malheur/build.gradle.kts @@ -0,0 +1,401 @@ +import com.android.build.gradle.LibraryExtension +import org.gradle.api.Project +import org.gradle.kotlin.dsl.* + +apply { + plugin("com.android.library") + plugin("kotlin-android") + plugin("kotlin-kapt") +} + +android { + compileSdkVersion(26) + buildToolsVersion("26.0.1") + + defaultConfig { + minSdkVersion(9) + targetSdkVersion(26) + + consumerProguardFiles("proguard-rules.pro") + } +} + +dependencies { + implementation(kotlin("stdlib")) + + implementation("com.google.code.gson:gson:2.2.4") +} + +fun Project.android(f: LibraryExtension.() -> Unit) + = configure(f) + +fun DependencyHandlerScope.androidJacocoAgent(dependencyNotation: Any) + = "androidJacocoAgent"(dependencyNotation) + +fun DependencyHandlerScope.androidJacocoAgent(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidJacocoAgent"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidJacocoAnt(dependencyNotation: Any) + = "androidJacocoAnt"(dependencyNotation) + +fun DependencyHandlerScope.androidJacocoAnt(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidJacocoAnt"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestAnnotationProcessor(dependencyNotation: Any) + = "androidTestAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.androidTestAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestApk(dependencyNotation: Any) + = "androidTestApk"(dependencyNotation) + +fun DependencyHandlerScope.androidTestApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestImplementation(dependencyNotation: Any) + = "androidTestImplementation"(dependencyNotation) + +fun DependencyHandlerScope.androidTestImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestJackPlugin(dependencyNotation: Any) + = "androidTestJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.androidTestJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestProvided(dependencyNotation: Any) + = "androidTestProvided"(dependencyNotation) + +fun DependencyHandlerScope.androidTestProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.androidTestWearApp(dependencyNotation: Any) + = "androidTestWearApp"(dependencyNotation) + +fun DependencyHandlerScope.androidTestWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "androidTestWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.annotationProcessor(dependencyNotation: Any) + = "annotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.annotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "annotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.apk(dependencyNotation: Any) + = "apk"(dependencyNotation) + +fun DependencyHandlerScope.apk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "apk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.archives(dependencyNotation: Any) + = "archives"(dependencyNotation) + +fun DependencyHandlerScope.archives(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "archives"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.implementation(dependencyNotation: Any) + = "implementation"(dependencyNotation) + +fun DependencyHandlerScope.implementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "implementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugAnnotationProcessor(dependencyNotation: Any) + = "debugAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.debugAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugApk(dependencyNotation: Any) + = "debugApk"(dependencyNotation) + +fun DependencyHandlerScope.debugApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugImplementation(dependencyNotation: Any) + = "debugImplementation"(dependencyNotation) + +fun DependencyHandlerScope.debugImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugJackPlugin(dependencyNotation: Any) + = "debugJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.debugJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugProvided(dependencyNotation: Any) + = "debugProvided"(dependencyNotation) + +fun DependencyHandlerScope.debugProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.debugWearApp(dependencyNotation: Any) + = "debugWearApp"(dependencyNotation) + +fun DependencyHandlerScope.debugWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "debugWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.default(dependencyNotation: Any) + = "default"(dependencyNotation) + +fun DependencyHandlerScope.default(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "default"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.jackPlugin(dependencyNotation: Any) + = "jackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.jackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "jackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kapt(dependencyNotation: Any) + = "kapt"(dependencyNotation) + +fun DependencyHandlerScope.kapt(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kapt"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptAndroidTest(dependencyNotation: Any) + = "kaptAndroidTest"(dependencyNotation) + +fun DependencyHandlerScope.kaptAndroidTest(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptAndroidTest"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptDebug(dependencyNotation: Any) + = "kaptDebug"(dependencyNotation) + +fun DependencyHandlerScope.kaptDebug(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptDebug"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptRelease(dependencyNotation: Any) + = "kaptRelease"(dependencyNotation) + +fun DependencyHandlerScope.kaptRelease(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptRelease"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptTest(dependencyNotation: Any) + = "kaptTest"(dependencyNotation) + +fun DependencyHandlerScope.kaptTest(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptTest"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptTestDebug(dependencyNotation: Any) + = "kaptTestDebug"(dependencyNotation) + +fun DependencyHandlerScope.kaptTestDebug(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptTestDebug"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.kaptTestRelease(dependencyNotation: Any) + = "kaptTestRelease"(dependencyNotation) + +fun DependencyHandlerScope.kaptTestRelease(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "kaptTestRelease"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.provided(dependencyNotation: Any) + = "provided"(dependencyNotation) + +fun DependencyHandlerScope.provided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "provided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseAnnotationProcessor(dependencyNotation: Any) + = "releaseAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.releaseAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseApk(dependencyNotation: Any) + = "releaseApk"(dependencyNotation) + +fun DependencyHandlerScope.releaseApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseImplementation(dependencyNotation: Any) + = "releaseImplementation"(dependencyNotation) + +fun DependencyHandlerScope.releaseImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseJackPlugin(dependencyNotation: Any) + = "releaseJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.releaseJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseProvided(dependencyNotation: Any) + = "releaseProvided"(dependencyNotation) + +fun DependencyHandlerScope.releaseProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.releaseWearApp(dependencyNotation: Any) + = "releaseWearApp"(dependencyNotation) + +fun DependencyHandlerScope.releaseWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "releaseWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testAnnotationProcessor(dependencyNotation: Any) + = "testAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.testAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testApk(dependencyNotation: Any) + = "testApk"(dependencyNotation) + +fun DependencyHandlerScope.testApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testImplementation(dependencyNotation: Any) + = "testImplementation"(dependencyNotation) + +fun DependencyHandlerScope.testImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugAnnotationProcessor(dependencyNotation: Any) + = "testDebugAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.testDebugAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugApk(dependencyNotation: Any) + = "testDebugApk"(dependencyNotation) + +fun DependencyHandlerScope.testDebugApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugImplementation(dependencyNotation: Any) + = "testDebugImplementation"(dependencyNotation) + +fun DependencyHandlerScope.testDebugImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugJackPlugin(dependencyNotation: Any) + = "testDebugJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.testDebugJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugProvided(dependencyNotation: Any) + = "testDebugProvided"(dependencyNotation) + +fun DependencyHandlerScope.testDebugProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testDebugWearApp(dependencyNotation: Any) + = "testDebugWearApp"(dependencyNotation) + +fun DependencyHandlerScope.testDebugWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testDebugWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testJackPlugin(dependencyNotation: Any) + = "testJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.testJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testProvided(dependencyNotation: Any) + = "testProvided"(dependencyNotation) + +fun DependencyHandlerScope.testProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseAnnotationProcessor(dependencyNotation: Any) + = "testReleaseAnnotationProcessor"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseAnnotationProcessor(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseAnnotationProcessor"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseApk(dependencyNotation: Any) + = "testReleaseApk"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseApk(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseApk"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseImplementation(dependencyNotation: Any) + = "testReleaseImplementation"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseImplementation(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseImplementation"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseJackPlugin(dependencyNotation: Any) + = "testReleaseJackPlugin"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseJackPlugin(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseJackPlugin"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseProvided(dependencyNotation: Any) + = "testReleaseProvided"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseProvided(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseProvided"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testReleaseWearApp(dependencyNotation: Any) + = "testReleaseWearApp"(dependencyNotation) + +fun DependencyHandlerScope.testReleaseWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testReleaseWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.testWearApp(dependencyNotation: Any) + = "testWearApp"(dependencyNotation) + +fun DependencyHandlerScope.testWearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "testWearApp"(dependencyNotation, dependencyConfiguration) + +fun DependencyHandlerScope.wearApp(dependencyNotation: Any) + = "wearApp"(dependencyNotation) + +fun DependencyHandlerScope.wearApp(dependencyNotation: String, + dependencyConfiguration: ExternalModuleDependency.() -> Unit) + = "wearApp"(dependencyNotation, dependencyConfiguration) diff --git a/malheur/proguard-rules.pro b/malheur/proguard-rules.pro new file mode 100644 index 000000000..8c9db7a6d --- /dev/null +++ b/malheur/proguard-rules.pro @@ -0,0 +1,21 @@ +# Gson uses generic type information stored in a class file when working with fields. Proguard +# removes such information by default, so configure it to keep all of it. +-keepattributes Signature + +# For using GSON @Expose annotation +-keepattributes *Annotation* + +# Gson specific classes +-dontwarn sun.misc.** +#-keep class com.google.gson.stream.** { *; } + +# Application classes that will be serialized/deserialized over Gson +-keep class com.google.gson.examples.android.model.** { *; } + +# Prevent proguard from stripping interface information from TypeAdapterFactory, +# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) +-keep class * implements com.google.gson.TypeAdapterFactory +-keep class * implements com.google.gson.JsonSerializer +-keep class * implements com.google.gson.JsonDeserializer + +-keep class **.BuildConfig { *; } diff --git a/malheur/src/main/AndroidManifest.xml b/malheur/src/main/AndroidManifest.xml new file mode 100644 index 000000000..89c8fa0f2 --- /dev/null +++ b/malheur/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="de.kuschku.malheur"> + + <uses-permission android:name="android.permission.READ_LOGS" /> +</manifest> diff --git a/malheur/src/main/java/de/kuschku/malheur/CrashHandler.kt b/malheur/src/main/java/de/kuschku/malheur/CrashHandler.kt new file mode 100644 index 000000000..415e19ddc --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/CrashHandler.kt @@ -0,0 +1,166 @@ +package de.kuschku.malheur + +import android.annotation.SuppressLint +import android.app.Application +import android.content.pm.PackageManager +import android.os.Build +import android.os.Debug +import android.os.Environment +import android.os.Process +import android.provider.Settings +import com.google.gson.GsonBuilder +import de.kuschku.malheur.data.* +import de.kuschku.malheur.util.* +import java.io.File +import java.text.SimpleDateFormat +import java.util.* + +object CrashHandler { + private lateinit var packageManager: PackageManager + private lateinit var config: ReportConfiguration + + private lateinit var originalHandler: Thread.UncaughtExceptionHandler + + private val logcatTimeFormatter = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US) + private val gson = GsonBuilder().disableHtmlEscaping().setPrettyPrinting().create() + + data class ReportConfiguration( + val crash: Boolean = true, + val crashCause: Boolean = true, + val crashException: Boolean = true, + val crashActiveThread: Boolean = true, + val crashStartTime: Boolean = true, + val crashCrashTime: Boolean = true, + val threads: Boolean = true, + val logcat: List<String> = listOf("main", "events", "crash"), + val application: Boolean = true, + val applicationVersionName: Boolean = true, + val applicationVersionCode: Boolean = true, + val applicationBuildConfig: Boolean = true, + val device: Boolean = true, + val deviceBuild: Boolean = true, + val deviceVersion: Boolean = true, + val deviceInstallationId: Boolean = true, + val deviceProcessor: Boolean = true, + val deviceRuntime: Boolean = true, + val environment: Boolean = true, + val environmentPaths: Boolean = true, + val environmentMemory: Boolean = true + ) + + @SuppressLint("HardwareIds") + fun init(application: Application, configuration: ReportConfiguration = ReportConfiguration(), + buildConfig: Class<*>?) { + val startTime = Date() + packageManager = application.packageManager + originalHandler = Thread.getDefaultUncaughtExceptionHandler() + + config = configuration + + Thread.setDefaultUncaughtExceptionHandler { activeThread, throwable -> + Thread { + val pid = Process.myPid().toString() + val crashTime = Date() + try { + val since = logcatTimeFormatter.format(startTime) + val data = Report( + crash = CrashInfo( + cause = orNull(config.crashCause) { + ExceptionInfo(throwable) + }, + exception = orNull(config.crashException) { + throwable.printStackTraceToString() + }, + activeThread = orNull(config.crashActiveThread) { + ThreadInfo(activeThread) + }, + startTime = orNull(config.crashStartTime) { + startTime.time + }, + crashTime = orNull(config.crashCrashTime) { + crashTime.time + } + ), + threads = orNull(config.threads) { + Thread.getAllStackTraces() + .filterKeys { it != Thread.currentThread() } + .map { (thread, stackTrace) -> + ThreadInfo(thread, stackTrace) + } + }, + logcat = config.logcat.map { buffer -> + buffer to readLogCat(since, buffer, pid) + }.toMap(), + application = orNull(config.application) { + AppInfo( + versionName = orNull(config.applicationVersionName) { + packageManager.getPackageInfo(application.packageName, 0).versionName + }, + versionCode = orNull(config.applicationVersionCode) { + packageManager.getPackageInfo(application.packageName, 0).versionCode + }, + buildConfig = orNull(config.applicationBuildConfig) { + reflectionCollectConstants( + buildConfig ?: getBuildConfigClass(application.packageName)) + } + ) + }, + device = orNull(config.device) { + DeviceInfo( + build = orNull(config.deviceBuild) { + reflectionCollectConstants(Build::class.java) + }, + version = orNull(config.deviceVersion) { + reflectionCollectConstants(Build.VERSION::class.java) + }, + installationId = orNull(config.deviceInstallationId) { + Settings.Secure.getString( + application.contentResolver, Settings.Secure.ANDROID_ID + ) + }, + processor = orNull(config.deviceProcessor) { + readProcInfo() + } + ) + }, + environment = orNull(config.environment) { + mapOf( + "paths" to orNull(config.environmentPaths) { + reflectionCollectGetters(Environment::class.java)?.map { (key, value) -> + key to if (value is File) { + value.canonicalPath + } else { + value + } + }?.toMap() + }, + "memory" to orNull(config.environmentMemory) { + val memoryInfo = Debug.MemoryInfo() + Debug.getMemoryInfo(memoryInfo) + MemoryInfo(memoryInfo) + } + ) + } + ) + + val json = gson.toJson(data) + println(json) + } catch (e: Throwable) { + originalHandler.uncaughtException(activeThread, throwable) + } + }.start() + } + } + + private fun getBuildConfigClass(packageName: String) = try { + Class.forName("$packageName.BuildConfig") + } catch (e: ClassNotFoundException) { + null + } + + private fun <T> orNull(condition: Boolean, closure: (() -> T)) = if (condition) { + closure() + } else { + null + } +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/AppInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/AppInfo.kt new file mode 100644 index 000000000..d31bef282 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/AppInfo.kt @@ -0,0 +1,7 @@ +package de.kuschku.malheur.data + +data class AppInfo( + val versionName: String?, + val versionCode: Int?, + val buildConfig: Map<String, Any?>? +) diff --git a/malheur/src/main/java/de/kuschku/malheur/data/CrashInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/CrashInfo.kt new file mode 100644 index 000000000..cae838453 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/CrashInfo.kt @@ -0,0 +1,9 @@ +package de.kuschku.malheur.data + +data class CrashInfo( + val cause: ExceptionInfo?, + val exception: String?, + val activeThread: ThreadInfo?, + val startTime: Long?, + val crashTime: Long? +) diff --git a/malheur/src/main/java/de/kuschku/malheur/data/DeviceInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/DeviceInfo.kt new file mode 100644 index 000000000..bd9a07ac5 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/DeviceInfo.kt @@ -0,0 +1,8 @@ +package de.kuschku.malheur.data + +data class DeviceInfo( + val build: Map<String, Any?>?, + val version: Map<String, Any?>?, + val installationId: String?, + val processor: String? +) diff --git a/malheur/src/main/java/de/kuschku/malheur/data/DisplayInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/DisplayInfo.kt new file mode 100644 index 000000000..38e1b7a2d --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/DisplayInfo.kt @@ -0,0 +1,24 @@ +package de.kuschku.malheur.data + +import android.view.Display +import de.kuschku.malheur.util.getMetrics + +data class DisplayInfo( + val width: Int, + val height: Int, + val pixelFormat: Int, + val refreshRate: Float, + val isHdr: Boolean, + val isWideGamut: Boolean, + val metrics: MetricsInfo +) { + constructor(display: Display) : this( + width = display.width, + height = display.height, + pixelFormat = display.pixelFormat, + refreshRate = display.refreshRate, + isHdr = display.isHdr, + isWideGamut = display.isWideColorGamut, + metrics = MetricsInfo(display.getMetrics()) + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt new file mode 100644 index 000000000..73befa667 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/ExceptionInfo.kt @@ -0,0 +1,19 @@ +package de.kuschku.malheur.data + +data class ExceptionInfo( + val type: String?, + val message: String?, + val localizedMessage: String?, + val stackTrace: List<TraceElement>?, + val suppressed: List<ExceptionInfo>?, + val cause: ExceptionInfo? +) { + constructor(throwable: Throwable) : this( + type = throwable.javaClass.canonicalName, + message = throwable.message, + localizedMessage = throwable.localizedMessage, + stackTrace = throwable.stackTrace?.map(::TraceElement), + suppressed = throwable.suppressed?.map(::ExceptionInfo), + cause = throwable.cause?.let(::ExceptionInfo) + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/MetricsInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/MetricsInfo.kt new file mode 100644 index 000000000..359f86302 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/MetricsInfo.kt @@ -0,0 +1,21 @@ +package de.kuschku.malheur.data + +import android.util.DisplayMetrics + +data class MetricsInfo( + val density: Float, + val scaledDensity: Float, + val widthPixels: Int, + val heightPixels: Int, + val xdpi: Float, + val ydpi: Float +) { + constructor(metrics: DisplayMetrics) : this( + density = metrics.density, + scaledDensity = metrics.scaledDensity, + widthPixels = metrics.widthPixels, + heightPixels = metrics.heightPixels, + xdpi = metrics.xdpi, + ydpi = metrics.ydpi + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/Report.kt b/malheur/src/main/java/de/kuschku/malheur/data/Report.kt new file mode 100644 index 000000000..d0dfbb9dd --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/Report.kt @@ -0,0 +1,10 @@ +package de.kuschku.malheur.data + +data class Report( + val crash: CrashInfo?, + val threads: List<ThreadInfo>?, + val logcat: Map<String, List<String>?>?, + val application: AppInfo?, + val device: DeviceInfo?, + val environment: Map<String, Any?>? +) diff --git a/malheur/src/main/java/de/kuschku/malheur/data/RuntimeInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/RuntimeInfo.kt new file mode 100644 index 000000000..033fdffe9 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/RuntimeInfo.kt @@ -0,0 +1,31 @@ +package de.kuschku.malheur.data + +import android.os.Debug + +data class MemoryInfo( + var dalvikPss: Int?, + var dalvikPrivateDirty: Int?, + var dalvikSharedDirty: Int?, + + var nativePss: Int?, + var nativePrivateDirty: Int?, + var nativeSharedDirty: Int?, + + var otherPss: Int?, + var otherPrivateDirty: Int?, + var otherSharedDirty: Int? +) { + constructor(memoryInfo: Debug.MemoryInfo?) : this( + dalvikPss = memoryInfo?.dalvikPss, + dalvikPrivateDirty = memoryInfo?.dalvikPrivateDirty, + dalvikSharedDirty = memoryInfo?.dalvikSharedDirty, + + nativePss = memoryInfo?.nativePss, + nativePrivateDirty = memoryInfo?.nativePrivateDirty, + nativeSharedDirty = memoryInfo?.nativeSharedDirty, + + otherPss = memoryInfo?.otherPss, + otherPrivateDirty = memoryInfo?.otherPrivateDirty, + otherSharedDirty = memoryInfo?.otherSharedDirty + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/ThreadInfo.kt b/malheur/src/main/java/de/kuschku/malheur/data/ThreadInfo.kt new file mode 100644 index 000000000..e80b4fc76 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/ThreadInfo.kt @@ -0,0 +1,21 @@ +package de.kuschku.malheur.data + +data class ThreadInfo( + val id: Long?, + val name: String?, + val group: String?, + val status: String?, + val stackTrace: List<TraceElement>?, + val isDaemon: Boolean?, + val priority: Int? +) { + constructor(thread: Thread, stackTrace: Array<StackTraceElement> = thread.stackTrace) : this( + id = thread.id, + name = thread.name, + group = thread.threadGroup?.name, + status = thread.state?.name, + stackTrace = ArrayList(stackTrace.map(::TraceElement)), + isDaemon = thread.isDaemon, + priority = thread.priority + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/data/TraceElement.kt b/malheur/src/main/java/de/kuschku/malheur/data/TraceElement.kt new file mode 100644 index 000000000..da84bbdd2 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/data/TraceElement.kt @@ -0,0 +1,17 @@ +package de.kuschku.malheur.data + +data class TraceElement( + val className: String?, + val methodName: String?, + val fileName: String?, + val lineNumber: Int?, + val isNative: Boolean? +) { + constructor(element: StackTraceElement) : this( + className = element.className, + methodName = element.methodName, + fileName = element.fileName, + lineNumber = element.lineNumber, + isNative = element.isNativeMethod + ) +} diff --git a/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt new file mode 100644 index 000000000..71cc22a4e --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/util/DisplayHelper.kt @@ -0,0 +1,10 @@ +package de.kuschku.malheur.util + +import android.util.DisplayMetrics +import android.view.Display + +fun Display.getMetrics(): DisplayMetrics { + val metrics = DisplayMetrics() + getMetrics(metrics) + return metrics +} diff --git a/malheur/src/main/java/de/kuschku/malheur/util/LogCatHelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/LogCatHelper.kt new file mode 100644 index 000000000..8837570c4 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/util/LogCatHelper.kt @@ -0,0 +1,9 @@ +package de.kuschku.malheur.util + +fun readLogCat(since: String, buffer: String, pid: String) = ProcessBuilder() + .command("logcat", "-t", since, "-b", buffer, "--pid", pid) + .redirectErrorStream(true) + .start() + .inputStream + .bufferedReader(Charsets.UTF_8) + .readLines() diff --git a/malheur/src/main/java/de/kuschku/malheur/util/ProcInfohelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/ProcInfohelper.kt new file mode 100644 index 000000000..0940b565a --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/util/ProcInfohelper.kt @@ -0,0 +1,13 @@ +package de.kuschku.malheur.util + +import java.io.File + +fun readProcInfo() = File("/proc/cpuinfo") + .bufferedReader(Charsets.UTF_8) + .lineSequence() + .map { line -> line.split(":") } + .filter { split -> split.size == 2 } + .map { (key, value) -> key.trim() to value.trim() } + .filter { (key, _) -> key == "Hardware" } + .map { (_, value) -> value } + .firstOrNull() diff --git a/malheur/src/main/java/de/kuschku/malheur/util/ReflectionHelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/ReflectionHelper.kt new file mode 100644 index 000000000..669bb8a73 --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/util/ReflectionHelper.kt @@ -0,0 +1,26 @@ +package de.kuschku.malheur.util + +fun reflectionCollectConstants(klass: Class<*>?) = klass?.declaredFields + ?.mapNotNull { + var result: Pair<String, Any?>? = null + try { + result = it.name to it.get(null) + } catch (e: IllegalAccessException) { + } catch (e: IllegalArgumentException) { + } + result + }?.toMap() + +fun <T> reflectionCollectGetters(klass: Class<T>?) = klass?.declaredMethods + ?.filter { it.parameterTypes.isEmpty() && it.returnType != Void::class.java } + ?.filter { it.name != "getClass" } + ?.filter { it.name.startsWith("get") || it.name.startsWith("is") } + ?.mapNotNull { + var result: Pair<String, Any?>? = null + try { + result = it.name to it.invoke(it) + } catch (e: IllegalAccessException) { + } catch (e: IllegalArgumentException) { + } + result + }?.toMap() diff --git a/malheur/src/main/java/de/kuschku/malheur/util/ThrowableHelper.kt b/malheur/src/main/java/de/kuschku/malheur/util/ThrowableHelper.kt new file mode 100644 index 000000000..c797860ed --- /dev/null +++ b/malheur/src/main/java/de/kuschku/malheur/util/ThrowableHelper.kt @@ -0,0 +1,11 @@ +package de.kuschku.malheur.util + +import java.io.PrintWriter +import java.io.StringWriter + +fun Throwable.printStackTraceToString(): String? { + val result = StringWriter() + val printWriter = PrintWriter(result) + printStackTrace(printWriter) + return result.toString() +} diff --git a/settings.gradle b/settings.gradle index 6c91f3880..f0ccacf73 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':invokerannotations', ':invokergenerator', ':lib', ':app' +include ':invokerannotations', ':invokergenerator', ':lib', ':app', ':malheur' -- GitLab