mirror of
https://github.com/IllusionMan1212/NPS-Browser.git
synced 2026-10-24 18:12:38 +00:00
Compare commits
3
Commits
2178a0c9a4
...
9e582d3788
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e582d3788
|
||
|
|
4b84275f3a
|
||
|
|
bcd2591ba7
|
@@ -16,7 +16,7 @@
|
|||||||
android:theme="@style/NPSTheme"
|
android:theme="@style/NPSTheme"
|
||||||
android:usesCleartextTraffic="true">
|
android:usesCleartextTraffic="true">
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".MainActivity"
|
||||||
android:configChanges="screenSize|screenLayout|keyboardHidden|orientation"
|
android:configChanges="screenSize|screenLayout|keyboardHidden|orientation"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/NPSTheme.Splash"
|
android:theme="@style/NPSTheme.Splash"
|
||||||
|
|||||||
+55
-23
@@ -1,4 +1,4 @@
|
|||||||
package com.illusionware.npsbrowser.activities
|
package com.illusionware.npsbrowser
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
@@ -28,6 +28,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Close
|
import androidx.compose.material.icons.filled.Close
|
||||||
|
import androidx.compose.material.icons.filled.Downloading
|
||||||
import androidx.compose.material.icons.filled.GridView
|
import androidx.compose.material.icons.filled.GridView
|
||||||
import androidx.compose.material.icons.filled.Search
|
import androidx.compose.material.icons.filled.Search
|
||||||
import androidx.compose.material.icons.filled.Settings
|
import androidx.compose.material.icons.filled.Settings
|
||||||
@@ -84,8 +85,6 @@ import androidx.navigation.compose.rememberNavController
|
|||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.illusionware.npsbrowser.R
|
|
||||||
import com.illusionware.npsbrowser.Routes
|
|
||||||
import com.illusionware.npsbrowser.data.ItemLayout
|
import com.illusionware.npsbrowser.data.ItemLayout
|
||||||
import com.illusionware.npsbrowser.data.SettingsPreferences
|
import com.illusionware.npsbrowser.data.SettingsPreferences
|
||||||
import com.illusionware.npsbrowser.model.ConsoleType
|
import com.illusionware.npsbrowser.model.ConsoleType
|
||||||
@@ -189,15 +188,17 @@ fun HomePage(
|
|||||||
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
||||||
settingsPrefs: SettingsPreferences,
|
settingsPrefs: SettingsPreferences,
|
||||||
settingsViewModel: SettingsViewModel,
|
settingsViewModel: SettingsViewModel,
|
||||||
|
packageListViewModel: PackageListViewModel = viewModel(
|
||||||
|
factory = PackageListViewModel.Factory
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
var isSearchOpen by rememberSaveable { mutableStateOf(false)}
|
|
||||||
var searchQuery by rememberSaveable { mutableStateOf("")}
|
|
||||||
val searchBar = remember { FocusRequester() }
|
val searchBar = remember { FocusRequester() }
|
||||||
var layoutDialogOpen by remember { mutableStateOf(false) }
|
var layoutDialogOpen by remember { mutableStateOf(false) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||||
|
|
||||||
|
val uiState = packageListViewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
val tsvs = settingsViewModel.availableTsvs()
|
val tsvs = settingsViewModel.availableTsvs()
|
||||||
var selectedBarItem by rememberSaveable { mutableStateOf(ConsoleType.PSVITA) }
|
var selectedBarItem by rememberSaveable { mutableStateOf(ConsoleType.PSVITA) }
|
||||||
|
|
||||||
@@ -215,22 +216,26 @@ fun HomePage(
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
if (!isSearchOpen) {
|
if (!uiState.isSearchOpen) {
|
||||||
Text(text = stringResource(id = R.string.app_name))
|
Text(text = stringResource(id = R.string.app_name))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (isSearchOpen) {
|
if (uiState.isSearchOpen) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = "Close Search",
|
tooltip = "Close Search",
|
||||||
onClick = { isSearchOpen = false }
|
onClick = {
|
||||||
|
packageListViewModel.closeSearch()
|
||||||
|
}
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close Search")
|
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close Search")
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
TextField(
|
TextField(
|
||||||
value = searchQuery,
|
value = uiState.searchQuery,
|
||||||
onValueChange = {str -> searchQuery = str},
|
onValueChange = {
|
||||||
|
packageListViewModel.search(it)
|
||||||
|
},
|
||||||
Modifier
|
Modifier
|
||||||
.weight(1.0f)
|
.weight(1.0f)
|
||||||
.focusRequester(searchBar),
|
.focusRequester(searchBar),
|
||||||
@@ -248,10 +253,12 @@ fun HomePage(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
if (searchQuery.isNotEmpty()) {
|
if (uiState.searchQuery.isNotEmpty()) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = "Clear Search",
|
tooltip = "Clear Search",
|
||||||
onClick = { searchQuery = "" }
|
onClick = {
|
||||||
|
packageListViewModel.clearSearch()
|
||||||
|
}
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear Search")
|
Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear Search")
|
||||||
}
|
}
|
||||||
@@ -261,10 +268,10 @@ fun HomePage(
|
|||||||
searchBar.requestFocus()
|
searchBar.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isSearchOpen) {
|
if (!uiState.isSearchOpen) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = stringResource(id = R.string.search_tooltip),
|
tooltip = stringResource(id = R.string.search_tooltip),
|
||||||
onClick = { isSearchOpen = true }
|
onClick = { packageListViewModel.openSearch() }
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.Search, contentDescription = stringResource(
|
Icon(imageVector = Icons.Filled.Search, contentDescription = stringResource(
|
||||||
id = R.string.search_tooltip
|
id = R.string.search_tooltip
|
||||||
@@ -275,17 +282,29 @@ fun HomePage(
|
|||||||
tooltip = stringResource(id = R.string.layout),
|
tooltip = stringResource(id = R.string.layout),
|
||||||
onClick = { layoutDialogOpen = true }
|
onClick = { layoutDialogOpen = true }
|
||||||
) {
|
) {
|
||||||
Icon(imageVector = if (settingsPrefs.layout == ItemLayout.GRID.ordinal) Icons.Filled.GridView else Icons.Filled.ViewList, contentDescription = stringResource(
|
Icon(
|
||||||
|
imageVector = if (settingsPrefs.layout == ItemLayout.GRID.ordinal) Icons.Filled.GridView else Icons.Filled.ViewList,
|
||||||
|
contentDescription = stringResource(
|
||||||
id = R.string.layout
|
id = R.string.layout
|
||||||
))
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
NPSIconButton(
|
||||||
|
tooltip = "Download queue",
|
||||||
|
onClick = { /*TODO*/ }
|
||||||
|
) {
|
||||||
|
Icon(imageVector = Icons.Filled.Downloading, contentDescription = "Download queue")
|
||||||
}
|
}
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = stringResource(id = R.string.title_activity_settings),
|
tooltip = stringResource(id = R.string.title_activity_settings),
|
||||||
onClick = { navigateToSettings() }
|
onClick = { navigateToSettings() }
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.Settings, contentDescription = stringResource(
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Settings,
|
||||||
|
contentDescription = stringResource(
|
||||||
id = R.string.title_activity_settings
|
id = R.string.title_activity_settings
|
||||||
))
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -306,7 +325,8 @@ fun HomePage(
|
|||||||
PackageList(
|
PackageList(
|
||||||
navigateToDetailsScreen = navigateToDetailsScreen,
|
navigateToDetailsScreen = navigateToDetailsScreen,
|
||||||
type = selectedBarItem,
|
type = selectedBarItem,
|
||||||
settingsPrefs = settingsPrefs
|
settingsPrefs = settingsPrefs,
|
||||||
|
viewModel = packageListViewModel,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,6 +341,18 @@ fun HomePage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NoResults() {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(16.dp),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(text = "No results found", color = MaterialTheme.colorScheme.outline)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NavBar(selectedTab: ConsoleType, onTabSelected: (ConsoleType) -> Unit) {
|
fun NavBar(selectedTab: ConsoleType, onTabSelected: (ConsoleType) -> Unit) {
|
||||||
NavigationBar {
|
NavigationBar {
|
||||||
@@ -411,9 +443,7 @@ fun NoTsvs(padding: PaddingValues = PaddingValues(0.dp), navigateToSettings: ()
|
|||||||
fun PackageList(
|
fun PackageList(
|
||||||
type: ConsoleType = ConsoleType.PSVITA,
|
type: ConsoleType = ConsoleType.PSVITA,
|
||||||
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
||||||
viewModel: PackageListViewModel = viewModel(
|
viewModel: PackageListViewModel,
|
||||||
factory = PackageListViewModel.Factory
|
|
||||||
),
|
|
||||||
settingsPrefs: SettingsPreferences,
|
settingsPrefs: SettingsPreferences,
|
||||||
) {
|
) {
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
@@ -447,7 +477,7 @@ fun PackageList(
|
|||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (packages.isEmpty()) {
|
if (packages.isEmpty() && uiState.searchQuery.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -461,6 +491,8 @@ fun PackageList(
|
|||||||
color = MaterialTheme.colorScheme.outline
|
color = MaterialTheme.colorScheme.outline
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (packages.isEmpty() && uiState.searchQuery.isNotEmpty()) {
|
||||||
|
NoResults()
|
||||||
} else {
|
} else {
|
||||||
if (settingsPrefs.layout == ItemLayout.LIST.ordinal) {
|
if (settingsPrefs.layout == ItemLayout.LIST.ordinal) {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
@@ -8,6 +8,12 @@ import androidx.datastore.preferences.preferencesDataStore
|
|||||||
import com.illusionware.npsbrowser.data.OnboardingPreferencesRepository
|
import com.illusionware.npsbrowser.data.OnboardingPreferencesRepository
|
||||||
import com.illusionware.npsbrowser.data.SettingsPreferencesRepository
|
import com.illusionware.npsbrowser.data.SettingsPreferencesRepository
|
||||||
|
|
||||||
|
@OptIn(ExperimentalUnsignedTypes::class)
|
||||||
|
val HMAC_KEY = ubyteArrayOf(
|
||||||
|
0xE5U, 0xE2U, 0x78U, 0xAAU, 0x1EU, 0xE3U, 0x40U, 0x82U, 0xA0U, 0x88U, 0x27U, 0x9CU, 0x83U, 0xF9U, 0xBBU, 0xC8U,
|
||||||
|
0x06U, 0x82U, 0x1CU, 0x52U, 0xF2U, 0xABU, 0x5DU, 0x2BU, 0x4AU, 0xBDU, 0x99U, 0x54U, 0x50U, 0x35U, 0x51U, 0x14U
|
||||||
|
)
|
||||||
|
|
||||||
private const val PREFERENCES_NAME = "preferences"
|
private const val PREFERENCES_NAME = "preferences"
|
||||||
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(
|
||||||
name = PREFERENCES_NAME
|
name = PREFERENCES_NAME
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ data class SettingsPreferences(
|
|||||||
val ps3Dlc: String,
|
val ps3Dlc: String,
|
||||||
val pspDlc: String,
|
val pspDlc: String,
|
||||||
val psvThemes: String,
|
val psvThemes: String,
|
||||||
val hmacKey: String,
|
|
||||||
val downloadDir: String,
|
val downloadDir: String,
|
||||||
val unpackDir: String,
|
val unpackDir: String,
|
||||||
val unpackInDownload: Boolean,
|
val unpackInDownload: Boolean,
|
||||||
@@ -64,7 +63,6 @@ class SettingsPreferencesRepository(
|
|||||||
private val PS3_DLC = stringPreferencesKey("ps3_dlc")
|
private val PS3_DLC = stringPreferencesKey("ps3_dlc")
|
||||||
private val PSP_DLC = stringPreferencesKey("psp_dlc")
|
private val PSP_DLC = stringPreferencesKey("psp_dlc")
|
||||||
private val PSV_THEMES = stringPreferencesKey("psv_themes")
|
private val PSV_THEMES = stringPreferencesKey("psv_themes")
|
||||||
private val HMAC_KEY = stringPreferencesKey("hmac_key")
|
|
||||||
private val DOWNLOAD_DIR = stringPreferencesKey("download_dir")
|
private val DOWNLOAD_DIR = stringPreferencesKey("download_dir")
|
||||||
private val UNPACK_DIR = stringPreferencesKey("unpack_dir")
|
private val UNPACK_DIR = stringPreferencesKey("unpack_dir")
|
||||||
private val UNPACK_IN_DOWNLOAD = booleanPreferencesKey("unpack_in_download")
|
private val UNPACK_IN_DOWNLOAD = booleanPreferencesKey("unpack_in_download")
|
||||||
@@ -108,12 +106,6 @@ class SettingsPreferencesRepository(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun setHMACKey(key: String) {
|
|
||||||
dataStore.edit { prefs ->
|
|
||||||
prefs[HMAC_KEY] = key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun setDownloadDir(dir: String) {
|
suspend fun setDownloadDir(dir: String) {
|
||||||
dataStore.edit { prefs ->
|
dataStore.edit { prefs ->
|
||||||
prefs[DOWNLOAD_DIR] = dir
|
prefs[DOWNLOAD_DIR] = dir
|
||||||
@@ -207,7 +199,6 @@ class SettingsPreferencesRepository(
|
|||||||
prefs[PS3_DLC] ?: "",
|
prefs[PS3_DLC] ?: "",
|
||||||
prefs[PSP_DLC] ?: "",
|
prefs[PSP_DLC] ?: "",
|
||||||
prefs[PSV_THEMES] ?: "",
|
prefs[PSV_THEMES] ?: "",
|
||||||
prefs[HMAC_KEY] ?: "",
|
|
||||||
prefs[DOWNLOAD_DIR] ?: defaultDownloadDir,
|
prefs[DOWNLOAD_DIR] ?: defaultDownloadDir,
|
||||||
prefs[UNPACK_DIR] ?: defaultDownloadDir,
|
prefs[UNPACK_DIR] ?: defaultDownloadDir,
|
||||||
prefs[UNPACK_IN_DOWNLOAD] ?: false,
|
prefs[UNPACK_IN_DOWNLOAD] ?: false,
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material.icons.filled.ContentPaste
|
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -74,20 +73,16 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.layout.Layout
|
import androidx.compose.ui.layout.Layout
|
||||||
import androidx.compose.ui.layout.layoutId
|
import androidx.compose.ui.layout.layoutId
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.Font
|
import androidx.compose.ui.text.font.Font
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import com.illusionware.npsbrowser.data.SettingsPreferences
|
import com.illusionware.npsbrowser.data.SettingsPreferences
|
||||||
import com.illusionware.npsbrowser.model.ConsoleType
|
import com.illusionware.npsbrowser.model.ConsoleType
|
||||||
import com.illusionware.npsbrowser.model.PackageItemType
|
import com.illusionware.npsbrowser.model.PackageItemType
|
||||||
import com.illusionware.npsbrowser.ui.components.NPSIconButton
|
|
||||||
import com.illusionware.npsbrowser.viewmodels.OnboardingViewModel
|
import com.illusionware.npsbrowser.viewmodels.OnboardingViewModel
|
||||||
import com.illusionware.npsbrowser.viewmodels.SettingsViewModel
|
import com.illusionware.npsbrowser.viewmodels.SettingsViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -626,10 +621,6 @@ fun OnBoardingSetupSettings(
|
|||||||
onboardingViewModel: OnboardingViewModel,
|
onboardingViewModel: OnboardingViewModel,
|
||||||
settingsPrefs: SettingsPreferences,
|
settingsPrefs: SettingsPreferences,
|
||||||
) {
|
) {
|
||||||
val clipboard = LocalClipboardManager.current
|
|
||||||
|
|
||||||
var hmacKey by rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
|
|
||||||
|
|
||||||
val downloadDirPicker = rememberLauncherForActivityResult(
|
val downloadDirPicker = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.OpenDocumentTree(),
|
contract = ActivityResultContracts.OpenDocumentTree(),
|
||||||
) { dirUri ->
|
) { dirUri ->
|
||||||
@@ -646,12 +637,6 @@ fun OnBoardingSetupSettings(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHmacKey(key: String) {
|
|
||||||
if (key.isNotEmpty()) {
|
|
||||||
settingsViewModel.setHMACKey(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scaffold { padding ->
|
Scaffold { padding ->
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
@@ -685,37 +670,6 @@ fun OnBoardingSetupSettings(
|
|||||||
.padding(vertical = 16.dp),
|
.padding(vertical = 16.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
) {
|
) {
|
||||||
ColumnGroup(title = "HMAC Key", modifier = Modifier.padding(horizontal = 16.dp)) {
|
|
||||||
Text(
|
|
||||||
text = "The HMAC Key is used to decrypt the PS Vita game updates' urls. We cannot provide this key since it's the property of Sony",
|
|
||||||
style = Typography.bodyMedium.copy(color = MaterialTheme.colorScheme.outline),
|
|
||||||
)
|
|
||||||
OutlinedTextField(
|
|
||||||
value = hmacKey,
|
|
||||||
onValueChange = {
|
|
||||||
hmacKey = it
|
|
||||||
setHmacKey(it.text)
|
|
||||||
},
|
|
||||||
label = { Text(text = "HMAC Key") },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
singleLine = true,
|
|
||||||
trailingIcon = {
|
|
||||||
NPSIconButton(
|
|
||||||
tooltip = "Paste",
|
|
||||||
onClick = {
|
|
||||||
val text = clipboard.getText()?.text ?: ""
|
|
||||||
hmacKey = TextFieldValue(text, selection = TextRange(text.length))
|
|
||||||
setHmacKey(text)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.ContentPaste,
|
|
||||||
contentDescription = "Paste",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
ColumnGroup(title = "Downloads", titleModifier = Modifier.padding(horizontal = 16.dp)) {
|
ColumnGroup(title = "Downloads", titleModifier = Modifier.padding(horizontal = 16.dp)) {
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier.padding(horizontal = 16.dp),
|
modifier = Modifier.padding(horizontal = 16.dp),
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
|
|||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import com.illusionware.npsbrowser.R
|
import com.illusionware.npsbrowser.R
|
||||||
import com.illusionware.npsbrowser.activities.PlaceholderColor
|
import com.illusionware.npsbrowser.PlaceholderColor
|
||||||
import com.illusionware.npsbrowser.data.ItemLayout
|
import com.illusionware.npsbrowser.data.ItemLayout
|
||||||
import com.illusionware.npsbrowser.data.SettingsPreferences
|
import com.illusionware.npsbrowser.data.SettingsPreferences
|
||||||
import com.illusionware.npsbrowser.data.Theme
|
import com.illusionware.npsbrowser.data.Theme
|
||||||
@@ -443,7 +443,6 @@ fun BottomSheetPreview() {
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
"",
|
"",
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.ContentPaste
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.outlined.ArrowDropDown
|
import androidx.compose.material.icons.outlined.ArrowDropDown
|
||||||
|
import androidx.compose.material.icons.outlined.ExpandMore
|
||||||
import androidx.compose.material.icons.outlined.FileDownload
|
import androidx.compose.material.icons.outlined.FileDownload
|
||||||
import androidx.compose.material.icons.outlined.Key
|
|
||||||
import androidx.compose.material.icons.outlined.Palette
|
import androidx.compose.material.icons.outlined.Palette
|
||||||
import androidx.compose.material.icons.outlined.Unarchive
|
import androidx.compose.material.icons.outlined.Unarchive
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -46,9 +46,9 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.painter.Painter
|
import androidx.compose.ui.graphics.painter.Painter
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -115,12 +115,10 @@ fun SettingsScreen(
|
|||||||
) {
|
) {
|
||||||
Appearance(viewModel, prefs)
|
Appearance(viewModel, prefs)
|
||||||
TsvFiles(viewModel, prefs)
|
TsvFiles(viewModel, prefs)
|
||||||
Updates(viewModel, prefs)
|
|
||||||
Downloads(viewModel, prefs)
|
Downloads(viewModel, prefs)
|
||||||
Pkg2Zip(viewModel, prefs)
|
Pkg2Zip(viewModel, prefs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +186,7 @@ fun TsvFiles(viewModel: SettingsViewModel, prefs: SettingsPreferences) {
|
|||||||
DialogSetting(
|
DialogSetting(
|
||||||
title = "Expand",
|
title = "Expand",
|
||||||
value = "PS3 DLC, PSP Games, PSP DLC, PSX Games, PSM Games",
|
value = "PS3 DLC, PSP Games, PSP DLC, PSX Games, PSM Games",
|
||||||
icon = Icons.Outlined.ArrowDropDown,
|
icon = Icons.Outlined.ExpandMore,
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
onClick = { expanded = true },
|
onClick = { expanded = true },
|
||||||
)
|
)
|
||||||
@@ -271,64 +269,8 @@ fun TSVPicker(
|
|||||||
onClick = {
|
onClick = {
|
||||||
filePicker.launch(arrayOf("text/tab-separated-values"))
|
filePicker.launch(arrayOf("text/tab-separated-values"))
|
||||||
}
|
}
|
||||||
)}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Updates(viewModel: SettingsViewModel, prefs: SettingsPreferences) {
|
|
||||||
val clipboard = LocalClipboardManager.current
|
|
||||||
|
|
||||||
var hmacOpenDialog by remember { mutableStateOf(false) }
|
|
||||||
var dialogHmacKey by remember { mutableStateOf("") }
|
|
||||||
|
|
||||||
SideEffect {
|
|
||||||
dialogHmacKey = prefs.hmacKey
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingGroup(title = "Updates") {
|
|
||||||
DialogSetting(
|
|
||||||
title = "HMAC Key",
|
|
||||||
value = prefs.hmacKey.ifEmpty { "None" },
|
|
||||||
icon = Icons.Outlined.Key,
|
|
||||||
onClick = { hmacOpenDialog = true }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (hmacOpenDialog) {
|
|
||||||
NPSAlertDialog(
|
|
||||||
onDismiss = { hmacOpenDialog = false },
|
|
||||||
title = "HMAC Key",
|
|
||||||
buttons = {
|
|
||||||
TextButton(onClick = { hmacOpenDialog = false }) {
|
|
||||||
Text(text = "Cancel")
|
|
||||||
}
|
|
||||||
TextButton(onClick = {
|
|
||||||
viewModel.setHMACKey(dialogHmacKey)
|
|
||||||
hmacOpenDialog = false
|
|
||||||
}) {
|
|
||||||
Text(text = "OK")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = dialogHmacKey,
|
|
||||||
onValueChange = { dialogHmacKey = it },
|
|
||||||
label = { Text(text = "HMAC Key") },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
singleLine = true,
|
|
||||||
trailingIcon = {
|
|
||||||
NPSIconButton(
|
|
||||||
tooltip = "Paste",
|
|
||||||
onClick = { dialogHmacKey = clipboard.getText()?.text ?: "" }
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Filled.ContentPaste,
|
|
||||||
contentDescription = "Paste",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Downloads(viewModel: SettingsViewModel, prefs: SettingsPreferences) {
|
fun Downloads(viewModel: SettingsViewModel, prefs: SettingsPreferences) {
|
||||||
@@ -575,7 +517,19 @@ fun ToggleSetting(title: String, icon: ImageVector? = null, checked: Boolean, on
|
|||||||
Box(modifier = Modifier.width(56.dp))
|
Box(modifier = Modifier.width(56.dp))
|
||||||
}
|
}
|
||||||
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
Text(text = title, color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.weight(1.0f))
|
||||||
Switch(checked = checked, onCheckedChange = null)
|
Switch(
|
||||||
|
checked = checked,
|
||||||
|
onCheckedChange = null,
|
||||||
|
thumbContent = if (checked) {
|
||||||
|
{
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Check,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.toLongOrDefault
|
import okhttp3.internal.toLongOrDefault
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -27,10 +28,13 @@ data class PackageListUiState(
|
|||||||
val packages: ArrayList<PackageItem> = ArrayList(),
|
val packages: ArrayList<PackageItem> = ArrayList(),
|
||||||
val error: String = "",
|
val error: String = "",
|
||||||
val isLoading: Boolean = true,
|
val isLoading: Boolean = true,
|
||||||
|
val isSearchOpen: Boolean = false,
|
||||||
|
val searchQuery: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesRepository): ViewModel() {
|
class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesRepository): ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(PackageListUiState())
|
private val _uiState = MutableStateFlow(PackageListUiState())
|
||||||
|
private val immutablePackages = ArrayList<PackageItem>()
|
||||||
val uiState: StateFlow<PackageListUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<PackageListUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
private val tsvReader = csvReader {
|
private val tsvReader = csvReader {
|
||||||
@@ -52,6 +56,7 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
|
|||||||
list.sortBy { it.name }
|
list.sortBy { it.name }
|
||||||
|
|
||||||
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
||||||
|
immutablePackages.addAll(list)
|
||||||
|
|
||||||
settingsRepo.tsvFiles().collect { tsv ->
|
settingsRepo.tsvFiles().collect { tsv ->
|
||||||
_uiState.emit(PackageListUiState())
|
_uiState.emit(PackageListUiState())
|
||||||
@@ -70,10 +75,45 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
|
|||||||
list.sortBy { it.name }
|
list.sortBy { it.name }
|
||||||
|
|
||||||
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
||||||
|
|
||||||
|
immutablePackages.addAll(list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun search(query: String) {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
packages = immutablePackages.filter { item ->
|
||||||
|
item.name.contains(query, true)
|
||||||
|
} as ArrayList<PackageItem>,
|
||||||
|
searchQuery = query
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun openSearch() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(isSearchOpen = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun closeSearch() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(
|
||||||
|
packages = immutablePackages,
|
||||||
|
searchQuery = "",
|
||||||
|
isSearchOpen = false
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clearSearch() {
|
||||||
|
_uiState.update {
|
||||||
|
it.copy(packages = immutablePackages, searchQuery = "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun parsePackageFromTSV(
|
private fun parsePackageFromTSV(
|
||||||
it: Triple<String, ConsoleType, PackageItemType>, app: NPSApp
|
it: Triple<String, ConsoleType, PackageItemType>, app: NPSApp
|
||||||
): ArrayList<PackageItem> {
|
): ArrayList<PackageItem> {
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesReposi
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
"",
|
"",
|
||||||
@@ -85,12 +84,6 @@ class SettingsViewModel(private val settingsPrefsRepo: SettingsPreferencesReposi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setHMACKey(hmacKey: String) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
settingsPrefsRepo.setHMACKey(hmacKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setDownloadDir(downloadDir: String) {
|
fun setDownloadDir(downloadDir: String) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
settingsPrefsRepo.setDownloadDir(downloadDir)
|
settingsPrefsRepo.setDownloadDir(downloadDir)
|
||||||
|
|||||||
Reference in New Issue
Block a user