forked from geode-sdk/android-launcher
add geode logs viewer
This commit is contained in:
9 files changed
+477
-32
No files matched your search
@@ -89,6 +89,24 @@
|
||||
<data android:scheme="geode-launcher" android:host="crashes" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".preferences.GeodeLogsListingActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/title_activity_geode_logs"
|
||||
android:theme="@style/Theme.GeodeLauncher">
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="geode-launcher" android:host="geode-logs" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".preferences.GeodeLogViewActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.GeodeLauncher">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".preferences.ApplicationIconActivity"
|
||||
android:exported="false"
|
||||
|
||||
@@ -9,10 +9,9 @@ import com.geode.launcher.utils.LaunchUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.Instant
|
||||
|
||||
data class CrashDump @OptIn(ExperimentalTime::class) constructor(
|
||||
data class CrashDump(
|
||||
val filename: String,
|
||||
val lastModified: Instant
|
||||
)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.geode.launcher.log
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import kotlin.time.Instant
|
||||
|
||||
data class GeodeLog(
|
||||
val filename: String,
|
||||
val lastModified: Instant,
|
||||
val fileSize: Long,
|
||||
)
|
||||
|
||||
class GeodeLogsViewModel(crashDirectory: File) : BaseDirectoryViewModel<GeodeLog>(crashDirectory) {
|
||||
companion object {
|
||||
val Factory: ViewModelProvider.Factory = viewModelFactory {
|
||||
initializer {
|
||||
val application = this[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY] as Application
|
||||
val crashDirectory = LaunchUtils.getGeodeLogsDirectory(application)
|
||||
|
||||
GeodeLogsViewModel(crashDirectory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemKey(item: GeodeLog) = item.filename
|
||||
|
||||
override fun getFileList(): List<GeodeLog> {
|
||||
if (!baseDirectory.exists()) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val children = baseDirectory.listFiles()
|
||||
?: return emptyList()
|
||||
|
||||
return children
|
||||
.map {
|
||||
GeodeLog(
|
||||
filename = it.name,
|
||||
lastModified = Instant.fromEpochMilliseconds(it.lastModified()),
|
||||
fileSize = it.length()
|
||||
)
|
||||
}
|
||||
.sortedByDescending { it.lastModified }
|
||||
}
|
||||
|
||||
suspend fun getFileText(filename: String): List<String> {
|
||||
val logFile = File(baseDirectory, filename)
|
||||
if (!logFile.exists()) {
|
||||
return listOf("File does not exist!")
|
||||
}
|
||||
|
||||
return runCatching {
|
||||
withContext(Dispatchers.IO) {
|
||||
logFile.readLines()
|
||||
}
|
||||
}.getOrDefault(emptyList())
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,6 @@ import com.geode.launcher.utils.LaunchUtils
|
||||
import com.geode.launcher.utils.PreferenceUtils
|
||||
import java.io.File
|
||||
import java.util.Date
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.toJavaInstant
|
||||
|
||||
class CrashLogsActivity : ComponentActivity() {
|
||||
@@ -108,7 +107,6 @@ fun shareCrash(context: Context, filename: String) {
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalTime::class)
|
||||
@Composable
|
||||
fun CrashCard(crashDump: CrashDump, crashViewModel: CrashViewModel, modifier: Modifier = Modifier) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier
|
||||
|
||||
@@ -179,13 +179,27 @@ fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
|
||||
title = stringResource(R.string.preferences_view_logs),
|
||||
icon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_description),
|
||||
painterResource(R.drawable.icon_mobile_code),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
onClick = { onOpenLogs(context) }
|
||||
)
|
||||
|
||||
OptionsButton(
|
||||
title = stringResource(R.string.preferences_view_geode_logs),
|
||||
icon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_description),
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
val launchIntent = Intent(context, GeodeLogsListingActivity::class.java)
|
||||
context.startActivity(launchIntent)
|
||||
}
|
||||
)
|
||||
|
||||
OptionsButton(
|
||||
title = stringResource(R.string.preferences_copy_external_button),
|
||||
description = LaunchUtils.getBaseDirectory(context).path,
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.geode.launcher.preferences
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.OnBackPressedDispatcher
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawingPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.geode.launcher.R
|
||||
import com.geode.launcher.log.GeodeLogsViewModel
|
||||
import com.geode.launcher.ui.theme.GeodeLauncherTheme
|
||||
import com.geode.launcher.ui.theme.LocalTheme
|
||||
import com.geode.launcher.ui.theme.Theme
|
||||
import com.geode.launcher.ui.theme.Typography
|
||||
import com.geode.launcher.ui.theme.robotoMonoFamily
|
||||
import com.geode.launcher.utils.PreferenceUtils
|
||||
|
||||
|
||||
class GeodeLogViewActivity : ComponentActivity() {
|
||||
companion object {
|
||||
const val EXTRA_LOG_VIEW_FILENAME = "EXTRA_LOG_FILENAME"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val filename = intent.getStringExtra(EXTRA_LOG_VIEW_FILENAME)
|
||||
if (filename.isNullOrEmpty()) {
|
||||
val launchIntent = Intent(this, GeodeLogsListingActivity::class.java)
|
||||
startActivity(launchIntent)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
setContent {
|
||||
val themeOption by PreferenceUtils.useIntPreference(PreferenceUtils.Key.THEME)
|
||||
val theme = Theme.fromInt(themeOption)
|
||||
|
||||
val backgroundOption by PreferenceUtils.useBooleanPreference(PreferenceUtils.Key.BLACK_BACKGROUND)
|
||||
val dynamicColorOption by PreferenceUtils.useBooleanPreference(PreferenceUtils.Key.DISABLE_USER_THEME)
|
||||
|
||||
CompositionLocalProvider(LocalTheme provides theme) {
|
||||
GeodeLauncherTheme(theme = theme, blackBackground = backgroundOption, dynamicColor = !dynamicColorOption) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
GeodeLogView(onBackPressedDispatcher, filename)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun GeodeLogView(
|
||||
onBackPressedDispatcher: OnBackPressedDispatcher?,
|
||||
filename: String,
|
||||
geodeLogsViewModel: GeodeLogsViewModel = viewModel(factory = GeodeLogsViewModel.Factory)
|
||||
) {
|
||||
var loadingFile by remember { mutableStateOf(true) }
|
||||
var description by remember { mutableStateOf(emptyList<String>()) }
|
||||
|
||||
LaunchedEffect(filename) {
|
||||
loadingFile = true
|
||||
description = geodeLogsViewModel.getFileText(filename)
|
||||
loadingFile = false
|
||||
}
|
||||
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
Column {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_arrow_back),
|
||||
contentDescription = stringResource(R.string.back_icon_alt)
|
||||
)
|
||||
}
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
filename,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
|
||||
if (loadingFile) {
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
if (!loadingFile) {
|
||||
if (description.isEmpty()) {
|
||||
Text(
|
||||
stringResource(R.string.application_geode_logs_empty),
|
||||
textAlign = TextAlign.Center,
|
||||
style = Typography.bodyLarge,
|
||||
modifier = Modifier.fillMaxSize().padding(8.dp)
|
||||
)
|
||||
} else {
|
||||
SelectionContainer {
|
||||
LazyColumn(modifier = Modifier
|
||||
.padding(innerPadding)
|
||||
.padding(horizontal = 8.dp)
|
||||
) {
|
||||
|
||||
items(description) { line ->
|
||||
Text(
|
||||
line,
|
||||
fontFamily = robotoMonoFamily,
|
||||
style = Typography.bodyMedium
|
||||
)
|
||||
}
|
||||
|
||||
item {
|
||||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.geode.launcher.preferences
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.DocumentsContract
|
||||
import android.text.format.Formatter
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.OnBackPressedDispatcher
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.geode.launcher.R
|
||||
import com.geode.launcher.UserDirectoryProvider
|
||||
import com.geode.launcher.log.GeodeLog
|
||||
import com.geode.launcher.log.GeodeLogsViewModel
|
||||
import com.geode.launcher.ui.theme.GeodeLauncherTheme
|
||||
import com.geode.launcher.ui.theme.LocalTheme
|
||||
import com.geode.launcher.ui.theme.Theme
|
||||
import com.geode.launcher.ui.theme.Typography
|
||||
import com.geode.launcher.ui.theme.robotoMonoFamily
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import com.geode.launcher.utils.PreferenceUtils
|
||||
import java.io.File
|
||||
|
||||
class GeodeLogsListingActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
enableEdgeToEdge()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
val themeOption by PreferenceUtils.useIntPreference(PreferenceUtils.Key.THEME)
|
||||
val theme = Theme.fromInt(themeOption)
|
||||
|
||||
val backgroundOption by PreferenceUtils.useBooleanPreference(PreferenceUtils.Key.BLACK_BACKGROUND)
|
||||
val dynamicColorOption by PreferenceUtils.useBooleanPreference(PreferenceUtils.Key.DISABLE_USER_THEME)
|
||||
|
||||
CompositionLocalProvider(LocalTheme provides theme) {
|
||||
GeodeLauncherTheme(theme = theme, blackBackground = backgroundOption, dynamicColor = !dynamicColorOption) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background
|
||||
) {
|
||||
GeodeLogsListingScreen(onBackPressedDispatcher)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun shareLog(context: Context, filename: String) {
|
||||
val baseDirectory = LaunchUtils.getBaseDirectory(context)
|
||||
val crashPath = File(LaunchUtils.getGeodeLogsDirectory(context), filename)
|
||||
|
||||
if (!crashPath.exists()) {
|
||||
Toast.makeText(context, R.string.launcher_error_export_missing, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
val providerLocation = crashPath.toRelativeString(baseDirectory)
|
||||
val documentsPath = "${UserDirectoryProvider.ROOT}${providerLocation}"
|
||||
|
||||
val uri = DocumentsContract.buildDocumentUri("${context.packageName}.user", documentsPath)
|
||||
|
||||
val shareIntent = Intent(Intent.ACTION_SEND).apply {
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
type = "text/plain"
|
||||
}
|
||||
|
||||
try {
|
||||
context.startActivity(shareIntent)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
Toast.makeText(context, R.string.no_activity_found, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GeodeLogCard(geodeLog: GeodeLog, geodeLogsViewModel: GeodeLogsViewModel, modifier: Modifier = Modifier) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 1.dp, horizontal = 8.dp)
|
||||
.height(64.dp)
|
||||
.then(modifier)
|
||||
.clickable {
|
||||
val launchIntent = Intent(context, GeodeLogViewActivity::class.java).run {
|
||||
putExtra(GeodeLogViewActivity.EXTRA_LOG_VIEW_FILENAME, geodeLog.filename)
|
||||
}
|
||||
context.startActivity(launchIntent)
|
||||
}
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerLow)
|
||||
.padding(vertical = 8.dp, horizontal = 8.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.SpaceAround,
|
||||
modifier = Modifier.fillMaxHeight().offset(x = 12.dp).weight(0.7f)
|
||||
) {
|
||||
Text(
|
||||
geodeLog.filename,
|
||||
fontFamily = robotoMonoFamily,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
)
|
||||
|
||||
val context = LocalContext.current
|
||||
val formattedSize = remember(geodeLog.fileSize) {
|
||||
Formatter.formatShortFileSize(context, geodeLog.fileSize)
|
||||
}
|
||||
Text(formattedSize, style = MaterialTheme.typography.labelMedium)
|
||||
}
|
||||
|
||||
Row(modifier = Modifier.weight(0.3f), horizontalArrangement = Arrangement.End) {
|
||||
val context = LocalContext.current
|
||||
IconButton(onClick = {
|
||||
shareLog(context, geodeLog.filename)
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_share),
|
||||
contentDescription = stringResource(R.string.application_logs_share)
|
||||
)
|
||||
}
|
||||
|
||||
IconButton(onClick = {
|
||||
geodeLogsViewModel.removeFile(geodeLog.filename)
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_delete),
|
||||
contentDescription = stringResource(R.string.application_crashes_delete_one)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun GeodeLogsListingScreen(
|
||||
onBackPressedDispatcher: OnBackPressedDispatcher?,
|
||||
geodeLogsViewModel: GeodeLogsViewModel = viewModel(factory = GeodeLogsViewModel.Factory)
|
||||
) {
|
||||
DirectoryListingScreen(
|
||||
onBackPressedDispatcher,
|
||||
geodeLogsViewModel,
|
||||
titleId = R.string.title_activity_geode_logs,
|
||||
noneAvailableId = R.string.application_geode_logs_no_logs,
|
||||
) { line, shape ->
|
||||
GeodeLogCard(
|
||||
line,
|
||||
geodeLogsViewModel,
|
||||
modifier = Modifier.clip(shape)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -577,33 +577,6 @@ fun AdvancedSettingsGroup(developerModeEnabled: Boolean, snackbarHostState: Snac
|
||||
}
|
||||
)
|
||||
|
||||
var showSafeModeDialog by remember { mutableStateOf(false) }
|
||||
|
||||
OptionsButton(
|
||||
title = stringResource(R.string.preference_launch_safe_mode),
|
||||
icon = {
|
||||
Icon(
|
||||
painterResource(R.drawable.icon_shield),
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
) {
|
||||
showSafeModeDialog = true
|
||||
}
|
||||
|
||||
if (showSafeModeDialog) {
|
||||
SafeModeDialog(onDismiss = {
|
||||
showSafeModeDialog = false
|
||||
}) {
|
||||
val launchIntent = Intent(context, MainActivity::class.java).apply {
|
||||
// hardcode it, why not
|
||||
action = Intent.ACTION_VIEW
|
||||
data = "geode-launcher://main/launch?safe-mode=true".toUri()
|
||||
}
|
||||
context.startActivity(launchIntent)
|
||||
}
|
||||
}
|
||||
|
||||
BackupButton(snackbarHostState)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@
|
||||
<string name="preferences_open_file_manager">Open file manager</string>
|
||||
<string name="preferences_view_logs">View application logs</string>
|
||||
<string name="preferences_view_crashes">View crash dumps</string>
|
||||
<string name="preferences_view_geode_logs">View Geode logs</string>
|
||||
|
||||
<string name="preference_theme_name">Theme</string>
|
||||
<string name="preference_theme_select">Select theme</string>
|
||||
@@ -336,4 +337,9 @@
|
||||
<string name="application_icon_trans">Transgender</string>
|
||||
<string name="application_icon_confirm">Change icon to %1$s?</string>
|
||||
<string name="application_icon_confirm_description">Geode Launcher will close itself to change the application icon.</string>
|
||||
|
||||
<!-- GeodeLogsListingActivity strings -->
|
||||
<string name="title_activity_geode_logs">Geode logs</string>
|
||||
<string name="application_geode_logs_no_logs">No logs available.</string>
|
||||
<string name="application_geode_logs_empty">File was empty.</string>
|
||||
</resources>
|
||||
Reference in new issue
Block a user