mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-05-14 13:17:09 +02:00
Added logic to superuser screen
This commit is contained in:
parent
c44b85ea87
commit
b66b82a6e9
@ -33,7 +33,8 @@ abstract class BaseActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding
|
|||||||
protected abstract val layoutRes: Int
|
protected abstract val layoutRes: Int
|
||||||
protected abstract val viewModel: ViewModel
|
protected abstract val viewModel: ViewModel
|
||||||
protected open val themeRes: Int = R.style.MagiskTheme
|
protected open val themeRes: Int = R.style.MagiskTheme
|
||||||
protected open val snackbarView get() = binding.root
|
|
||||||
|
open val snackbarView get() = binding.root
|
||||||
|
|
||||||
private val resultCallbacks by lazy { SparseArrayCompat<RequestCallback>() }
|
private val resultCallbacks by lazy { SparseArrayCompat<RequestCallback>() }
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ val redesignModule = module {
|
|||||||
viewModel { RequestViewModel() }
|
viewModel { RequestViewModel() }
|
||||||
viewModel { SafetynetViewModel() }
|
viewModel { SafetynetViewModel() }
|
||||||
viewModel { SettingsViewModel() }
|
viewModel { SettingsViewModel() }
|
||||||
viewModel { SuperuserViewModel(get(), get()) }
|
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
|
||||||
viewModel { ThemeViewModel() }
|
viewModel { ThemeViewModel() }
|
||||||
|
|
||||||
viewModel { MainViewModel() }
|
viewModel { MainViewModel() }
|
||||||
|
@ -2,14 +2,17 @@ package com.topjohnwu.magisk.model.events
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import com.topjohnwu.magisk.base.BaseActivity
|
||||||
|
import com.topjohnwu.magisk.extensions.snackbar
|
||||||
|
|
||||||
class SnackbarEvent private constructor(
|
class SnackbarEvent private constructor(
|
||||||
@StringRes private val messageRes: Int,
|
@StringRes private val messageRes: Int,
|
||||||
private val messageString: String?,
|
private val messageString: String?,
|
||||||
val length: Int,
|
val length: Int,
|
||||||
val f: Snackbar.() -> Unit
|
val f: Snackbar.() -> Unit
|
||||||
) : ViewEvent() {
|
) : ViewEvent(), ActivityExecutor {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@StringRes messageRes: Int,
|
@StringRes messageRes: Int,
|
||||||
@ -24,4 +27,11 @@ class SnackbarEvent private constructor(
|
|||||||
) : this(-1, message, length, f)
|
) : this(-1, message, length, f)
|
||||||
|
|
||||||
fun message(context: Context): String = messageString ?: context.getString(messageRes)
|
fun message(context: Context): String = messageString ?: context.getString(messageRes)
|
||||||
|
|
||||||
|
override fun invoke(activity: AppCompatActivity) {
|
||||||
|
if (activity is BaseActivity<*, *>) {
|
||||||
|
activity.snackbar(activity.snackbarView, message(activity), length, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,14 @@ import com.topjohnwu.magisk.view.MagiskDialog
|
|||||||
|
|
||||||
abstract class DialogEvent : ViewEvent(), ContextExecutor {
|
abstract class DialogEvent : ViewEvent(), ContextExecutor {
|
||||||
|
|
||||||
|
protected lateinit var dialog: MagiskDialog
|
||||||
|
|
||||||
override fun invoke(context: Context) {
|
override fun invoke(context: Context) {
|
||||||
MagiskDialog(context).apply(this::build).reveal()
|
dialog = MagiskDialog(context).apply(this::build).reveal()
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun build(dialog: MagiskDialog)
|
abstract fun build(dialog: MagiskDialog)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias GenericDialogListener = () -> Unit
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.topjohnwu.magisk.model.events.dialog
|
||||||
|
|
||||||
|
import android.hardware.fingerprint.FingerprintManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper
|
||||||
|
import com.topjohnwu.magisk.utils.Utils
|
||||||
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
|
|
||||||
|
class FingerprintDialog(
|
||||||
|
builder: Builder.() -> Unit
|
||||||
|
) : DialogEvent() {
|
||||||
|
|
||||||
|
private val callbacks = Builder().apply(builder)
|
||||||
|
private var helper: Helper? = null
|
||||||
|
get() {
|
||||||
|
if (field == null) {
|
||||||
|
runCatching { field = Helper() }
|
||||||
|
}
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun build(dialog: MagiskDialog) {
|
||||||
|
dialog.applyIcon(R.drawable.ic_fingerprint)
|
||||||
|
.applyTitle(R.string.auth_fingerprint)
|
||||||
|
.cancellable(false) //possible fix for devices that have flawed under-screen sensor implementation
|
||||||
|
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
|
titleRes = android.R.string.cancel
|
||||||
|
onClick {
|
||||||
|
callbacks.listenerOnFailure()
|
||||||
|
helper?.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onShow {
|
||||||
|
helper?.authenticate() ?: it.let {
|
||||||
|
callbacks.listenerOnFailure()
|
||||||
|
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT)
|
||||||
|
it.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Builder internal constructor() {
|
||||||
|
internal var listenerOnFailure: GenericDialogListener = {}
|
||||||
|
internal var listenerOnSuccess: GenericDialogListener = {}
|
||||||
|
|
||||||
|
fun onFailure(listener: GenericDialogListener) {
|
||||||
|
listenerOnFailure = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSuccess(listener: GenericDialogListener) {
|
||||||
|
listenerOnSuccess = listener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class Helper @Throws(Exception::class) constructor() : FingerprintHelper() {
|
||||||
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||||
|
dialog.applyMessage(errString)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationHelp(helpCode: Int, helpString: CharSequence) {
|
||||||
|
dialog.applyMessage(helpString)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationFailed() {
|
||||||
|
dialog.applyMessage(R.string.auth_fail)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAuthenticationSucceeded(result: FingerprintManager.AuthenticationResult) {
|
||||||
|
callbacks.listenerOnSuccess()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.topjohnwu.magisk.model.events.dialog
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.magisk.view.MagiskDialog
|
||||||
|
|
||||||
|
class SuperuserRevokeDialog(
|
||||||
|
builder: Builder.() -> Unit
|
||||||
|
) : DialogEvent() {
|
||||||
|
|
||||||
|
private val callbacks = Builder().apply(builder)
|
||||||
|
|
||||||
|
override fun build(dialog: MagiskDialog) {
|
||||||
|
dialog.applyTitle(R.string.su_revoke_title)
|
||||||
|
.applyMessage(R.string.su_revoke_msg, callbacks.appName)
|
||||||
|
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
|
||||||
|
titleRes = R.string.yes
|
||||||
|
onClick { callbacks.listenerOnSuccess() }
|
||||||
|
}
|
||||||
|
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
|
||||||
|
titleRes = R.string.no_thanks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Builder internal constructor() {
|
||||||
|
var appName: String = ""
|
||||||
|
|
||||||
|
internal var listenerOnSuccess: GenericDialogListener = {}
|
||||||
|
|
||||||
|
fun onSuccess(listener: GenericDialogListener) {
|
||||||
|
listenerOnSuccess = listener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ import androidx.fragment.app.Fragment
|
|||||||
import androidx.transition.TransitionManager
|
import androidx.transition.TransitionManager
|
||||||
import com.topjohnwu.magisk.R
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.base.BaseActivity
|
import com.topjohnwu.magisk.base.BaseActivity
|
||||||
|
import com.topjohnwu.magisk.extensions.snackbar
|
||||||
|
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||||
import com.topjohnwu.magisk.model.events.ViewEvent
|
import com.topjohnwu.magisk.model.events.ViewEvent
|
||||||
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
|
import com.topjohnwu.magisk.model.navigation.MagiskNavigationEvent
|
||||||
import com.topjohnwu.magisk.model.navigation.Navigator
|
import com.topjohnwu.magisk.model.navigation.Navigator
|
||||||
@ -54,6 +56,9 @@ abstract class CompatActivity<ViewModel : CompatViewModel, Binding : ViewDataBin
|
|||||||
|
|
||||||
override fun onEventDispatched(event: ViewEvent) {
|
override fun onEventDispatched(event: ViewEvent) {
|
||||||
delegate.onEventExecute(event, this)
|
delegate.onEventExecute(event, this)
|
||||||
|
when (event) {
|
||||||
|
is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBackPressed() {
|
override fun onBackPressed() {
|
||||||
|
@ -1,20 +1,34 @@
|
|||||||
package com.topjohnwu.magisk.redesign.superuser
|
package com.topjohnwu.magisk.redesign.superuser
|
||||||
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.res.Resources
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
import com.topjohnwu.magisk.data.database.PolicyDao
|
import com.topjohnwu.magisk.data.database.PolicyDao
|
||||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||||
import com.topjohnwu.magisk.extensions.applySchedulers
|
import com.topjohnwu.magisk.extensions.applySchedulers
|
||||||
import com.topjohnwu.magisk.extensions.subscribeK
|
import com.topjohnwu.magisk.extensions.subscribeK
|
||||||
|
import com.topjohnwu.magisk.extensions.toggle
|
||||||
|
import com.topjohnwu.magisk.model.entity.MagiskPolicy
|
||||||
import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
|
import com.topjohnwu.magisk.model.entity.recycler.PolicyRvItem
|
||||||
|
import com.topjohnwu.magisk.model.events.PolicyEnableEvent
|
||||||
|
import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
|
||||||
|
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||||
|
import com.topjohnwu.magisk.model.events.dialog.FingerprintDialog
|
||||||
|
import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog
|
||||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||||
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
import com.topjohnwu.magisk.redesign.compat.CompatViewModel
|
||||||
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||||
import com.topjohnwu.magisk.utils.DiffObservableList
|
import com.topjohnwu.magisk.utils.DiffObservableList
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper
|
||||||
|
import com.topjohnwu.magisk.utils.RxBus
|
||||||
|
import io.reactivex.Single
|
||||||
|
|
||||||
class SuperuserViewModel(
|
class SuperuserViewModel(
|
||||||
|
private val rxBus: RxBus,
|
||||||
private val db: PolicyDao,
|
private val db: PolicyDao,
|
||||||
private val packageManager: PackageManager
|
private val packageManager: PackageManager,
|
||||||
|
private val resources: Resources
|
||||||
) : CompatViewModel() {
|
) : CompatViewModel() {
|
||||||
|
|
||||||
val items = diffListOf<PolicyRvItem>()
|
val items = diffListOf<PolicyRvItem>()
|
||||||
@ -22,6 +36,17 @@ class SuperuserViewModel(
|
|||||||
it.bindExtra(BR.viewModel, this)
|
it.bindExtra(BR.viewModel, this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
rxBus.register<PolicyEnableEvent>()
|
||||||
|
.subscribeK { togglePolicy(it.item, it.enable) }
|
||||||
|
.add()
|
||||||
|
rxBus.register<PolicyUpdateEvent>()
|
||||||
|
.subscribeK { updatePolicy(it) }
|
||||||
|
.add()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
override fun refresh() = db.fetchAll()
|
override fun refresh() = db.fetchAll()
|
||||||
.flattenAsFlowable { it }
|
.flattenAsFlowable { it }
|
||||||
.parallel()
|
.parallel()
|
||||||
@ -39,12 +64,77 @@ class SuperuserViewModel(
|
|||||||
.applyViewModel(this)
|
.applyViewModel(this)
|
||||||
.subscribeK { items.update(it.first, it.second) }
|
.subscribeK { items.update(it.first, it.second) }
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
fun hidePressed() = Navigation.hide().publish()
|
fun hidePressed() = Navigation.hide().publish()
|
||||||
|
|
||||||
fun deletePressed(item: PolicyRvItem) {
|
fun deletePressed(item: PolicyRvItem) {
|
||||||
TODO()
|
fun updateState() = deletePolicy(item.item)
|
||||||
|
.subscribeK { items.removeAll { it.itemSameAs(item) } }
|
||||||
|
.add()
|
||||||
|
|
||||||
|
if (FingerprintHelper.useFingerprint()) {
|
||||||
|
FingerprintDialog {
|
||||||
|
onSuccess { updateState() }
|
||||||
|
}.publish()
|
||||||
|
} else {
|
||||||
|
SuperuserRevokeDialog {
|
||||||
|
appName = item.item.appName
|
||||||
|
onSuccess { updateState() }
|
||||||
|
}.publish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
private fun updatePolicy(it: PolicyUpdateEvent) = when (it) {
|
||||||
|
is PolicyUpdateEvent.Notification -> updatePolicy(it.item).map {
|
||||||
|
when {
|
||||||
|
it.notification -> R.string.su_snack_notif_on
|
||||||
|
else -> R.string.su_snack_notif_off
|
||||||
|
} to it.appName
|
||||||
|
}
|
||||||
|
is PolicyUpdateEvent.Log -> updatePolicy(it.item).map {
|
||||||
|
when {
|
||||||
|
it.logging -> R.string.su_snack_log_on
|
||||||
|
else -> R.string.su_snack_log_off
|
||||||
|
} to it.appName
|
||||||
|
}
|
||||||
|
}.map { resources.getString(it.first, it.second) }
|
||||||
|
.subscribeK { SnackbarEvent(it).publish() }
|
||||||
|
.add()
|
||||||
|
|
||||||
|
private fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
|
||||||
|
fun updateState() {
|
||||||
|
val policy = if (enable) MagiskPolicy.ALLOW else MagiskPolicy.DENY
|
||||||
|
val app = item.item.copy(policy = policy)
|
||||||
|
|
||||||
|
updatePolicy(app)
|
||||||
|
.map { it.policy == MagiskPolicy.ALLOW }
|
||||||
|
.map { if (it) R.string.su_snack_grant else R.string.su_snack_deny }
|
||||||
|
.map { resources.getString(it).format(item.item.appName) }
|
||||||
|
.subscribeK { SnackbarEvent(it).publish() }
|
||||||
|
.add()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FingerprintHelper.useFingerprint()) {
|
||||||
|
FingerprintDialog {
|
||||||
|
onSuccess { updateState() }
|
||||||
|
onFailure { item.isEnabled.toggle() }
|
||||||
|
}.publish()
|
||||||
|
} else {
|
||||||
|
updateState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---
|
||||||
|
|
||||||
|
private fun updatePolicy(policy: MagiskPolicy) =
|
||||||
|
db.update(policy).andThen(Single.just(policy))
|
||||||
|
|
||||||
|
private fun deletePolicy(policy: MagiskPolicy) =
|
||||||
|
db.delete(policy.uid).andThen(Single.just(policy))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun <T : ComparableRvItem<T>> diffListOf(
|
inline fun <T : ComparableRvItem<T>> diffListOf(
|
||||||
|
@ -10,10 +10,8 @@ import android.view.LayoutInflater
|
|||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.databinding.DataBindingUtil
|
|
||||||
import androidx.databinding.ViewDataBinding
|
import androidx.databinding.ViewDataBinding
|
||||||
import com.topjohnwu.magisk.BR
|
import com.topjohnwu.magisk.BR
|
||||||
import com.topjohnwu.magisk.R
|
|
||||||
import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
|
import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
|
||||||
import com.topjohnwu.magisk.utils.KObservableField
|
import com.topjohnwu.magisk.utils.KObservableField
|
||||||
|
|
||||||
@ -21,12 +19,11 @@ class MagiskDialog @JvmOverloads constructor(
|
|||||||
context: Context, theme: Int = 0
|
context: Context, theme: Int = 0
|
||||||
) : AlertDialog(context, theme) {
|
) : AlertDialog(context, theme) {
|
||||||
|
|
||||||
private val binding: DialogMagiskBaseBinding
|
private val binding: DialogMagiskBaseBinding =
|
||||||
|
DialogMagiskBaseBinding.inflate(LayoutInflater.from(context))
|
||||||
private val data = Data()
|
private val data = Data()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val layoutInflater = LayoutInflater.from(context)
|
|
||||||
binding = DataBindingUtil.inflate(layoutInflater, R.layout.dialog_magisk_base, null, false)
|
|
||||||
binding.setVariable(BR.data, data)
|
binding.setVariable(BR.data, data)
|
||||||
super.setView(binding.root)
|
super.setView(binding.root)
|
||||||
}
|
}
|
||||||
@ -106,8 +103,8 @@ class MagiskDialog @JvmOverloads constructor(
|
|||||||
fun applyTitle(title: CharSequence) =
|
fun applyTitle(title: CharSequence) =
|
||||||
apply { data.title.value = title }
|
apply { data.title.value = title }
|
||||||
|
|
||||||
fun applyMessage(@StringRes stringRes: Int) =
|
fun applyMessage(@StringRes stringRes: Int, vararg args: Any) =
|
||||||
apply { data.message.value = context.getString(stringRes) }
|
apply { data.message.value = context.getString(stringRes, *args) }
|
||||||
|
|
||||||
fun applyMessage(message: CharSequence) =
|
fun applyMessage(message: CharSequence) =
|
||||||
apply { data.message.value = message }
|
apply { data.message.value = message }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user