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 viewModel: ViewModel
|
||||
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>() }
|
||||
|
||||
|
@ -23,7 +23,7 @@ val redesignModule = module {
|
||||
viewModel { RequestViewModel() }
|
||||
viewModel { SafetynetViewModel() }
|
||||
viewModel { SettingsViewModel() }
|
||||
viewModel { SuperuserViewModel(get(), get()) }
|
||||
viewModel { SuperuserViewModel(get(), get(), get(), get()) }
|
||||
viewModel { ThemeViewModel() }
|
||||
|
||||
viewModel { MainViewModel() }
|
||||
|
@ -2,14 +2,17 @@ package com.topjohnwu.magisk.model.events
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.topjohnwu.magisk.base.BaseActivity
|
||||
import com.topjohnwu.magisk.extensions.snackbar
|
||||
|
||||
class SnackbarEvent private constructor(
|
||||
@StringRes private val messageRes: Int,
|
||||
private val messageString: String?,
|
||||
val length: Int,
|
||||
val f: Snackbar.() -> Unit
|
||||
) : ViewEvent() {
|
||||
) : ViewEvent(), ActivityExecutor {
|
||||
|
||||
constructor(
|
||||
@StringRes messageRes: Int,
|
||||
@ -24,4 +27,11 @@ class SnackbarEvent private constructor(
|
||||
) : this(-1, message, length, f)
|
||||
|
||||
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 {
|
||||
|
||||
protected lateinit var dialog: MagiskDialog
|
||||
|
||||
override fun invoke(context: Context) {
|
||||
MagiskDialog(context).apply(this::build).reveal()
|
||||
dialog = MagiskDialog(context).apply(this::build).reveal()
|
||||
}
|
||||
|
||||
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 com.topjohnwu.magisk.R
|
||||
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.navigation.MagiskNavigationEvent
|
||||
import com.topjohnwu.magisk.model.navigation.Navigator
|
||||
@ -54,6 +56,9 @@ abstract class CompatActivity<ViewModel : CompatViewModel, Binding : ViewDataBin
|
||||
|
||||
override fun onEventDispatched(event: ViewEvent) {
|
||||
delegate.onEventExecute(event, this)
|
||||
when (event) {
|
||||
is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
@ -1,20 +1,34 @@
|
||||
package com.topjohnwu.magisk.redesign.superuser
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.data.database.PolicyDao
|
||||
import com.topjohnwu.magisk.databinding.ComparableRvItem
|
||||
import com.topjohnwu.magisk.extensions.applySchedulers
|
||||
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.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.redesign.compat.CompatViewModel
|
||||
import com.topjohnwu.magisk.redesign.home.itemBindingOf
|
||||
import com.topjohnwu.magisk.utils.DiffObservableList
|
||||
import com.topjohnwu.magisk.utils.FingerprintHelper
|
||||
import com.topjohnwu.magisk.utils.RxBus
|
||||
import io.reactivex.Single
|
||||
|
||||
class SuperuserViewModel(
|
||||
private val rxBus: RxBus,
|
||||
private val db: PolicyDao,
|
||||
private val packageManager: PackageManager
|
||||
private val packageManager: PackageManager,
|
||||
private val resources: Resources
|
||||
) : CompatViewModel() {
|
||||
|
||||
val items = diffListOf<PolicyRvItem>()
|
||||
@ -22,6 +36,17 @@ class SuperuserViewModel(
|
||||
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()
|
||||
.flattenAsFlowable { it }
|
||||
.parallel()
|
||||
@ -39,12 +64,77 @@ class SuperuserViewModel(
|
||||
.applyViewModel(this)
|
||||
.subscribeK { items.update(it.first, it.second) }
|
||||
|
||||
// ---
|
||||
|
||||
fun hidePressed() = Navigation.hide().publish()
|
||||
|
||||
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(
|
||||
|
@ -10,10 +10,8 @@ import android.view.LayoutInflater
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.databinding.ViewDataBinding
|
||||
import com.topjohnwu.magisk.BR
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
|
||||
import com.topjohnwu.magisk.utils.KObservableField
|
||||
|
||||
@ -21,12 +19,11 @@ class MagiskDialog @JvmOverloads constructor(
|
||||
context: Context, theme: Int = 0
|
||||
) : AlertDialog(context, theme) {
|
||||
|
||||
private val binding: DialogMagiskBaseBinding
|
||||
private val binding: DialogMagiskBaseBinding =
|
||||
DialogMagiskBaseBinding.inflate(LayoutInflater.from(context))
|
||||
private val data = Data()
|
||||
|
||||
init {
|
||||
val layoutInflater = LayoutInflater.from(context)
|
||||
binding = DataBindingUtil.inflate(layoutInflater, R.layout.dialog_magisk_base, null, false)
|
||||
binding.setVariable(BR.data, data)
|
||||
super.setView(binding.root)
|
||||
}
|
||||
@ -106,8 +103,8 @@ class MagiskDialog @JvmOverloads constructor(
|
||||
fun applyTitle(title: CharSequence) =
|
||||
apply { data.title.value = title }
|
||||
|
||||
fun applyMessage(@StringRes stringRes: Int) =
|
||||
apply { data.message.value = context.getString(stringRes) }
|
||||
fun applyMessage(@StringRes stringRes: Int, vararg args: Any) =
|
||||
apply { data.message.value = context.getString(stringRes, *args) }
|
||||
|
||||
fun applyMessage(message: CharSequence) =
|
||||
apply { data.message.value = message }
|
||||
|
Loading…
Reference in New Issue
Block a user