Add edge cases to detect MIUI & Huawei devices to select correct installer and apply spoof
This commit is contained in:
63
app/src/main/java/com/aurora/extensions/Dialog.kt
Normal file
63
app/src/main/java/com/aurora/extensions/Dialog.kt
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
fun Context.showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
|
||||
runOnUiThread {
|
||||
val backgroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
|
||||
val builder = MaterialAlertDialogBuilder(this).apply {
|
||||
setTitle(titleId)
|
||||
setMessage(messageId)
|
||||
setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
|
||||
background = ColorDrawable(backgroundColor)
|
||||
}.create()
|
||||
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.showDialog(title: String, message: String) {
|
||||
runOnUiThread {
|
||||
val backgroundColor: Int = getStyledAttributeColor(android.R.attr.colorBackground)
|
||||
val builder = MaterialAlertDialogBuilder(this).apply {
|
||||
setTitle(title)
|
||||
setMessage(message)
|
||||
setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
|
||||
background = ColorDrawable(backgroundColor)
|
||||
}.create()
|
||||
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
|
||||
fun Fragment.showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
|
||||
requireContext().showDialog(titleId, messageId)
|
||||
}
|
||||
|
||||
fun Fragment.showDialog(title: String, message: String) {
|
||||
requireContext().showDialog(title, message)
|
||||
}
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
package com.aurora.extensions
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import java.util.*
|
||||
|
||||
|
||||
fun isLAndAbove(): Boolean {
|
||||
@@ -48,4 +50,47 @@ fun isQAndAbove(): Boolean {
|
||||
|
||||
fun isRAndAbove(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
}
|
||||
|
||||
fun isMIUI(): Boolean {
|
||||
return getSystemProperty("ro.miui.ui.version.name").isNotEmpty()
|
||||
}
|
||||
|
||||
fun isHuawei(): Boolean {
|
||||
return Build.MANUFACTURER.toLowerCase(Locale.getDefault()).contains("huawei")
|
||||
|| Build.HARDWARE.toLowerCase(Locale.getDefault()).contains("kirin")
|
||||
|| Build.HARDWARE.toLowerCase(Locale.getDefault()).contains("hi3")
|
||||
}
|
||||
|
||||
fun getMIUIVersion(): String {
|
||||
val version = getSystemProperty("ro.miui.ui.version.name")
|
||||
val versionCode = getSystemProperty("ro.miui.ui.version.code")
|
||||
return if (version.isNotEmpty() && versionCode.isNotEmpty())
|
||||
"$version.$versionCode"
|
||||
else
|
||||
"unknown"
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
fun isMiuiOptimizationDisabled(): Boolean {
|
||||
if ("0" == getSystemProperty("persist.sys.miui_optimization")) {
|
||||
return true
|
||||
} else try {
|
||||
return Class.forName("android.miui.AppOpsUtils")
|
||||
.getDeclaredMethod("isXOptMode")
|
||||
.invoke(null) as Boolean
|
||||
} catch (e: java.lang.Exception) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
fun getSystemProperty(key: String): String {
|
||||
return try {
|
||||
Class.forName("android.os.SystemProperties")
|
||||
.getDeclaredMethod("get", String::class.java)
|
||||
.invoke(null, key) as String
|
||||
} catch (e: Exception) {
|
||||
""
|
||||
}
|
||||
}
|
||||
@@ -20,16 +20,15 @@
|
||||
package com.aurora.store.data.providers
|
||||
|
||||
import android.content.Context
|
||||
import com.aurora.store.data.SingletonHolder
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.lang.reflect.Modifier
|
||||
import java.util.*
|
||||
|
||||
class SpoofProvider private constructor(var context: Context) {
|
||||
class SpoofProvider constructor(var context: Context) {
|
||||
|
||||
companion object : SingletonHolder<SpoofProvider, Context>(::SpoofProvider) {
|
||||
companion object {
|
||||
const val LOCALE_SPOOF_ENABLED = "LOCALE_SPOOF_ENABLED"
|
||||
const val LOCALE_SPOOF_LANG = "LOCALE_SPOOF_LANG"
|
||||
const val LOCALE_SPOOF_COUNTRY = "LOCALE_SPOOF_COUNTRY"
|
||||
|
||||
@@ -19,15 +19,13 @@
|
||||
|
||||
package com.aurora.store.view.ui.onboarding
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.StringRes
|
||||
import com.aurora.extensions.getStyledAttributeColor
|
||||
import com.aurora.extensions.runOnUiThread
|
||||
import com.aurora.extensions.isMIUI
|
||||
import com.aurora.extensions.isMiuiOptimizationDisabled
|
||||
import com.aurora.extensions.showDialog
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.installer.ServiceInstaller
|
||||
import com.aurora.store.data.model.Installer
|
||||
@@ -38,7 +36,7 @@ import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.view.epoxy.views.preference.InstallerViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.aurora.store.view.ui.sheets.DeviceMiuiSheet
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import java.nio.charset.StandardCharsets
|
||||
@@ -94,10 +92,21 @@ class InstallerFragment : BaseFragment() {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (isMIUI() && !isMiuiOptimizationDisabled()) {
|
||||
DeviceMiuiSheet.newInstance().show(childFragmentManager, DeviceMiuiSheet.TAG)
|
||||
}
|
||||
}
|
||||
|
||||
private fun save(installerId: Int) {
|
||||
when (installerId) {
|
||||
0 -> {
|
||||
if (isMIUI() && !isMiuiOptimizationDisabled()) {
|
||||
DeviceMiuiSheet.newInstance().show(childFragmentManager, DeviceMiuiSheet.TAG)
|
||||
}
|
||||
this.installerId = installerId
|
||||
save(PREFERENCE_INSTALLER_ID, installerId)
|
||||
}
|
||||
2 -> {
|
||||
if (checkRootAvailability()) {
|
||||
this.installerId = installerId
|
||||
@@ -150,20 +159,4 @@ class InstallerFragment : BaseFragment() {
|
||||
ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
|
||||
)
|
||||
}
|
||||
|
||||
private fun showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
|
||||
runOnUiThread {
|
||||
val backgroundColor: Int =
|
||||
requireContext().getStyledAttributeColor(android.R.attr.colorBackground)
|
||||
|
||||
val builder = MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(titleId)
|
||||
setMessage(messageId)
|
||||
setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
|
||||
background = ColorDrawable(backgroundColor)
|
||||
}.create()
|
||||
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,15 +19,12 @@
|
||||
|
||||
package com.aurora.store.view.ui.preferences
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.aurora.extensions.getStyledAttributeColor
|
||||
import com.aurora.extensions.runOnUiThread
|
||||
import com.aurora.extensions.showDialog
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.installer.ServiceInstaller
|
||||
@@ -36,7 +33,6 @@ import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.save
|
||||
import com.aurora.store.view.custom.preference.AuroraListPreference
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.topjohnwu.superuser.Shell
|
||||
|
||||
|
||||
@@ -109,20 +105,4 @@ class InstallationPreference : PreferenceFragmentCompat() {
|
||||
ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
|
||||
)
|
||||
}
|
||||
|
||||
private fun showDialog(@StringRes titleId: Int, @StringRes messageId: Int) {
|
||||
runOnUiThread {
|
||||
val backgroundColor: Int =
|
||||
requireContext().getStyledAttributeColor(android.R.attr.colorBackground)
|
||||
|
||||
val builder = MaterialAlertDialogBuilder(requireContext()).apply {
|
||||
setTitle(titleId)
|
||||
setMessage(messageId)
|
||||
setPositiveButton(android.R.string.ok) { dialog: DialogInterface, _ -> dialog.dismiss() }
|
||||
background = ColorDrawable(backgroundColor)
|
||||
}.create()
|
||||
|
||||
builder.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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.sheets
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.aurora.extensions.load
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.gplayapi.DeviceManager
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.providers.SpoofProvider
|
||||
import com.aurora.store.databinding.SheetDeviceHuaweiBinding
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
import java.util.*
|
||||
|
||||
class DeviceHuaweiSheet : BaseBottomSheet() {
|
||||
|
||||
private lateinit var B: SheetDeviceHuaweiBinding
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "DeviceHuaweiSheet"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): DeviceHuaweiSheet {
|
||||
return DeviceHuaweiSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateContentView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
B = SheetDeviceHuaweiBinding.inflate(inflater, container, false)
|
||||
|
||||
inflateData()
|
||||
attachAction()
|
||||
|
||||
return B.root
|
||||
}
|
||||
|
||||
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
}
|
||||
|
||||
private fun inflateData() {
|
||||
B.imgIcon.load(R.drawable.ic_huawei_logo) {
|
||||
transform(CircleCrop())
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachAction() {
|
||||
B.btnPrimary.setOnClickListener {
|
||||
applySpoof()
|
||||
}
|
||||
|
||||
B.btnSecondary.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun applySpoof() {
|
||||
val properties: Properties? = when (Build.VERSION.SDK_INT) {
|
||||
30, 29 -> DeviceManager.loadProperties("op_8_pro.properties")
|
||||
28 -> DeviceManager.loadProperties("nk_9.properties")
|
||||
27 -> DeviceManager.loadProperties("mi_8_se.properties")
|
||||
26 -> DeviceManager.loadProperties("op_3.properties")
|
||||
else -> DeviceManager.loadProperties("op_x.properties")
|
||||
}
|
||||
|
||||
properties?.let {
|
||||
SpoofProvider(requireContext()).setSpoofDeviceProperties(it)
|
||||
toast(R.string.toast_spoof_applied)
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.sheets
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.aurora.extensions.load
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.databinding.SheetDeviceMiuiBinding
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
|
||||
class DeviceMiuiSheet : BaseBottomSheet() {
|
||||
|
||||
private lateinit var B: SheetDeviceMiuiBinding
|
||||
|
||||
companion object {
|
||||
|
||||
const val TAG = "DeviceMiuiSheet"
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): DeviceMiuiSheet {
|
||||
return DeviceMiuiSheet().apply {
|
||||
arguments = Bundle().apply {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateContentView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
B = SheetDeviceMiuiBinding.inflate(inflater, container, false)
|
||||
|
||||
inflateData()
|
||||
attachAction()
|
||||
|
||||
return B.root
|
||||
}
|
||||
|
||||
override fun onContentViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
||||
}
|
||||
|
||||
private fun inflateData() {
|
||||
B.imgIcon.load(R.drawable.ic_xiaomi_logo) {
|
||||
transform(CircleCrop())
|
||||
}
|
||||
}
|
||||
|
||||
private fun attachAction() {
|
||||
B.btnPrimary.setOnClickListener {
|
||||
openDeveloperSettings()
|
||||
}
|
||||
|
||||
B.btnSecondary.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDeveloperSettings() {
|
||||
try {
|
||||
startActivity(Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS))
|
||||
} catch (e: Exception) {
|
||||
toast(R.string.toast_developer_setting_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,17 +23,15 @@ import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.aurora.extensions.*
|
||||
import com.aurora.store.MainActivity
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.AuthState
|
||||
import com.aurora.store.data.event.BusEvent
|
||||
import com.aurora.store.databinding.ActivitySplashBinding
|
||||
import com.aurora.extensions.hide
|
||||
import com.aurora.extensions.load
|
||||
import com.aurora.extensions.open
|
||||
import com.aurora.extensions.show
|
||||
import com.aurora.store.view.ui.commons.BaseActivity
|
||||
import com.aurora.store.view.ui.commons.BlacklistActivity
|
||||
import com.aurora.store.view.ui.sheets.DeviceHuaweiSheet
|
||||
import com.aurora.store.view.ui.spoof.SpoofActivity
|
||||
import com.aurora.store.viewmodel.auth.AuthViewModel
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
|
||||
@@ -91,6 +89,13 @@ class SplashActivity : BaseActivity() {
|
||||
AuthState.Unavailable -> {
|
||||
updateStatus("You need to login first")
|
||||
updateActionLayout(true)
|
||||
|
||||
if (isHuawei()) {
|
||||
runOnUiThread {
|
||||
DeviceHuaweiSheet.newInstance()
|
||||
.show(supportFragmentManager, DeviceHuaweiSheet.TAG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AuthState.SignedIn -> {
|
||||
|
||||
@@ -23,13 +23,13 @@ import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.providers.NativeDeviceInfoProvider
|
||||
import com.aurora.store.data.providers.SpoofDeviceProvider
|
||||
import com.aurora.store.data.providers.SpoofProvider
|
||||
import com.aurora.store.databinding.FragmentGenericRecyclerBinding
|
||||
import com.aurora.store.util.Log
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.view.epoxy.views.preference.DeviceViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import nl.komponents.kovenant.task
|
||||
@@ -68,7 +68,7 @@ class DeviceSpoofFragment : BaseFragment() {
|
||||
)
|
||||
|
||||
properties = NativeDeviceInfoProvider(requireContext()).getNativeDeviceProperties()
|
||||
spoofProvider = SpoofProvider.with(requireContext())
|
||||
spoofProvider = SpoofProvider(requireContext())
|
||||
|
||||
return B.root
|
||||
}
|
||||
@@ -91,21 +91,27 @@ class DeviceSpoofFragment : BaseFragment() {
|
||||
private fun updateController(locales: List<Properties>) {
|
||||
B.recycler.withModels {
|
||||
setFilterDuplicates(true)
|
||||
locales.forEach {
|
||||
add(
|
||||
DeviceViewModel_()
|
||||
.id(it.hashCode())
|
||||
.markChecked(properties == it)
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
properties = it
|
||||
saveSelection(it)
|
||||
requestModelBuild()
|
||||
locales
|
||||
.sortedBy { it.getProperty("UserReadableName") }
|
||||
.forEach {
|
||||
add(
|
||||
DeviceViewModel_()
|
||||
.id(it.hashCode())
|
||||
.markChecked(
|
||||
properties.getProperty("UserReadableName") == it.getProperty(
|
||||
"UserReadableName"
|
||||
)
|
||||
)
|
||||
.checked { _, checked ->
|
||||
if (checked) {
|
||||
properties = it
|
||||
saveSelection(it)
|
||||
requestModelBuild()
|
||||
}
|
||||
}
|
||||
}
|
||||
.properties(it)
|
||||
)
|
||||
}
|
||||
.properties(it)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class LocaleSpoofFragment : BaseFragment() {
|
||||
)
|
||||
)
|
||||
|
||||
spoofProvider = SpoofProvider.with(requireContext())
|
||||
spoofProvider = SpoofProvider(requireContext())
|
||||
|
||||
return B.root
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ import java.net.UnknownHostException
|
||||
|
||||
class AuthViewModel(application: Application) : BaseAndroidViewModel(application) {
|
||||
|
||||
private val spoofProvider = SpoofProvider.with(getApplication())
|
||||
private val spoofProvider = SpoofProvider(getApplication())
|
||||
|
||||
val liveData: MutableLiveData<AuthState> = MutableLiveData()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user