SpoofProvider: Refactor and adjust a bit

* Show default device on top under different category
* Remove spoof configuration when default is selected

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2024-11-19 13:37:26 +07:00
parent a26a4953c8
commit 84cd577a3c
12 changed files with 229 additions and 186 deletions

View File

@@ -36,22 +36,22 @@ class SpoofProviderTest {
@Test @Test
fun testSpoofingDeviceLocale() { fun testSpoofingDeviceLocale() {
assertThat(spoofProvider.isLocaleSpoofEnabled()).isFalse() assertThat(spoofProvider.isLocaleSpoofEnabled).isFalse()
spoofProvider.setSpoofLocale(Locale.JAPAN) spoofProvider.setSpoofLocale(Locale.JAPAN)
assertThat(spoofProvider.isLocaleSpoofEnabled()).isTrue() assertThat(spoofProvider.isLocaleSpoofEnabled).isTrue()
assertThat(spoofProvider.getSpoofLocale() == Locale.JAPAN).isTrue() assertThat(spoofProvider.locale == Locale.JAPAN).isTrue()
} }
@Test @Test
fun testSpoofingDeviceProperties() { fun testSpoofingDeviceProperties() {
assertThat(spoofProvider.isDeviceSpoofEnabled()).isFalse() assertThat(spoofProvider.isDeviceSpoofEnabled).isFalse()
val properties = Properties().apply { val properties = Properties().apply {
setProperty("UserReadableName", "Test") setProperty("UserReadableName", "Test")
} }
spoofProvider.setSpoofDeviceProperties(properties) spoofProvider.setSpoofDeviceProperties(properties)
assertThat(spoofProvider.isDeviceSpoofEnabled()).isTrue() assertThat(spoofProvider.isDeviceSpoofEnabled).isTrue()
assertThat(spoofProvider.getSpoofDeviceProperties() == properties).isTrue() assertThat(spoofProvider.deviceProperties == properties).isTrue()
} }
} }

View File

