Skip to content

Commit

Permalink
Add crash tracking and viewing.
Browse files Browse the repository at this point in the history
  • Loading branch information
camporter committed Jul 2, 2023
1 parent 59cde9a commit 7695cc9
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 1 deletion.
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ dependencies {
// gif support
implementation("io.coil-kt:coil-gif:2.4.0")

// crash handling
implementation("com.github.FunkyMuse:Crashy:1.2.0")

// To use Kotlin annotation processing tool
ksp("androidx.room:room-compiler:2.5.2")

Expand Down
9 changes: 9 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,15 @@
<!--#INSTANCE_LIST_END#-->
</intent-filter>
</activity>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="com.crazylegend.crashyreporter.initializer.CrashyInitializer"
android:value="androidx.startup" />
</provider>
</application>

</manifest>
7 changes: 7 additions & 0 deletions app/src/main/java/com/jerboa/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import com.jerboa.ui.components.settings.about.AboutActivity
import com.jerboa.ui.components.settings.account.AccountSettingsActivity
import com.jerboa.ui.components.settings.account.AccountSettingsViewModel
import com.jerboa.ui.components.settings.account.AccountSettingsViewModelFactory
import com.jerboa.ui.components.settings.crashes.CrashesActivity
import com.jerboa.ui.components.settings.lookandfeel.LookAndFeelActivity
import com.jerboa.ui.theme.JerboaTheme

Expand Down Expand Up @@ -591,6 +592,12 @@ class MainActivity : AppCompatActivity() {
usePrivateTabs = appSettings.usePrivateTabs,
)
}

composable(route = Route.CRASHES) {
CrashesActivity(
navController = navController,
)
}
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions app/src/main/java/com/jerboa/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.pager.PagerState
import androidx.compose.material3.DrawerState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.TabPosition
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateListOf
Expand Down Expand Up @@ -768,6 +770,24 @@ fun scrollToTop(
}
}

fun showSnackbar(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
message: String,
actionLabel: String?,
withDismissAction: Boolean = false,
snackbarDuration: SnackbarDuration,
) {
scope.launch {
snackbarHostState.showSnackbar(
message,
actionLabel,
withDismissAction,
snackbarDuration,
)
}
}

// https://stackoverflow.com/questions/69234880/how-to-get-intent-data-in-a-composable
fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
Expand Down
15 changes: 14 additions & 1 deletion app/src/main/java/com/jerboa/ui/components/common/AppBars.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.google.accompanist.flowlayout.FlowCrossAxisAlignment
import com.google.accompanist.flowlayout.FlowMainAxisAlignment
import com.google.accompanist.flowlayout.FlowRow
Expand All @@ -66,6 +67,7 @@ fun SimpleTopAppBar(
text: String,
navController: NavController,
scrollBehavior: TopAppBarScrollBehavior? = null,
actions: @Composable RowScope.() -> Unit = {},
) {
TopAppBar(
scrollBehavior = scrollBehavior,
Expand All @@ -82,9 +84,18 @@ fun SimpleTopAppBar(
)
}
},
actions = actions,
)
}

@Preview
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleTopAppBarPreview() {
SimpleTopAppBar(text = "Preview", navController = rememberNavController()) {
}
}

