Feat: dev settings

This commit is contained in:
wingio 2023-03-24 11:59:58 -04:00
parent 584e5e8b3b
commit eba0fbf29c
13 changed files with 126 additions and 36 deletions

View File

@ -24,7 +24,7 @@ class PreferenceManager(private val context: Context) :
var monet by booleanPreference("monet", Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
var isDeveloper by booleanPreference("is_developer", true)
var isDeveloper by booleanPreference("is_developer", false)
var theme by enumPreference("theme", Theme.SYSTEM)

View File

@ -13,6 +13,7 @@ fun SettingsTextField(
disabled: Boolean = false,
pref: String,
error: Boolean = false,
supportingText: String? = null,
onPrefChange: (String) -> Unit,
) {
Box(modifier = Modifier.padding(horizontal = 18.dp, vertical = 10.dp)) {
@ -23,7 +24,8 @@ fun SettingsTextField(
enabled = !disabled,
label = { Text(label) },
isError = error,
singleLine = true
singleLine = true,
supportingText = if (supportingText != null) { -> Text(supportingText) } else null
)
}
}

View File

@ -1,9 +1,7 @@
package dev.beefers.vendetta.manager.ui.screen.about
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -16,7 +14,6 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Divider
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
@ -30,41 +27,48 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
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.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import coil.compose.AsyncImage
import dev.beefers.vendetta.manager.BuildConfig
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.ui.widgets.about.LinkItem
import dev.beefers.vendetta.manager.ui.widgets.about.ListItem
import dev.beefers.vendetta.manager.ui.widgets.about.UserEntry
import dev.beefers.vendetta.manager.utils.Constants
import dev.beefers.vendetta.manager.utils.getBitmap
import dev.beefers.vendetta.manager.utils.showToast
import org.koin.androidx.compose.get
class AboutScreen: Screen {
class AboutScreen : Screen {
@Composable
@OptIn(ExperimentalMaterial3Api::class)
override fun Content() {
val uriHandler = LocalUriHandler.current
val prefs: PreferenceManager = get()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val ctx = LocalContext.current
val bitmap = remember {
ctx.getBitmap(R.drawable.ic_launcher, 60).asImageBitmap()
}
var tapCount by remember {
mutableStateOf(0)
}
Scaffold(
topBar = { TitleBar(scrollBehavior) },
@ -100,7 +104,20 @@ class AboutScreen: Screen {
text = "v${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})",
style = MaterialTheme.typography.labelLarge,
color = LocalContentColor.current.copy(alpha = 0.5f),
modifier = Modifier.clickable { }
modifier = Modifier.clickable(
enabled = !prefs.isDeveloper
) {
tapCount++
when (tapCount) {
3 -> ctx.showToast(R.string.msg_seven_left)
5 -> ctx.showToast(R.string.msg_five_left)
8 -> ctx.showToast(R.string.msg_two_left)
10 -> {
ctx.showToast(R.string.msg_unlocked)
prefs.isDeveloper = true
}
}
}
)
Row(
@ -153,7 +170,7 @@ class AboutScreen: Screen {
uriHandler.openUri("https://github.com/${member.username}")
}
)
if(i != Constants.TEAM_MEMBERS.lastIndex) {
if (i != Constants.TEAM_MEMBERS.lastIndex) {
Divider(
thickness = 0.5.dp,
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f),

View File

@ -20,7 +20,6 @@ import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.OpenInNew
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.RestartAlt
import androidx.compose.material.icons.outlined.Home
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.Button
@ -54,7 +53,6 @@ import dev.beefers.vendetta.manager.ui.components.SegmentedButton
import dev.beefers.vendetta.manager.ui.screen.about.AboutScreen
import dev.beefers.vendetta.manager.ui.screen.installer.InstallerScreen
import dev.beefers.vendetta.manager.ui.viewmodel.home.HomeViewModel
import dev.beefers.vendetta.manager.ui.viewmodel.main.MainViewModel
import dev.beefers.vendetta.manager.ui.widgets.home.Commit
import dev.beefers.vendetta.manager.utils.DiscordVersion
import dev.beefers.vendetta.manager.utils.ManagerTab
@ -75,6 +73,10 @@ class HomeScreen : ManagerTab {
val nav = LocalNavigator.currentOrThrow
val prefs: PreferenceManager = get()
val viewModel: HomeViewModel = getScreenModel()
val latestVersion = when {
prefs.discordVersion.isBlank() -> viewModel.discordVersions?.get(prefs.channel)
else -> DiscordVersion.fromVersionCode(prefs.discordVersion)
}
val iconColor = when {
prefs.patchIcon -> Color(0xFF3AB8BA)
prefs.channel == DiscordVersion.Type.ALPHA -> Color(0xFFFBB33C)
@ -106,18 +108,18 @@ class HomeScreen : ManagerTab {
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
) {
AnimatedVisibility(visible = viewModel.discordVersions != null) {
AnimatedVisibility(visible = viewModel.installManager.current != null) {
Text(
text = "Latest: ${viewModel.discordVersions?.get(prefs.channel)}",
text = "Current: ${viewModel.installManager.current?.versionName}",
style = MaterialTheme.typography.labelLarge,
color = LocalContentColor.current.copy(alpha = 0.5f),
textAlign = TextAlign.Center
)
}
AnimatedVisibility(visible = viewModel.installManager.current != null) {
AnimatedVisibility(visible = latestVersion != null) {
Text(
text = "Current: ${viewModel.installManager.current?.versionName}",
text = "Latest: $latestVersion",
style = MaterialTheme.typography.labelLarge,
color = LocalContentColor.current.copy(alpha = 0.5f),
textAlign = TextAlign.Center
@ -135,10 +137,7 @@ class HomeScreen : ManagerTab {
) {
val label = when {
viewModel.installManager.current == null -> R.string.action_install
viewModel.installManager.current?.versionName == viewModel.discordVersions?.get(
prefs.channel
).toString() -> R.string.action_reinstall
viewModel.installManager.current?.versionName == latestVersion.toString() -> R.string.action_reinstall
else -> R.string.action_update
}
Text(stringResource(label))

View File

@ -9,18 +9,21 @@ import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import cafe.adriel.voyager.core.screen.Screen
import cafe.adriel.voyager.koin.getScreenModel
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
import dev.beefers.vendetta.manager.BuildConfig
import dev.beefers.vendetta.manager.ui.screen.home.HomeScreen
import dev.beefers.vendetta.manager.ui.viewmodel.main.MainViewModel
import dev.beefers.vendetta.manager.ui.widgets.updater.UpdateDialog
import dev.beefers.vendetta.manager.utils.MainTab
@ -31,6 +34,7 @@ class MainScreen : Screen {
@OptIn(ExperimentalMaterial3Api::class, ExperimentalPagerApi::class)
@Composable
override fun Content() {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val viewModel: MainViewModel = getScreenModel()
val pagerState = rememberPagerState()
@ -39,8 +43,10 @@ class MainScreen : Screen {
) {
Scaffold(
bottomBar = { NavBar() },
topBar = { TitleBar() },
modifier = Modifier.fillMaxSize()
topBar = { TitleBar(scrollBehavior) },
modifier = Modifier
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection)
) { pv ->
if (viewModel.showUpdateDialog && viewModel.release != null) {
UpdateDialog(
@ -71,18 +77,21 @@ class MainScreen : Screen {
@OptIn(ExperimentalPagerApi::class, ExperimentalMaterial3Api::class)
@Composable
private fun TitleBar() {
private fun TitleBar(
scrollBehavior: TopAppBarScrollBehavior
) {
val pagerState = LocalPagerState.current
val tab = MainTab.values()[pagerState.currentPage].tab
TopAppBar(
title = { Text(tab.options.title) },
actions = { tab.Actions() }
actions = { tab.Actions() },
scrollBehavior = if (tab is HomeScreen) null else scrollBehavior
)
}
@Composable
@OptIn(ExperimentalPagerApi::class)
@OptIn(ExperimentalPagerApi::class, ExperimentalMaterial3Api::class)
private fun NavBar() {
val pagerState = LocalPagerState.current
val scope = rememberCoroutineScope()

View File

@ -11,6 +11,10 @@ import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
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.platform.LocalContext
import androidx.compose.ui.res.stringResource
@ -19,6 +23,7 @@ import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import cafe.adriel.voyager.navigator.tab.TabOptions
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.domain.manager.InstallManager
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.ui.components.settings.SettingsButton
import dev.beefers.vendetta.manager.ui.components.settings.SettingsHeader
@ -27,6 +32,7 @@ import dev.beefers.vendetta.manager.ui.components.settings.SettingsSwitch
import dev.beefers.vendetta.manager.ui.components.settings.SettingsTextField
import dev.beefers.vendetta.manager.ui.screen.about.AboutScreen
import dev.beefers.vendetta.manager.ui.viewmodel.settings.SettingsViewModel
import dev.beefers.vendetta.manager.utils.DiscordVersion
import dev.beefers.vendetta.manager.utils.ManagerTab
import dev.beefers.vendetta.manager.utils.TabOptions
import dev.beefers.vendetta.manager.utils.navigate
@ -44,6 +50,7 @@ class SettingsScreen : ManagerTab {
override fun Content() {
val viewModel: SettingsViewModel = getScreenModel()
val prefs: PreferenceManager = get()
val installManager: InstallManager = get()
val ctx = LocalContext.current
Column(
@ -103,6 +110,51 @@ class SettingsScreen : ManagerTab {
viewModel.clearCache()
}
)
if (prefs.isDeveloper) {
SettingsHeader(stringResource(R.string.settings_developer))
SettingsTextField(
label = stringResource(R.string.settings_package_name),
pref = prefs.packageName,
onPrefChange = {
prefs.packageName = it
installManager.getInstalled()
}
)
var version by remember {
mutableStateOf(prefs.discordVersion)
}
var versionError by remember {
mutableStateOf(false)
}
val supportingText = if (versionError)
stringResource(R.string.msg_invalid_version)
else if (version.isNotBlank())
DiscordVersion.fromVersionCode(version).toString()
else
null
SettingsTextField(
label = stringResource(R.string.settings_version),
pref = version,
error = versionError,
supportingText = supportingText,
onPrefChange = {
version = it
if (DiscordVersion.fromVersionCode(it) == null && it.isNotBlank()) {
versionError = true
} else {
versionError = false
prefs.discordVersion = it
}
}
)
SettingsSwitch(
label = stringResource(R.string.settings_debuggable),
secondaryLabel = stringResource(R.string.settings_debuggable_description),
pref = prefs.debuggable,
onPrefChange = { prefs.debuggable = it }
)
}
}
}

View File

@ -54,7 +54,7 @@ class MainViewModel(
fun downloadAndInstallUpdate() {
coroutineScope.launch {
val update = File(cacheDir, "update.apk")
if(update.exists()) update.delete()
if (update.exists()) update.delete()
isUpdating = true
downloadManager.downloadUpdate(update)
isUpdating = false

View File

@ -15,7 +15,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage

View File

@ -44,7 +44,7 @@ fun UserEntry(
) {
AsyncImage(
modifier = Modifier
.size(if(isLarge) 70.dp else 50.dp)
.size(if (isLarge) 70.dp else 50.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp)),
model = "https://github.com/$username.png",

View File

@ -1,8 +1,6 @@
package dev.beefers.vendetta.manager.ui.widgets.updater
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.SystemUpdate
@ -46,9 +44,9 @@ fun UpdateDialog(
Box {
Text(
text = stringResource(R.string.action_start_update),
color = if(isUpdating) Color.Transparent else LocalContentColor.current
color = if (isUpdating) Color.Transparent else LocalContentColor.current
)
if(isUpdating) {
if (isUpdating) {
CircularProgressIndicator(
strokeWidth = 3.dp,
modifier = Modifier

View File

@ -13,7 +13,6 @@ import com.google.accompanist.pager.ExperimentalPagerApi
import dev.beefers.vendetta.manager.ui.screen.home.HomeScreen
import dev.beefers.vendetta.manager.ui.screen.main.LocalPagerState
import dev.beefers.vendetta.manager.ui.screen.settings.SettingsScreen
import dev.beefers.vendetta.manager.ui.viewmodel.main.MainViewModel
enum class MainTab(val tab: ManagerTab) {
HOME(HomeScreen()),

View File

@ -8,7 +8,7 @@ data class DiscordVersion(
val major: Int,
val minor: Int,
val type: Type
) : Serializable {
) : Serializable, Comparable<DiscordVersion> {
enum class Type(val label: String, @StringRes val labelRes: Int) {
STABLE("Stable", R.string.channel_stable),
@ -16,6 +16,9 @@ data class DiscordVersion(
ALPHA("Alpha", R.string.channel_alpha)
}
override fun compareTo(other: DiscordVersion): Int =
toVersionCode().toInt() - other.toVersionCode().toInt()
override fun toString() = "$major.$minor - ${type.label}"
fun toVersionCode() = "$major${type.ordinal}${if (minor < 10) 0 else ""}${minor}"
@ -25,6 +28,7 @@ data class DiscordVersion(
fun fromVersionCode(string: String): DiscordVersion? = with(string) {
if (length < 4) return@with null
if (toIntOrNull() == null) return@with null
if (toInt() <= 126021) return@with null
val codeReversed = toCharArray().reversed().joinToString("")
val typeInt = codeReversed[2].toString().toInt()
val type = Type.values().getOrNull(typeInt) ?: return@with null

View File

@ -4,6 +4,11 @@
<string name="msg_cleared_cache">Cache cleared successfully</string>
<string name="msg_load_fail">Failed to load commits</string>
<string name="msg_coming_soon">Coming soon</string>
<string name="msg_invalid_version">Invalid Discord version</string>
<string name="msg_seven_left">7 more taps</string>
<string name="msg_five_left">5 more taps</string>
<string name="msg_two_left">2 more taps</string>
<string name="msg_unlocked">You are now a developer</string>
<string name="group_download">Download APKs</string>
<string name="group_patch">Patching</string>
@ -71,6 +76,12 @@
<string name="settings_app_icon_description">Uses the Vendetta icon instead of Discord\'s</string>
<string name="settings_channel">Release channel</string>
<string name="settings_developer">Developer only</string>
<string name="settings_package_name">Package name</string>
<string name="settings_version">Discord version</string>
<string name="settings_debuggable">Debuggable</string>
<string name="settings_debuggable_description">Enable debuggable flag</string>
<string name="update_description">Vendetta Manager version %1$s is now available!</string>
<string name="channel_stable">Stable</string>