mirror of
https://github.com/whyorean/AuroraStore.git
synced 2025-12-23 22:27:55 -05:00
compose: spoof: Initial migration to compose
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Composable to display device details for spoofing in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param userReadableName Name of the device, obtained through `UserReadableName` property
|
||||
* @param manufacturer Name of the device manufacturer, obtained through `Build.MANUFACTURER` property
|
||||
* @param androidVersionSdk Android version on the device, obtained through `Build.VERSION.SDK_INT` property
|
||||
* @param platforms Platforms supported on the device, obtained through `Platforms` property
|
||||
* @param isChecked If the device is selected
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DeviceListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
userReadableName: String,
|
||||
manufacturer: String,
|
||||
androidVersionSdk: String,
|
||||
platforms: String,
|
||||
isChecked: Boolean = false,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { if (!isChecked) onClick() })
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = userReadableName,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = platforms.replace(",\\s*".toRegex(), ", "),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DeviceListItemPreview() {
|
||||
DeviceListItem(
|
||||
userReadableName = "Google Pixel 7a",
|
||||
manufacturer = "Google",
|
||||
androidVersionSdk = "33",
|
||||
platforms = "arm64-v8a",
|
||||
isChecked = true
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Composable to display locale details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param displayName Display name of the locale
|
||||
* @param displayLanguage Display name of the language in the locale
|
||||
* @param isChecked Whether the locale is checked/selected
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun LocaleListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
displayName: String,
|
||||
displayLanguage: String,
|
||||
isChecked: Boolean = false,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { if (!isChecked) onClick() })
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = displayName,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = displayLanguage,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun LocaleListItemPreview() {
|
||||
LocaleListItem(
|
||||
displayName = Locale.JAPANESE.displayName,
|
||||
displayLanguage = Locale.JAPAN.getDisplayLanguage(Locale.JAPAN),
|
||||
isChecked = true
|
||||
)
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import com.aurora.store.compose.ui.downloads.DownloadsScreen
|
||||
import com.aurora.store.compose.ui.favourite.FavouriteScreen
|
||||
import com.aurora.store.compose.ui.onboarding.OnboardingScreen
|
||||
import com.aurora.store.compose.ui.search.SearchScreen
|
||||
import com.aurora.store.compose.ui.spoof.SpoofScreen
|
||||
|
||||
/**
|
||||
* Navigation display for compose screens
|
||||
@@ -37,6 +38,16 @@ import com.aurora.store.compose.ui.search.SearchScreen
|
||||
fun NavDisplay(startDestination: NavKey) {
|
||||
val backstack = rememberNavBackStack(startDestination)
|
||||
|
||||
// TODO: Rework when migrating splash fragment to compose
|
||||
val splashIntent = NavDeepLinkBuilder(LocalContext.current)
|
||||
.setGraph(R.navigation.mobile_navigation)
|
||||
.setDestination(R.id.splashFragment)
|
||||
.setComponentName(MainActivity::class.java)
|
||||
.createTaskStackBuilder()
|
||||
.intents
|
||||
.first()
|
||||
.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) }
|
||||
|
||||
// TODO: Drop this logic once everything is in compose
|
||||
val activity = LocalActivity.current
|
||||
fun onNavigateUp() {
|
||||
@@ -95,16 +106,6 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
}
|
||||
|
||||
entry<Screen.Accounts> {
|
||||
// TODO: Rework when migrating splash fragment to compose
|
||||
val splashIntent = NavDeepLinkBuilder(LocalContext.current)
|
||||
.setGraph(R.navigation.mobile_navigation)
|
||||
.setDestination(R.id.splashFragment)
|
||||
.setComponentName(MainActivity::class.java)
|
||||
.createTaskStackBuilder()
|
||||
.intents
|
||||
.first()
|
||||
.apply { addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) }
|
||||
|
||||
AccountsScreen(
|
||||
onNavigateUp = ::onNavigateUp,
|
||||
onNavigateToSplash = { activity?.startActivity(splashIntent) }
|
||||
@@ -127,6 +128,13 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
entry<Screen.Onboarding> {
|
||||
OnboardingScreen()
|
||||
}
|
||||
|
||||
entry<Screen.Spoof> {
|
||||
SpoofScreen(
|
||||
onNavigateUp = ::onNavigateUp,
|
||||
onNavigateToSplash = { activity?.startActivity(splashIntent) }
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -51,4 +51,7 @@ sealed class Screen : NavKey, Parcelable {
|
||||
|
||||
@Serializable
|
||||
data object Onboarding : Screen()
|
||||
|
||||
@Serializable
|
||||
data object Spoof : Screen()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.spoof
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TextDividerComposable
|
||||
import com.aurora.store.compose.composable.DeviceListItem
|
||||
import com.aurora.store.compose.preview.PreviewTemplate
|
||||
import com.aurora.store.viewmodel.spoof.SpoofViewModel
|
||||
import java.util.Properties
|
||||
import kotlin.random.Random
|
||||
|
||||
@Composable
|
||||
fun DevicePage(
|
||||
onRequestNavigateToSplash: () -> Unit,
|
||||
viewModel: SpoofViewModel = hiltViewModel(),
|
||||
) {
|
||||
val availableDevices by viewModel.availableDevices.collectAsStateWithLifecycle()
|
||||
val currentDevice by viewModel.currentDevice.collectAsStateWithLifecycle()
|
||||
|
||||
PageContent(
|
||||
devices = availableDevices,
|
||||
defaultDevice = viewModel.defaultProperties,
|
||||
isDeviceSelected = { device -> device == currentDevice },
|
||||
onDeviceSelected = { properties ->
|
||||
viewModel.onDeviceSelected(properties)
|
||||
onRequestNavigateToSplash()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PageContent(
|
||||
defaultDevice: Properties = Properties(),
|
||||
devices: List<Properties> = emptyList(),
|
||||
isDeviceSelected: (properties: Properties) -> Boolean = { false },
|
||||
onDeviceSelected: (properties: Properties) -> Unit = {}
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
TextDividerComposable(
|
||||
title = stringResource(R.string.default_spoof)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
DeviceListItem(
|
||||
userReadableName = defaultDevice.getProperty("UserReadableName"),
|
||||
manufacturer = defaultDevice.getProperty("Build.MANUFACTURER"),
|
||||
androidVersionSdk = defaultDevice.getProperty("Build.VERSION.SDK_INT"),
|
||||
platforms = defaultDevice.getProperty("Platforms"),
|
||||
isChecked = isDeviceSelected(defaultDevice),
|
||||
onClick = { onDeviceSelected(defaultDevice) }
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
TextDividerComposable(
|
||||
title = stringResource(R.string.available_spoof)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(items = devices, key = { device -> device.getProperty("Build.PRODUCT") }) { device ->
|
||||
DeviceListItem(
|
||||
userReadableName = device.getProperty("UserReadableName"),
|
||||
manufacturer = device.getProperty("Build.MANUFACTURER"),
|
||||
androidVersionSdk = device.getProperty("Build.VERSION.SDK_INT"),
|
||||
platforms = device.getProperty("Platforms"),
|
||||
isChecked = isDeviceSelected(device),
|
||||
onClick = { onDeviceSelected(device) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DevicePagePreview() {
|
||||
fun getDevice(): Properties {
|
||||
return Properties().apply {
|
||||
setProperty("UserReadableName", "Google Pixel 9a")
|
||||
setProperty("Build.VERSION.SDK_INT", "35")
|
||||
setProperty("Build.MANUFACTURER", "Google")
|
||||
setProperty("Platforms", "arm64-v8a")
|
||||
setProperty("Build.PRODUCT", Random.nextInt().toString())
|
||||
}
|
||||
}
|
||||
|
||||
PreviewTemplate {
|
||||
val defaultDevice = getDevice()
|
||||
PageContent(
|
||||
defaultDevice = defaultDevice,
|
||||
devices = List(10) { getDevice() },
|
||||
isDeviceSelected = { device -> defaultDevice == device }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.spoof
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TextDividerComposable
|
||||
import com.aurora.store.compose.composable.LocaleListItem
|
||||
import com.aurora.store.compose.preview.PreviewTemplate
|
||||
import com.aurora.store.viewmodel.spoof.SpoofViewModel
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun LocalePage(
|
||||
onRequestNavigateToSplash: () -> Unit,
|
||||
viewModel: SpoofViewModel = hiltViewModel()
|
||||
) {
|
||||
val availableLocales by viewModel.availableLocales.collectAsStateWithLifecycle()
|
||||
val currentLocale by viewModel.currentLocale.collectAsStateWithLifecycle()
|
||||
|
||||
PageContent(
|
||||
defaultLocale = viewModel.defaultLocale,
|
||||
locales = availableLocales,
|
||||
isLocaleSelected = { locale -> currentLocale == locale },
|
||||
onLocaleSelected = { locale ->
|
||||
viewModel.onLocaleSelected(locale)
|
||||
onRequestNavigateToSplash()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PageContent(
|
||||
defaultLocale: Locale = Locale.getDefault(),
|
||||
locales: List<Locale> = emptyList(),
|
||||
isLocaleSelected: (locale: Locale) -> Boolean = { false },
|
||||
onLocaleSelected: (locale: Locale) -> Unit = {},
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
TextDividerComposable(
|
||||
title = stringResource(R.string.default_spoof)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
LocaleListItem(
|
||||
displayName = defaultLocale.displayName,
|
||||
displayLanguage = defaultLocale.getDisplayLanguage(defaultLocale),
|
||||
isChecked = isLocaleSelected(defaultLocale),
|
||||
onClick = { onLocaleSelected(defaultLocale) }
|
||||
)
|
||||
}
|
||||
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
TextDividerComposable(
|
||||
title = stringResource(R.string.available_spoof)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
items(items = locales, key = { locale -> locale.hashCode() }) { locale ->
|
||||
LocaleListItem(
|
||||
displayName = locale.displayName,
|
||||
displayLanguage = locale.getDisplayLanguage(locale),
|
||||
isChecked = isLocaleSelected(locale),
|
||||
onClick = { onLocaleSelected(locale) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun LocalePagePreview() {
|
||||
PreviewTemplate {
|
||||
PageContent(
|
||||
locales = Locale.getAvailableLocales().toList().filter { it.displayName.isNotBlank() },
|
||||
isLocaleSelected = { locale -> locale == Locale.getDefault() }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.spoof
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SecondaryTabRow
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.SnackbarResult
|
||||
import androidx.compose.material3.Tab
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.ui.spoof.navigation.SpoofPage
|
||||
import com.aurora.store.data.providers.AccountProvider
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun SpoofScreen(onNavigateUp: () -> Unit, onNavigateToSplash: () -> Unit) {
|
||||
ScreenContent(
|
||||
onNavigateUp = onNavigateUp,
|
||||
onNavigateToSplash = onNavigateToSplash
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
pages: List<SpoofPage> = listOf(SpoofPage.DEVICE, SpoofPage.LOCALE),
|
||||
onNavigateUp: () -> Unit = {},
|
||||
onNavigateToSplash: () -> Unit = {}
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val pagerState = rememberPagerState { pages.size }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
|
||||
fun onRequestNavigateToSplash() {
|
||||
coroutineScope.launch {
|
||||
val result = snackBarHostState.showSnackbar(
|
||||
message = context.getString(R.string.force_restart_snack),
|
||||
actionLabel = context.getString(R.string.action_restart),
|
||||
duration = SnackbarDuration.Indefinite
|
||||
)
|
||||
when (result) {
|
||||
SnackbarResult.ActionPerformed -> {
|
||||
AccountProvider.logout(context)
|
||||
onNavigateToSplash()
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
snackbarHost = {
|
||||
SnackbarHost(hostState = snackBarHostState)
|
||||
},
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = stringResource(R.string.title_spoof_manager),
|
||||
onNavigateUp = onNavigateUp
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
) {
|
||||
SecondaryTabRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selectedTabIndex = pagerState.currentPage
|
||||
) {
|
||||
pages.fastForEachIndexed { index, _ ->
|
||||
Tab(
|
||||
selected = pagerState.currentPage == index,
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
pagerState.animateScrollToPage(index)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(text = stringResource(id = pages[index].localized))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
HorizontalPager(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = pagerState,
|
||||
verticalAlignment = Alignment.Top
|
||||
) { page ->
|
||||
when (pages[page]) {
|
||||
SpoofPage.DEVICE -> DevicePage(
|
||||
onRequestNavigateToSplash = ::onRequestNavigateToSplash
|
||||
)
|
||||
|
||||
SpoofPage.LOCALE -> LocalePage(
|
||||
onRequestNavigateToSplash = ::onRequestNavigateToSplash
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.spoof.navigation
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Pages that are shown in SpoofScreen
|
||||
*/
|
||||
enum class SpoofPage(@StringRes val localized: Int) {
|
||||
DEVICE(R.string.title_device),
|
||||
LOCALE(R.string.title_language)
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.epoxy.views.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.CompoundButton
|
||||
import com.airbnb.epoxy.CallbackProp
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.ViewDeviceBinding
|
||||
import com.aurora.store.view.epoxy.views.BaseModel
|
||||
import com.aurora.store.view.epoxy.views.BaseView
|
||||
import java.util.Properties
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class DeviceView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewDeviceBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp
|
||||
fun properties(properties: Properties) {
|
||||
binding.line1.text = properties.getProperty("UserReadableName")
|
||||
binding.line2.text = resources.getString(
|
||||
R.string.spoof_property,
|
||||
properties.getProperty("Build.MANUFACTURER"),
|
||||
properties.getProperty("Build.VERSION.SDK_INT")
|
||||
)
|
||||
binding.line3.text = properties.getProperty("Platforms")
|
||||
}
|
||||
|
||||
@ModelProp
|
||||
fun markChecked(isChecked: Boolean) {
|
||||
binding.checkbox.isChecked = isChecked
|
||||
binding.checkbox.isEnabled = !isChecked
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun checked(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?) {
|
||||
binding.checkbox.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.epoxy.views.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.CompoundButton
|
||||
import com.airbnb.epoxy.CallbackProp
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.databinding.ViewLocaleBinding
|
||||
import com.aurora.store.view.epoxy.views.BaseModel
|
||||
import com.aurora.store.view.epoxy.views.BaseView
|
||||
import java.util.Locale
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class LocaleView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewLocaleBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp
|
||||
fun locale(locale: Locale) {
|
||||
binding.line1.text = locale.displayName
|
||||
binding.line2.text = locale.getDisplayLanguage(locale)
|
||||
}
|
||||
|
||||
@ModelProp
|
||||
fun markChecked(isChecked: Boolean) {
|
||||
binding.checkbox.isChecked = isChecked
|
||||
binding.checkbox.isEnabled = !isChecked
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun checked(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener?) {
|
||||
binding.checkbox.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||
}
|
||||
}
|
||||
@@ -438,10 +438,10 @@ class MoreDialogFragment : DialogFragment() {
|
||||
icon = R.drawable.ic_favorite_unchecked,
|
||||
screen = Screen.Favourite
|
||||
),
|
||||
ViewOption(
|
||||
ComposeOption(
|
||||
title = R.string.title_spoof_manager,
|
||||
icon = R.drawable.ic_spoof,
|
||||
destinationID = R.id.spoofFragment
|
||||
screen = Screen.Spoof
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -83,9 +83,7 @@ abstract class BaseFlavouredSplashFragment : BaseFragment<FragmentSplashBinding>
|
||||
requireContext().navigate(Screen.Blacklist)
|
||||
}
|
||||
|
||||
R.id.menu_spoof_manager -> {
|
||||
findNavController().navigate(R.id.spoofFragment)
|
||||
}
|
||||
R.id.menu_spoof_manager -> requireContext().navigate(Screen.Spoof)
|
||||
|
||||
R.id.menu_settings -> {
|
||||
findNavController().navigate(R.id.settingsFragment)
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.spoof
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.providers.AccountProvider
|
||||
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
|
||||
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||
import com.aurora.store.view.epoxy.views.preference.DeviceViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.spoof.SpoofViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Properties
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DeviceSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
|
||||
|
||||
private val viewModel: SpoofViewModel by viewModels()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(): DeviceSpoofFragment {
|
||||
return DeviceSpoofFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.availableDevices.collect { updateController(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateController(devices: List<Properties>) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("default_divider")
|
||||
.title(getString(R.string.default_spoof))
|
||||
)
|
||||
|
||||
add(
|
||||
DeviceViewModel_()
|
||||
.id(viewModel.defaultProperties.hashCode())
|
||||
.markChecked(viewModel.isDeviceSelected(viewModel.defaultProperties))
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
viewModel.onDeviceSelected(viewModel.defaultProperties)
|
||||
requestModelBuild()
|
||||
AccountProvider.logout(requireContext())
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
}
|
||||
}
|
||||
.properties(viewModel.defaultProperties)
|
||||
)
|
||||
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("available_divider")
|
||||
.title(getString(R.string.available_spoof))
|
||||
)
|
||||
|
||||
devices.forEach {
|
||||
add(
|
||||
DeviceViewModel_()
|
||||
.id(it.hashCode())
|
||||
.markChecked(viewModel.isDeviceSelected(it))
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
viewModel.onDeviceSelected(it)
|
||||
requestModelBuild()
|
||||
AccountProvider.logout(requireContext())
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
}
|
||||
}
|
||||
.properties(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.spoof
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.providers.AccountProvider
|
||||
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
|
||||
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||
import com.aurora.store.view.epoxy.views.preference.LocaleViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.spoof.SpoofViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Locale
|
||||
|
||||
@AndroidEntryPoint
|
||||
class LocaleSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
|
||||
|
||||
private val viewModel: SpoofViewModel by viewModels()
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun newInstance(): LocaleSpoofFragment {
|
||||
return LocaleSpoofFragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.availableLocales.collect {
|
||||
updateController(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateController(locales: List<Locale>) {
|
||||
binding.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("default_divider")
|
||||
.title(getString(R.string.default_spoof))
|
||||
)
|
||||
|
||||
add(
|
||||
LocaleViewModel_()
|
||||
.id(viewModel.defaultLocale.language)
|
||||
.markChecked(viewModel.isLocaleSelected(viewModel.defaultLocale))
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
viewModel.onLocaleSelected(viewModel.defaultLocale)
|
||||
requestModelBuild()
|
||||
AccountProvider.logout(requireContext())
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
}
|
||||
}
|
||||
.locale(viewModel.defaultLocale)
|
||||
)
|
||||
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id("available_divider")
|
||||
.title(getString(R.string.available_spoof))
|
||||
)
|
||||
|
||||
locales.forEach {
|
||||
add(
|
||||
LocaleViewModel_()
|
||||
.id(it.language)
|
||||
.markChecked(viewModel.spoofProvider.locale == it)
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
viewModel.onLocaleSelected(it)
|
||||
requestModelBuild()
|
||||
AccountProvider.logout(requireContext())
|
||||
findNavController().navigate(R.id.forceRestartDialog)
|
||||
}
|
||||
}
|
||||
.locale(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.spoof
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.providers.NativeDeviceInfoProvider
|
||||
import com.aurora.store.databinding.FragmentSpoofBinding
|
||||
import com.aurora.store.util.PathUtil
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayoutMediator
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SpoofFragment : BaseFragment<FragmentSpoofBinding>() {
|
||||
private val TAG = SpoofFragment::class.java.simpleName
|
||||
|
||||
// Android is weird, even if export device config with proper mime type, it will refuse to open
|
||||
// it again with same mime type
|
||||
private val importMimeType = "application/octet-stream"
|
||||
private val exportMimeType = "text/x-java-properties"
|
||||
|
||||
private val startForDocumentImport =
|
||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) {
|
||||
if (it != null) importDeviceConfig(it) else toast(R.string.toast_import_failed)
|
||||
}
|
||||
private val startForDocumentExport =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) {
|
||||
if (it != null) exportDeviceConfig(it) else toast(R.string.toast_export_failed)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_import -> {
|
||||
startForDocumentImport.launch(arrayOf(importMimeType))
|
||||
}
|
||||
|
||||
R.id.action_export -> {
|
||||
startForDocumentExport
|
||||
.launch("aurora_store_${Build.BRAND}_${Build.DEVICE}.properties")
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// ViewPager
|
||||
binding.pager.adapter = ViewPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
|
||||
|
||||
TabLayoutMediator(
|
||||
binding.tabLayout,
|
||||
binding.pager,
|
||||
true
|
||||
) { tab: TabLayout.Tab, position: Int ->
|
||||
when (position) {
|
||||
0 -> tab.text = getString(R.string.title_device)
|
||||
1 -> tab.text = getString(R.string.title_language)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}.attach()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding.pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun importDeviceConfig(uri: Uri) {
|
||||
try {
|
||||
requireContext().contentResolver?.openInputStream(uri)?.use { input ->
|
||||
PathUtil.getNewEmptySpoofConfig(requireContext()).outputStream().use {
|
||||
input.copyTo(it)
|
||||
}
|
||||
}
|
||||
toast(R.string.toast_import_success)
|
||||
activity?.recreate()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to import device config", exception)
|
||||
toast(R.string.toast_import_failed)
|
||||
}
|
||||
}
|
||||
|
||||
private fun exportDeviceConfig(uri: Uri) {
|
||||
try {
|
||||
NativeDeviceInfoProvider.getNativeDeviceProperties(requireContext(), true)
|
||||
.store(requireContext().contentResolver?.openOutputStream(uri), "DEVICE_CONFIG")
|
||||
toast(R.string.toast_export_success)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to export device config", exception)
|
||||
toast(R.string.toast_export_failed)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ViewPagerAdapter(fragment: FragmentManager, lifecycle: Lifecycle) :
|
||||
FragmentStateAdapter(fragment, lifecycle) {
|
||||
override fun createFragment(position: Int): Fragment {
|
||||
return when (position) {
|
||||
0 -> DeviceSpoofFragment.newInstance()
|
||||
1 -> LocaleSpoofFragment.newInstance()
|
||||
else -> Fragment()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,39 +21,30 @@ class SpoofViewModel @Inject constructor(
|
||||
val defaultLocale: Locale = Locale.getDefault()
|
||||
val defaultProperties = NativeDeviceInfoProvider.getNativeDeviceProperties(context)
|
||||
|
||||
private var currentDevice = spoofProvider.deviceProperties.getProperty("UserReadableName")
|
||||
private var currentLocale = spoofProvider.locale
|
||||
private val _currentLocale = MutableStateFlow(spoofProvider.locale)
|
||||
val currentLocale = _currentLocale.asStateFlow()
|
||||
|
||||
private val _availableLocales: MutableStateFlow<List<Locale>> = MutableStateFlow(
|
||||
spoofProvider.availableSpoofLocales
|
||||
)
|
||||
private val _availableLocales = MutableStateFlow(spoofProvider.availableSpoofLocales)
|
||||
val availableLocales = _availableLocales.asStateFlow()
|
||||
|
||||
private val _availableDevices: MutableStateFlow<List<Properties>> = MutableStateFlow(
|
||||
spoofProvider.availableSpoofDeviceProperties
|
||||
)
|
||||
private val _currentDevice = MutableStateFlow(spoofProvider.deviceProperties)
|
||||
val currentDevice = _currentDevice.asStateFlow()
|
||||
|
||||
private val _availableDevices = MutableStateFlow(spoofProvider.availableSpoofDeviceProperties)
|
||||
val availableDevices = _availableDevices.asStateFlow()
|
||||
|
||||
fun isDeviceSelected(properties: Properties): Boolean {
|
||||
return currentDevice == properties.getProperty("UserReadableName")
|
||||
}
|
||||
|
||||
fun onDeviceSelected(properties: Properties) {
|
||||
currentDevice = properties.getProperty("UserReadableName")
|
||||
_currentDevice.value = properties
|
||||
|
||||
if (currentDevice == defaultProperties.getProperty("UserReadableName")) {
|
||||
if (currentDevice == defaultProperties) {
|
||||
spoofProvider.removeSpoofDeviceProperties()
|
||||
} else {
|
||||
spoofProvider.setSpoofDeviceProperties(properties)
|
||||
}
|
||||
}
|
||||
|
||||
fun isLocaleSelected(locale: Locale): Boolean {
|
||||
return currentLocale == locale
|
||||
}
|
||||
|
||||
fun onLocaleSelected(locale: Locale) {
|
||||
currentLocale = locale
|
||||
_currentLocale.value = locale
|
||||
|
||||
if (currentLocale == defaultLocale) {
|
||||
spoofProvider.removeSpoofLocale()
|
||||
|
||||
@@ -61,11 +61,6 @@
|
||||
android:name="com.aurora.store.view.ui.all.AppsGamesFragment"
|
||||
android:label="@string/title_apps_games"
|
||||
tools:layout="@layout/fragment_generic_with_search" />
|
||||
<fragment
|
||||
android:id="@+id/spoofFragment"
|
||||
android:name="com.aurora.store.view.ui.spoof.SpoofFragment"
|
||||
android:label="@string/title_spoof_manager"
|
||||
tools:layout="@layout/fragment_spoof" />
|
||||
<fragment
|
||||
android:id="@+id/settingsFragment"
|
||||
android:name="com.aurora.store.view.ui.preferences.SettingsFragment"
|
||||
|
||||
@@ -470,6 +470,7 @@
|
||||
<string name="checking_updates">Checking for updates</string>
|
||||
|
||||
<!-- ForceRestartDialog -->
|
||||
<string name="force_restart_snack">Restart to apply changes?</string>
|
||||
<string name="force_restart_title">Restart Aurora Store</string>
|
||||
<string name="force_restart_summary">Aurora Store needs to be restarted to apply the newly changed settings</string>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user