@Composable
fun BottomAppBarAll(
selectedTab: BottomNavTab,
Expand Down Expand Up @@ -586,5 +597,7 @@ fun Modifier.simpleVerticalScrollbar(
fun LoadingBar(
padding: PaddingValues = PaddingValues(0.dp),
) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth().padding(padding).testTag("jerboa:loading"))
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth().padding(padding).testTag("jerboa:loading"),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,5 @@ fun NavController.toAccountSettings() = navigate(Route.ACCOUNT_SETTINGS)
fun NavController.toLookAndFeel() = navigate(Route.LOOK_AND_FEEL)

fun NavController.toAbout() = navigate(Route.ABOUT)

fun NavController.toCrashes() = navigate(Route.CRASHES)
1 change: 1 addition & 0 deletions app/src/main/java/com/jerboa/ui/components/common/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ object Route {
const val LOOK_AND_FEEL = "lookAndFeel"
const val ACCOUNT_SETTINGS = "accountSettings"
const val ABOUT = "about"
const val CRASHES = "crashes"

class CommunityFromIdArgs(val id: Int) {
constructor(navBackStackEntry: NavBackStackEntry) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.alorma.compose.settings.ui.SettingsMenuLink
import com.jerboa.R
import com.jerboa.openLink
import com.jerboa.ui.components.common.SimpleTopAppBar
import com.jerboa.ui.components.common.toCrashes

const val githubUrl = "https://github.com/dessalines/jerboa"
const val jerboaMatrixChat = "https://matrix.to/#/#jerboa-dev:matrix.org"
Expand Down Expand Up @@ -97,6 +98,16 @@ fun AboutActivity(
openLink("$githubUrl/issues")
},
)
SettingsMenuLink(
title = { Text(stringResource(R.string.crashes)) },
icon = {
Icon(
imageVector = Icons.Outlined.Build,
contentDescription = null,
)
},
onClick = { navController.toCrashes() },
)
SettingsMenuLink(
title = { Text(stringResource(R.string.settings_about_developer_matrix_chatroom)) },
icon = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package com.jerboa.ui.components.settings.crashes

import android.content.Context
import android.util.Log
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ContentCopy
import androidx.compose.material.icons.outlined.Delete
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.crazylegend.crashyreporter.CrashyReporter
import com.jerboa.R
import com.jerboa.copyToClipboard
import com.jerboa.showSnackbar
import com.jerboa.ui.components.common.SimpleTopAppBar
import com.jerboa.ui.theme.MEDIUM_PADDING
import com.jerboa.ui.theme.SMALL_PADDING

@Composable
fun CrashesActivity(
navController: NavController,
) {
Log.d("jerboa", "Got to Crashes activity")

val ctx = LocalContext.current

val crashes = CrashyReporter.getLogsAsStrings()?.toMutableStateList() ?: run { mutableStateListOf() }

Crashes(ctx = ctx, navController = navController, crashes = crashes)
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Crashes(ctx: Context, navController: NavController, crashes: MutableList<String>) {
val scope = rememberCoroutineScope()

val snackbarHostState = remember { SnackbarHostState() }
val deleteMessage = stringResource(R.string.crashes_all_deleted)

Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = {
SimpleTopAppBar(
text = stringResource(R.string.crashes),
navController = navController,
actions = {
IconButton(
onClick = {
CrashyReporter.purgeLogs()
crashes.clear()
showSnackbar(
scope,
snackbarHostState,
deleteMessage,
null,
true,
SnackbarDuration.Short,
)
},
) {
Icon(
Icons.Outlined.Delete,
contentDescription = stringResource(R.string.crashes_delete),
)
}
},
)
},
content = { padding ->
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.padding(padding),
) {
crashes.forEachIndexed { index, crash ->
CrashItem(ctx = ctx, crash = crash)
}
}
},
)
}

@Composable
fun CrashItem(ctx: Context, crash: String) {
var expanded by remember { mutableStateOf(false) }
val textModifier = Modifier.clickable(onClick = { expanded = !expanded })

Row(
modifier = Modifier
.fillMaxWidth()
.padding(MEDIUM_PADDING),
) {
Column(
modifier = Modifier.padding(MEDIUM_PADDING),
) {
IconButton(onClick = {
copyToClipboard(ctx, crash, "crash")
}) {
Icon(
Icons.Outlined.ContentCopy,
contentDescription = stringResource(R.string.crashes_copy_text),
)
}
}
if (expanded) {
Text(
text = crash,
modifier = textModifier,
)
} else {
Text(
text = crash,
maxLines = 4,
overflow = TextOverflow.Ellipsis,
modifier = textModifier,
)
}
}
Divider(modifier = Modifier.padding(bottom = SMALL_PADDING))
}

@Preview
@Composable
fun CrashesPreview() {
Crashes(
ctx = LocalContext.current,
navController = rememberNavController(),
crashes = mutableListOf(
"A really bad one\nlots\nof\ntrace\nlines\nhere",
"NullPointerException!",
),
)
}
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
<string name="community_sortBy">Sort by</string>
<string name="community_subscribe">Subscribe</string>
<string name="community_users_month">%1$s users / month</string>
<string name="crashes">Crashes</string>
<string name="crashes_all_deleted">All crashes deleted</string>
<string name="crashes_copy_text">Copy crash text</string>
<string name="crashes_delete">Delete all crashes</string>
<string name="createPost_selectCommunity">Select community from list</string>
<string name="create_post_a_title_here">a title here....</string>
<string name="create_post_community">Community</string>
Expand Down

0 comments on commit 7695cc9

Please sign in to comment.