Add edge cases to detect MIUI & Huawei devices to select correct installer and apply spoof

This commit is contained in:
Rahul Kumar Patel
2021-02-25 16:21:04 +05:30
parent 51c4e3f27f
commit 5aa4d6fe5e
17 changed files with 659 additions and 70 deletions

View 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)
}

View File

@@ -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) {
""
}
}

View File

@@ -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"

View File

@@ -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()
}
}
}

View File

@@ -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()
}
}
}

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}
}

View File

@@ -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 -> {

View File

@@ -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)
)
}
}
}

View File

@@ -65,7 +65,7 @@ class LocaleSpoofFragment : BaseFragment() {
)
)
spoofProvider = SpoofProvider.with(requireContext())
spoofProvider = SpoofProvider(requireContext())
return B.root
}

View File

@@ -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()