mirror of
https://codeberg.org/mi6e4ka/openstore.git
synced 2026-07-10 00:12:06 +00:00
feat: initial settings implementation
This commit is contained in:
parent
01fabf52d2
commit
3b039d8dfd
7 changed files with 330 additions and 1 deletions
|
|
@ -27,6 +27,7 @@ import dev.mi6e4ka.openstore.ui.screen.details.DetailsScreen
|
|||
import dev.mi6e4ka.openstore.ui.screen.results.ResultsScreen
|
||||
import dev.mi6e4ka.openstore.ui.screen.reviews.ReviewsScreen
|
||||
import dev.mi6e4ka.openstore.ui.screen.search.SearchScreen
|
||||
import dev.mi6e4ka.openstore.ui.screen.settings.SettingsScreen
|
||||
import dev.mi6e4ka.openstore.ui.theme.AppTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
|
@ -106,6 +107,9 @@ class MainActivity : ComponentActivity() {
|
|||
val packageName = it.arguments?.getString("packageName") ?: ""
|
||||
ReviewsScreen(packageName = packageName, navController = navController)
|
||||
}
|
||||
composable("settings") {
|
||||
SettingsScreen(navController = navController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ 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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
|
|
@ -22,11 +23,15 @@ import androidx.compose.foundation.text.KeyboardActions
|
|||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
|
|
@ -54,6 +59,7 @@ import androidx.navigation.NavController
|
|||
import androidx.navigation.compose.rememberNavController
|
||||
import dev.mi6e4ka.openstore.BuildConfig
|
||||
import dev.mi6e4ka.openstore.R
|
||||
import dev.mi6e4ka.openstore.ui.screen.results.Platform
|
||||
import java.net.URLEncoder
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
|
|
@ -79,7 +85,14 @@ fun SearchScreen(navController: NavController = rememberNavController()) {
|
|||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = { TopAppBar(title = {Text(stringResource(R.string.app_name))}) },
|
||||
topBar = { TopAppBar(
|
||||
title = {Text(stringResource(R.string.app_name))},
|
||||
actions = {
|
||||
IconButton({navController.navigate("settings")}) {
|
||||
Icon(painterResource(R.drawable.ic_settings_24px), "settings")
|
||||
}
|
||||
}
|
||||
) },
|
||||
) { innerPadding ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,281 @@
|
|||
package dev.mi6e4ka.openstore.ui.screen.settings
|
||||
|
||||
import android.content.Context
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TopAppBar
|
||||
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.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavController
|
||||
import dev.mi6e4ka.openstore.R
|
||||
import androidx.core.content.edit
|
||||
|
||||
data class SettingsSection(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val description: String?,
|
||||
val icon: Int,
|
||||
val type: ParamType,
|
||||
val defaultVal: String,
|
||||
val select: List<SettingsSectionSelect>? = null
|
||||
)
|
||||
|
||||
data class SettingsSectionSelect(
|
||||
val title: String,
|
||||
val value: String
|
||||
)
|
||||
|
||||
enum class ParamType {
|
||||
SWITCH,
|
||||
SELECT,
|
||||
MULTI_SELECT
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
navController: NavController
|
||||
) {
|
||||
val sections = listOf(
|
||||
SettingsSection(
|
||||
id = "installer_type",
|
||||
title = "Установщик",
|
||||
description = "Если есть проблемы при установке приложений",
|
||||
icon = R.drawable.apk_install_24px,
|
||||
type = ParamType.SELECT,
|
||||
defaultVal = "session",
|
||||
select = listOf(
|
||||
SettingsSectionSelect(
|
||||
title = "Session",
|
||||
value = "session"
|
||||
),
|
||||
SettingsSectionSelect(
|
||||
title = "Intent",
|
||||
value = "intent"
|
||||
)
|
||||
)
|
||||
),
|
||||
SettingsSection(
|
||||
id = "updates_sources",
|
||||
title = "Проверка обновлений",
|
||||
description = "Для приложений из каких источников проверять обновления",
|
||||
icon = R.drawable.ic_update_24px,
|
||||
type = ParamType.MULTI_SELECT,
|
||||
defaultVal = "",
|
||||
select = listOf(
|
||||
SettingsSectionSelect(
|
||||
title = "OpenStore",
|
||||
value = "dev.mi6e4ka.dev"
|
||||
),
|
||||
SettingsSectionSelect(
|
||||
title = "RuStore",
|
||||
value = "ru.vk.store"
|
||||
),
|
||||
SettingsSectionSelect(
|
||||
title = "Google Play",
|
||||
value = "com.android.vending"
|
||||
)
|
||||
)
|
||||
),
|
||||
SettingsSection(
|
||||
id = "updates_check_all",
|
||||
title = "Проверять обновления для всех не системных приложений",
|
||||
description = "RuStore получает полный список установленных приложений, не безопасно",
|
||||
icon = R.drawable.ic_arrow_circle_up_24px,
|
||||
type = ParamType.SWITCH,
|
||||
defaultVal = ""
|
||||
)
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.settings_title)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
{navController.popBackStack()}
|
||||
) {
|
||||
Icon(painterResource(R.drawable.ic_arrow_back_24px), "back")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) {
|
||||
innerPadding ->
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize().padding(innerPadding)
|
||||
) {
|
||||
items(sections) { item ->
|
||||
SettingsCard(
|
||||
item = item
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SettingsCard(
|
||||
item: SettingsSection
|
||||
) {
|
||||
|
||||
val context = LocalContext.current
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
var showDialog by remember { mutableStateOf(false) }
|
||||
|
||||
var booleanSelect by remember { mutableStateOf(
|
||||
if (item.type == ParamType.SWITCH) prefs.getBoolean(item.id, false) else false
|
||||
)}
|
||||
|
||||
ElevatedCard (
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.clickable{
|
||||
if (item.type != ParamType.SWITCH) {
|
||||
showDialog = true
|
||||
} else {
|
||||
prefs.edit { putBoolean(item.id, !booleanSelect) }
|
||||
booleanSelect = !booleanSelect
|
||||
}
|
||||
}
|
||||
.padding(15.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(15.dp)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(item.icon),
|
||||
"icon",
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(item.title, fontWeight = FontWeight.Medium)
|
||||
Text(item.description?:"", color = Color.Gray, fontWeight = FontWeight.Normal)
|
||||
}
|
||||
if (item.type == ParamType.SWITCH) {
|
||||
|
||||
Switch(
|
||||
booleanSelect,
|
||||
{
|
||||
v -> prefs.edit { putBoolean(item.id, v) }; booleanSelect = v
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showDialog) {
|
||||
val selected = remember { mutableStateListOf<SettingsSectionSelect>() }
|
||||
if (item.type == ParamType.MULTI_SELECT) {
|
||||
selected.apply { addAll(prefs.getString(item.id, item.defaultVal)!!.split(";").mapNotNull {
|
||||
v ->
|
||||
item.select?.firstOrNull { it.value == v }
|
||||
})
|
||||
}
|
||||
}
|
||||
var selectedVal by remember { mutableStateOf(prefs.getString(item.id, item.defaultVal)) }
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDialog = false },
|
||||
|
||||
title = {
|
||||
Text(item.title)
|
||||
},
|
||||
|
||||
text = {
|
||||
LazyColumn {
|
||||
items(item.select?:listOf()) { entry ->
|
||||
Row(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
if (item.type == ParamType.MULTI_SELECT) {
|
||||
if (entry in selected) {
|
||||
selected.remove(entry)
|
||||
} else {
|
||||
selected.add(entry)
|
||||
}
|
||||
} else {
|
||||
selectedVal = entry.value
|
||||
}
|
||||
}
|
||||
.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (item.type == ParamType.SELECT) {
|
||||
RadioButton(
|
||||
selected = selectedVal == entry.value,
|
||||
onClick = { selectedVal = entry.value }
|
||||
)
|
||||
} else {
|
||||
Checkbox(
|
||||
checked = entry in selected,
|
||||
onCheckedChange = {
|
||||
if (it) selected.add(entry)
|
||||
else selected.remove(entry)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
entry.title,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
if (item.type == ParamType.MULTI_SELECT) {
|
||||
prefs.edit { putString(item.id, selected.joinToString(";") { it.value }) }
|
||||
|
||||
} else {
|
||||
prefs.edit { putString(item.id, selectedVal) }
|
||||
}
|
||||
showDialog = false
|
||||
}) {
|
||||
Text("Сохранить")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package dev.mi6e4ka.openstore.ui.screen.settings
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
class DetailsViewModel() : ViewModel() {
|
||||
|
||||
}
|
||||
|
||||
class SettingsViewModelFactory() : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
if (modelClass.isAssignableFrom(DetailsViewModel::class.java)) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return DetailsViewModel() as T
|
||||
}
|
||||
throw IllegalArgumentException("Unknown ViewModel class")
|
||||
}
|
||||
}
|
||||
10
app/src/main/res/drawable/ic_settings_24px.xml
Normal file
10
app/src/main/res/drawable/ic_settings_24px.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M370,880L354,752Q341,747 329.5,740Q318,733 307,725L188,775L78,585L181,507Q180,500 180,493.5Q180,487 180,480Q180,473 180,466.5Q180,460 181,453L78,375L188,185L307,235Q318,227 330,220Q342,213 354,208L370,80L590,80L606,208Q619,213 630.5,220Q642,227 653,235L772,185L882,375L779,453Q780,460 780,466.5Q780,473 780,480Q780,487 780,493.5Q780,500 778,507L881,585L771,775L653,725Q642,733 630,740Q618,747 606,752L590,880L370,880ZM440,800L519,800L533,694Q564,686 590.5,670.5Q617,655 639,633L738,674L777,606L691,541Q696,527 698,511.5Q700,496 700,480Q700,464 698,448.5Q696,433 691,419L777,354L738,286L639,328Q617,305 590.5,289.5Q564,274 533,266L520,160L441,160L427,266Q396,274 369.5,289.5Q343,305 321,327L222,286L183,354L269,418Q264,433 262,448Q260,463 260,480Q260,496 262,511Q264,526 269,541L183,606L222,674L321,632Q343,655 369.5,670.5Q396,686 427,694L440,800ZM482,620Q540,620 581,579Q622,538 622,480Q622,422 581,381Q540,340 482,340Q423,340 382.5,381Q342,422 342,480Q342,538 382.5,579Q423,620 482,620ZM480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480L480,480Z"/>
|
||||
</vector>
|
||||
|
|
@ -33,4 +33,5 @@
|
|||
<string name="negative_first">Negative</string>
|
||||
<string name="download_has_started">Download has started</string>
|
||||
<string name="not_found_error">Nothing found</string>
|
||||
<string name="settings_title">Settings</string>
|
||||
</resources>
|
||||
|
|
@ -34,4 +34,5 @@
|
|||
<string name="negative_first">Негативные</string>
|
||||
<string name="download_has_started">Загрузка началась</string>
|
||||
<string name="not_found_error">Ничего не найдено</string>
|
||||
<string name="settings_title">Настройки</string>
|
||||
</resources>
|
||||
Loading…
Add table
Add a link
Reference in a new issue