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:
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user