@@ -32,13 +32,10 @@ import com.aurora.store.data.model.Auth
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.google.gson.Gson import com.google.gson.Gson
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.util.Locale
import java.util.Properties
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@@ -72,24 +69,6 @@ class AuthProvider @Inject constructor(
val isAnonymous: Boolean val isAnonymous: Boolean
get() = AccountProvider.getAccountType(context) == AccountType.ANONYMOUS get() = AccountProvider.getAccountType(context) == AccountType.ANONYMOUS
private val properties: Properties
get() {
val currentProperties = if (spoofProvider.isDeviceSpoofEnabled()) {
spoofProvider.getSpoofDeviceProperties()
} else {
NativeDeviceInfoProvider(context).getNativeDeviceProperties()
}
setVendingVersion(currentProperties)
return currentProperties
}
private val locale: Locale
get() = if (spoofProvider.isLocaleSpoofEnabled()) {
spoofProvider.getSpoofLocale()
} else {
Locale.getDefault()
}
/** /**
* Checks whether saved AuthData is valid or not * Checks whether saved AuthData is valid or not
*/ */
@@ -124,8 +103,8 @@ class AuthProvider @Inject constructor(
email = email, email = email,
token = token, token = token,
tokenType = tokenType, tokenType = tokenType,
properties = properties, properties = spoofProvider.deviceProperties,
locale = locale, locale = spoofProvider.locale,
) )
) )
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -153,8 +132,8 @@ class AuthProvider @Inject constructor(
token = auth.auth, token = auth.auth,
tokenType = AuthHelper.Token.AUTH, tokenType = AuthHelper.Token.AUTH,
isAnonymous = true, isAnonymous = true,
properties = properties, properties = spoofProvider.deviceProperties,
locale = locale locale = spoofProvider.locale
) )
) )
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -178,18 +157,6 @@ class AuthProvider @Inject constructor(
Preferences.remove(context, PREFERENCE_AUTH_DATA) Preferences.remove(context, PREFERENCE_AUTH_DATA)
} }
private fun setVendingVersion(currentProperties: Properties) {
val vendingVersionIndex = Preferences.getInteger(context, PREFERENCE_VENDING_VERSION)
if (vendingVersionIndex > 0) {
val resources = context.resources
val versionCodes = resources.getStringArray(R.array.pref_vending_version_codes)
val versionStrings = resources.getStringArray(R.array.pref_vending_version)
currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex])
currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex])
}
}
@Throws(Exception::class) @Throws(Exception::class)
private fun throwError(playResponse: PlayResponse, context: Context) { private fun throwError(playResponse: PlayResponse, context: Context) {
when (playResponse.code) { when (playResponse.code) {

View File

@@ -26,12 +26,12 @@ import androidx.core.content.getSystemService
import com.aurora.extensions.isHuawei import com.aurora.extensions.isHuawei
import java.util.Properties import java.util.Properties
class NativeDeviceInfoProvider(val context: Context) { object NativeDeviceInfoProvider {
fun getNativeDeviceProperties(isExport: Boolean = false): Properties { fun getNativeDeviceProperties(context: Context, isExport: Boolean = false): Properties {
val properties = Properties().apply { val properties = Properties().apply {
//Build Props //Build Props
setProperty("UserReadableName", "${Build.DEVICE}-default") setProperty("UserReadableName", "${Build.MANUFACTURER} ${Build.MODEL}")
setProperty("Build.HARDWARE", Build.HARDWARE) setProperty("Build.HARDWARE", Build.HARDWARE)
setProperty( setProperty(
"Build.RADIO", "Build.RADIO",
@@ -71,11 +71,11 @@ class NativeDeviceInfoProvider(val context: Context) {
//Supported Platforms //Supported Platforms
setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ",")) setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ","))
//Supported Features //Supported Features
setProperty("Features", getFeatures().joinToString(separator = ",")) setProperty("Features", getFeatures(context).joinToString(separator = ","))
//Shared Locales //Shared Locales
setProperty("Locales", getLocales().joinToString(separator = ",")) setProperty("Locales", getLocales(context).joinToString(separator = ","))
//Shared Libraries //Shared Libraries
setProperty("SharedLibraries", getSharedLibraries().joinToString(separator = ",")) setProperty("SharedLibraries", getSharedLibraries(context).joinToString(separator = ","))
//GL Extensions //GL Extensions
val activityManager = context.getSystemService<ActivityManager>() val activityManager = context.getSystemService<ActivityManager>()
setProperty( setProperty(
@@ -104,27 +104,25 @@ class NativeDeviceInfoProvider(val context: Context) {
setProperty("SimOperator", "38") setProperty("SimOperator", "38")
} }
if (isHuawei && !isExport) if (isHuawei && !isExport) stripHuaweiProperties(properties)
stripHuaweiProperties(properties)
return properties return properties
} }
private fun getFeatures(): List<String> { private fun getFeatures(context: Context): List<String> {
return context return context
.packageManager .packageManager
.systemAvailableFeatures .systemAvailableFeatures
?.mapNotNull { it.name } ?: emptyList() .mapNotNull { it.name }
} }
private fun getLocales(): List<String> { private fun getLocales(context: Context): List<String> {
return context return context
.assets .assets
.locales .locales
.mapNotNull { it.replace("-", "_") } .mapNotNull { it.replace("-", "_") }
} }
private fun getSharedLibraries(): List<String> { private fun getSharedLibraries(context: Context): List<String> {
return context return context
.packageManager .packageManager
.systemSharedLibraryNames .systemSharedLibraryNames

View File

@@ -23,7 +23,6 @@ import android.content.Context
import android.util.Log import android.util.Log
import com.aurora.store.BuildConfig import com.aurora.store.BuildConfig
import com.aurora.store.util.PathUtil import com.aurora.store.util.PathUtil
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.BufferedInputStream import java.io.BufferedInputStream
import java.io.File import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
@@ -31,22 +30,26 @@ import java.io.IOException
import java.util.Properties import java.util.Properties
import java.util.jar.JarEntry import java.util.jar.JarEntry
import java.util.jar.JarFile import java.util.jar.JarFile
import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
/**
* Provider class to work with device spoof configs imported by users & shipped by GPlayAPI library
*
* Do not use this class directly. Consider using [SpoofProvider] instead.
*/
@Singleton @Singleton
class SpoofDeviceProvider @Inject constructor(@ApplicationContext val context: Context) { open class SpoofDeviceProvider(private val context: Context) {
private val TAG = SpoofDeviceProvider::class.java.simpleName private val TAG = SpoofDeviceProvider::class.java.simpleName
private val SUFFIX = ".properties" private val SUFFIX = ".properties"
val availableDevice: List<Properties> val availableDeviceProperties: MutableList<Properties>
get() { get() {
val propertiesList: MutableList<Properties> = ArrayList() val propertiesList: MutableList<Properties> = ArrayList()
propertiesList.add(0, NativeDeviceInfoProvider(context).getNativeDeviceProperties())
propertiesList.addAll(spoofDevicesFromApk) propertiesList.addAll(spoofDevicesFromApk)
propertiesList.addAll(spoofDevicesFromUser) propertiesList.addAll(spoofDevicesFromUser)
propertiesList.sortBy { it.getProperty("UserReadableName") }
return propertiesList return propertiesList
} }

View File

@@ -20,7 +20,9 @@
package com.aurora.store.data.providers package com.aurora.store.data.providers
import android.content.Context import android.content.Context
import com.aurora.store.R
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import com.google.gson.Gson import com.google.gson.Gson
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.Locale import java.util.Locale
@@ -28,50 +30,65 @@ import java.util.Properties
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
/**
* Provider class to work with device and locale spoofs
*/
@Singleton @Singleton
class SpoofProvider @Inject constructor( class SpoofProvider @Inject constructor(
private val gson: Gson,
@ApplicationContext val context: Context, @ApplicationContext val context: Context,
private val gson: Gson ) : SpoofDeviceProvider(context) {
) {
companion object { companion object {
const val LOCALE_SPOOF_ENABLED = "LOCALE_SPOOF_ENABLED" private const val LOCALE_SPOOF_ENABLED = "LOCALE_SPOOF_ENABLED"
const val LOCALE_SPOOF_LANG = "LOCALE_SPOOF_LANG" private const val LOCALE_SPOOF_LANG = "LOCALE_SPOOF_LANG"
const val LOCALE_SPOOF_COUNTRY = "LOCALE_SPOOF_COUNTRY" private const val LOCALE_SPOOF_COUNTRY = "LOCALE_SPOOF_COUNTRY"
const val DEVICE_SPOOF_ENABLED = "DEVICE_SPOOF_ENABLED" private const val DEVICE_SPOOF_ENABLED = "DEVICE_SPOOF_ENABLED"
const val DEVICE_SPOOF_PROPERTIES = "DEVICE_SPOOF_PROPERTIES" private const val DEVICE_SPOOF_PROPERTIES = "DEVICE_SPOOF_PROPERTIES"
} }
fun isLocaleSpoofEnabled(): Boolean { val availableSpoofDeviceProperties get() = availableDeviceProperties
return Preferences.getBoolean(context, LOCALE_SPOOF_ENABLED) val availableSpoofLocales = Locale.getAvailableLocales().toMutableList().apply {
remove(Locale.getDefault())
sortBy { it.displayName }
} }
fun isDeviceSpoofEnabled(): Boolean { val deviceProperties: Properties
return Preferences.getBoolean(context, DEVICE_SPOOF_ENABLED) get() {
} val currentProperties = if (isDeviceSpoofEnabled) {
spoofDeviceProperties
} else {
NativeDeviceInfoProvider.getNativeDeviceProperties(context)
}
setVendingVersion(currentProperties)
return currentProperties
}
fun getSpoofLocale(): Locale { val locale: Locale
return if (isLocaleSpoofEnabled()) { get() = if (isLocaleSpoofEnabled) {
Locale( spoofLocale
Preferences.getString(context, LOCALE_SPOOF_LANG),
Preferences.getString(context, LOCALE_SPOOF_COUNTRY)
)
} else { } else {
Locale.getDefault() Locale.getDefault()
} }
}
fun getSpoofDeviceProperties(): Properties { val isLocaleSpoofEnabled: Boolean
return if (isDeviceSpoofEnabled()) { get() = Preferences.getBoolean(context, LOCALE_SPOOF_ENABLED)
return gson.fromJson(
Preferences.getString(context, DEVICE_SPOOF_PROPERTIES), val isDeviceSpoofEnabled: Boolean
Properties::class.java get() = Preferences.getBoolean(context, DEVICE_SPOOF_ENABLED)
)
} else { private val spoofLocale: Locale
Properties() get() = Locale(
} Preferences.getString(context, LOCALE_SPOOF_LANG),
} Preferences.getString(context, LOCALE_SPOOF_COUNTRY)
)
private val spoofDeviceProperties: Properties
get() = gson.fromJson(
Preferences.getString(context, DEVICE_SPOOF_PROPERTIES),
Properties::class.java
)
fun setSpoofLocale(locale: Locale) { fun setSpoofLocale(locale: Locale) {
Preferences.putBoolean(context, LOCALE_SPOOF_ENABLED, true) Preferences.putBoolean(context, LOCALE_SPOOF_ENABLED, true)
@@ -94,4 +111,16 @@ class SpoofProvider @Inject constructor(
Preferences.remove(context, DEVICE_SPOOF_ENABLED) Preferences.remove(context, DEVICE_SPOOF_ENABLED)
Preferences.remove(context, DEVICE_SPOOF_PROPERTIES) Preferences.remove(context, DEVICE_SPOOF_PROPERTIES)
} }
private fun setVendingVersion(currentProperties: Properties) {
val vendingVersionIndex = Preferences.getInteger(context, PREFERENCE_VENDING_VERSION)
if (vendingVersionIndex > 0) {
val resources = context.resources
val versionCodes = resources.getStringArray(R.array.pref_vending_version_codes)
val versionStrings = resources.getStringArray(R.array.pref_vending_version)
currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex])
currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex])
}
}
} }

View File

@@ -55,6 +55,7 @@ class DeviceView @JvmOverloads constructor(
@ModelProp @ModelProp
fun markChecked(isChecked: Boolean) { fun markChecked(isChecked: Boolean) {
binding.checkbox.isChecked = isChecked binding.checkbox.isChecked = isChecked
binding.checkbox.isEnabled = !isChecked
} }
@CallbackProp @CallbackProp

View File

@@ -49,6 +49,7 @@ class LocaleView @JvmOverloads constructor(
@ModelProp @ModelProp
fun markChecked(isChecked: Boolean) { fun markChecked(isChecked: Boolean) {
binding.checkbox.isChecked = isChecked binding.checkbox.isChecked = isChecked
binding.checkbox.isEnabled = !isChecked
} }
@CallbackProp @CallbackProp

View File

@@ -25,83 +25,85 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.providers.NativeDeviceInfoProvider
import com.aurora.store.data.providers.SpoofProvider
import com.aurora.store.databinding.FragmentGenericRecyclerBinding 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.epoxy.views.preference.DeviceViewModel_
import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.spoof.SpoofViewModel import com.aurora.store.viewmodel.spoof.SpoofViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Properties import java.util.Properties
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class DeviceSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() { class DeviceSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
private val viewModel: SpoofViewModel by viewModels() private val viewModel: SpoofViewModel by viewModels()
@Inject
lateinit var spoofProvider: SpoofProvider
private lateinit var properties: Properties
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(): DeviceSpoofFragment { fun newInstance(): DeviceSpoofFragment {
return DeviceSpoofFragment().apply { return DeviceSpoofFragment()
}
} }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
properties = if (spoofProvider.isDeviceSpoofEnabled()) {
spoofProvider.getSpoofDeviceProperties()
} else {
NativeDeviceInfoProvider(requireContext()).getNativeDeviceProperties()
}
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.availableDevices.collect { updateController(it) } viewModel.availableDevices.collect { updateController(it) }
} }
} }
viewModel.fetchAvailableDevices()
} }
private fun updateController(locales: List<Properties>) { private fun updateController(devices: List<Properties>) {
binding.recycler.withModels { binding.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
locales
.sortedBy { it.getProperty("UserReadableName") } add(
.forEach { TextDividerViewModel_()
add( .id("default_divider")
DeviceViewModel_() .title(getString(R.string.default_spoof))
.id(it.hashCode()) )
.markChecked(
properties.getProperty("UserReadableName") == it.getProperty( add(
"UserReadableName" DeviceViewModel_()
) .id(viewModel.defaultProperties.hashCode())
) .markChecked(viewModel.isDeviceSelected(viewModel.defaultProperties))
.checked { _, checked -> .checked { _, checked ->
if (checked) { if (checked) {
properties = it viewModel.onDeviceSelected(viewModel.defaultProperties)
saveSelection(it) requestModelBuild()
requestModelBuild() 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()
findNavController().navigate(R.id.forceRestartDialog)
} }
.properties(it) }
) .properties(it)
} )
}
} }
} }
private fun saveSelection(properties: Properties) {
requireContext().toast(R.string.spoof_apply)
spoofProvider.setSpoofDeviceProperties(properties)
}
} }

View File

@@ -20,68 +20,83 @@
package com.aurora.store.view.ui.spoof package com.aurora.store.view.ui.spoof
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.data.providers.SpoofProvider
import com.aurora.store.databinding.FragmentGenericRecyclerBinding 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.epoxy.views.preference.LocaleViewModel_
import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.spoof.SpoofViewModel
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import java.util.Locale import java.util.Locale
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class LocaleSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() { class LocaleSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
private val TAG = LocaleSpoofFragment::class.java.simpleName private val viewModel: SpoofViewModel by viewModels()
@Inject
lateinit var spoofProvider: SpoofProvider
private lateinit var locale: Locale
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(): LocaleSpoofFragment { fun newInstance(): LocaleSpoofFragment {
return LocaleSpoofFragment().apply { return LocaleSpoofFragment()
}
} }
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
locale = if (spoofProvider.isLocaleSpoofEnabled()) { viewLifecycleOwner.lifecycleScope.launch {
spoofProvider.getSpoofLocale() viewModel.availableLocales.collect {
} else { updateController(it)
Locale.getDefault() }
}
try {
updateController(fetchAvailableLocales())
} catch (exception: Exception) {
Log.e(TAG, "Could not get available locales", exception)
} }
} }
private fun updateController(locales: List<Locale>) { private fun updateController(locales: List<Locale>) {
binding.recycler.withModels { binding.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
locales
.sortedBy { it.displayName } add(
.forEach { 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()
findNavController().navigate(R.id.forceRestartDialog)
}
}
.locale(viewModel.defaultLocale)
)
add(
TextDividerViewModel_()
.id("available_divider")
.title(getString(R.string.available_spoof))
)
locales.forEach {
add( add(
LocaleViewModel_() LocaleViewModel_()
.id(it.language) .id(it.language)
.markChecked(locale == it) .markChecked(viewModel.spoofProvider.locale == it)
.checked { _, checked -> .checked { _, checked ->
if (checked) { if (checked) {
locale = it viewModel.onLocaleSelected(it)
saveSelection(it)
requestModelBuild() requestModelBuild()
findNavController().navigate(R.id.forceRestartDialog)
} }
} }
.locale(it) .locale(it)
@@ -89,17 +104,4 @@ class LocaleSpoofFragment : BaseFragment<FragmentGenericRecyclerBinding>() {
} }
} }
} }
private fun fetchAvailableLocales(): List<Locale> {
val locales = Locale.getAvailableLocales()
val localeList: MutableList<Locale> = ArrayList()
localeList.addAll(locales)
localeList.add(0, Locale.getDefault())
return localeList
}
private fun saveSelection(locale: Locale) {
requireContext().toast(R.string.spoof_apply)
spoofProvider.setSpoofLocale(locale)
}
} }

View File

@@ -123,8 +123,7 @@ class SpoofFragment : BaseFragment<FragmentGenericWithPagerBinding>() {
private fun exportDeviceConfig(uri: Uri) { private fun exportDeviceConfig(uri: Uri) {
try { try {
NativeDeviceInfoProvider(requireContext()) NativeDeviceInfoProvider.getNativeDeviceProperties(requireContext(), true)
.getNativeDeviceProperties(true)
.store(requireContext().contentResolver?.openOutputStream(uri), "DEVICE_CONFIG") .store(requireContext().contentResolver?.openOutputStream(uri), "DEVICE_CONFIG")
toast(R.string.toast_export_success) toast(R.string.toast_export_success)
} catch (exception: Exception) { } catch (exception: Exception) {

View File

@@ -1,27 +1,64 @@
package com.aurora.store.viewmodel.spoof package com.aurora.store.viewmodel.spoof
import android.content.Context
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import com.aurora.store.data.providers.NativeDeviceInfoProvider
import com.aurora.store.data.providers.SpoofDeviceProvider import com.aurora.store.data.providers.SpoofProvider
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch import java.util.Locale
import java.util.Properties import java.util.Properties
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class SpoofViewModel @Inject constructor( class SpoofViewModel @Inject constructor(
private val spoofDeviceProvider: SpoofDeviceProvider val spoofProvider: SpoofProvider,
@ApplicationContext private val context: Context
): ViewModel() { ): ViewModel() {
private val _availableDevices: MutableStateFlow<List<Properties>> = MutableStateFlow(emptyList()) val defaultLocale: Locale = Locale.getDefault()
val defaultProperties = NativeDeviceInfoProvider.getNativeDeviceProperties(context)
private var currentDevice = spoofProvider.deviceProperties.getProperty("UserReadableName")
private var currentLocale = spoofProvider.locale
private val _availableLocales: MutableStateFlow<List<Locale>> = MutableStateFlow(
spoofProvider.availableSpoofLocales
)
val availableLocales = _availableLocales.asStateFlow()
private val _availableDevices: MutableStateFlow<List<Properties>> = MutableStateFlow(
spoofProvider.availableSpoofDeviceProperties
)
val availableDevices = _availableDevices.asStateFlow() val availableDevices = _availableDevices.asStateFlow()
fun fetchAvailableDevices() { fun isDeviceSelected(properties: Properties): Boolean {
viewModelScope.launch(Dispatchers.IO) { return currentDevice == properties.getProperty("UserReadableName")
_availableDevices.value = spoofDeviceProvider.availableDevice }
fun onDeviceSelected(properties: Properties) {
currentDevice = properties.getProperty("UserReadableName")
if (currentDevice == defaultProperties.getProperty("UserReadableName")) {
spoofProvider.removeSpoofDeviceProperties()
} else {
spoofProvider.setSpoofDeviceProperties(properties)
}
}
fun isLocaleSelected(locale: Locale): Boolean {
return currentLocale == locale
}
fun onLocaleSelected(locale: Locale) {
currentLocale = locale
if (currentLocale == defaultLocale) {
spoofProvider.removeSpoofLocale()
} else {
spoofProvider.setSpoofLocale(locale)
} }
} }
} }

View File

@@ -467,4 +467,8 @@
<!-- UnarchivePackageReceiver --> <!-- UnarchivePackageReceiver -->
<string name="authentication_required_title">Authentication required</string> <string name="authentication_required_title">Authentication required</string>
<string name="authentication_required_unarchive">Please log in to your Google Play account to unarchive the app!</string> <string name="authentication_required_unarchive">Please log in to your Google Play account to unarchive the app!</string>
<!-- SpoofFragment -->
<string name="default_spoof">Default</string>
<string name="available_spoof">Available</string>
</resources> </resources>