You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Update UI for sharedUID support
This commit is contained in:
@@ -10,37 +10,49 @@ import com.topjohnwu.magisk.databinding.RvContainer
|
||||
import com.topjohnwu.magisk.databinding.set
|
||||
|
||||
class PolicyRvItem(
|
||||
private val viewModel: SuperuserViewModel,
|
||||
override val item: SuPolicy,
|
||||
val packageName: String,
|
||||
private val isSharedUid: Boolean,
|
||||
val icon: Drawable,
|
||||
val viewModel: SuperuserViewModel
|
||||
val appName: String
|
||||
) : ObservableDiffRvItem<PolicyRvItem>(), RvContainer<SuPolicy> {
|
||||
|
||||
override val layoutRes = R.layout.item_policy_md2
|
||||
|
||||
val title get() = if (isSharedUid) "[SharedUID] $appName" else appName
|
||||
|
||||
private inline fun <reified T> setImpl(new: T, old: T, setter: (T) -> Unit) {
|
||||
if (old != new) {
|
||||
setter(new)
|
||||
}
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var isExpanded = false
|
||||
set(value) = set(value, field, { field = it }, BR.expanded)
|
||||
|
||||
// This property binds with the UI state
|
||||
@get:Bindable
|
||||
var isEnabled
|
||||
get() = item.policy == SuPolicy.ALLOW
|
||||
set(value) {
|
||||
if (value != isEnabled)
|
||||
viewModel.togglePolicy(this, value)
|
||||
set(value) = setImpl(value, isEnabled) {
|
||||
viewModel.togglePolicy(this, value)
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var shouldNotify
|
||||
get() = item.notification
|
||||
set(value) = set(value, shouldNotify, { item.notification = it }, BR.shouldNotify) {
|
||||
viewModel.updatePolicy(item, isLogging = false)
|
||||
private set(value) = setImpl(value, shouldNotify) {
|
||||
item.notification = it
|
||||
viewModel.updateNotify(this)
|
||||
}
|
||||
|
||||
@get:Bindable
|
||||
var shouldLog
|
||||
get() = item.logging
|
||||
set(value) = set(value, shouldLog, { item.logging = it }, BR.shouldLog) {
|
||||
viewModel.updatePolicy(item, isLogging = true)
|
||||
private set(value) = setImpl(value, shouldLog) {
|
||||
item.logging = it
|
||||
viewModel.updateLogging(this)
|
||||
}
|
||||
|
||||
fun toggleExpand() {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package com.topjohnwu.magisk.ui.superuser
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES
|
||||
import androidx.databinding.ObservableArrayList
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.topjohnwu.magisk.BR
|
||||
@@ -12,9 +15,11 @@ import com.topjohnwu.magisk.core.utils.currentLocale
|
||||
import com.topjohnwu.magisk.databinding.AnyDiffRvItem
|
||||
import com.topjohnwu.magisk.databinding.diffListOf
|
||||
import com.topjohnwu.magisk.databinding.itemBindingOf
|
||||
import com.topjohnwu.magisk.di.AppContext
|
||||
import com.topjohnwu.magisk.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.events.dialog.SuperuserRevokeDialog
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import com.topjohnwu.magisk.utils.asText
|
||||
import com.topjohnwu.magisk.view.TextItem
|
||||
@@ -41,6 +46,7 @@ class SuperuserViewModel(
|
||||
|
||||
// ---
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
override fun refresh() = viewModelScope.launch {
|
||||
if (!Utils.showSuperUser()) {
|
||||
state = State.LOADING_FAILED
|
||||
@@ -49,11 +55,28 @@ class SuperuserViewModel(
|
||||
state = State.LOADING
|
||||
val (policies, diff) = withContext(Dispatchers.IO) {
|
||||
db.deleteOutdated()
|
||||
val policies = db.fetchAll().map {
|
||||
PolicyRvItem(it, it.icon, this@SuperuserViewModel)
|
||||
}.sortedWith(compareBy(
|
||||
{ it.item.appName.lowercase(currentLocale) },
|
||||
{ it.item.packageName }
|
||||
val policies = ArrayList<PolicyRvItem>()
|
||||
val pm = AppContext.packageManager
|
||||
for (policy in db.fetchAll()) {
|
||||
val pkgs = pm.getPackagesForUid(policy.uid) ?: continue
|
||||
policies.addAll(pkgs.mapNotNull { pkg ->
|
||||
try {
|
||||
val info = pm.getPackageInfo(pkg, MATCH_UNINSTALLED_PACKAGES)
|
||||
PolicyRvItem(
|
||||
this@SuperuserViewModel, policy,
|
||||
info.packageName,
|
||||
info.sharedUserId != null,
|
||||
info.applicationInfo.loadIcon(pm),
|
||||
info.applicationInfo.getLabel(pm)
|
||||
)
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
null
|
||||
}
|
||||
})
|
||||
}
|
||||
policies.sortWith(compareBy(
|
||||
{ it.appName.lowercase(currentLocale) },
|
||||
{ it.packageName }
|
||||
))
|
||||
policies to itemsPolicies.calculateDiff(policies)
|
||||
}
|
||||
@@ -82,40 +105,56 @@ class SuperuserViewModel(
|
||||
}.publish()
|
||||
} else {
|
||||
SuperuserRevokeDialog {
|
||||
appName = item.item.appName
|
||||
appName = item.title
|
||||
onSuccess { updateState() }
|
||||
}.publish()
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
fun updatePolicy(policy: SuPolicy, isLogging: Boolean) = viewModelScope.launch {
|
||||
db.update(policy)
|
||||
val res = when {
|
||||
isLogging -> when {
|
||||
policy.logging -> R.string.su_snack_log_on
|
||||
fun updateNotify(item: PolicyRvItem) {
|
||||
viewModelScope.launch {
|
||||
db.update(item.item)
|
||||
val res = when {
|
||||
item.item.logging -> R.string.su_snack_log_on
|
||||
else -> R.string.su_snack_log_off
|
||||
}
|
||||
else -> when {
|
||||
policy.notification -> R.string.su_snack_notif_on
|
||||
itemsPolicies.forEach {
|
||||
if (it.item.uid == item.item.uid) {
|
||||
it.notifyPropertyChanged(BR.shouldNotify)
|
||||
}
|
||||
}
|
||||
SnackbarEvent(res.asText(item.appName)).publish()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateLogging(item: PolicyRvItem) {
|
||||
viewModelScope.launch {
|
||||
db.update(item.item)
|
||||
val res = when {
|
||||
item.item.notification -> R.string.su_snack_notif_on
|
||||
else -> R.string.su_snack_notif_off
|
||||
}
|
||||
itemsPolicies.forEach {
|
||||
if (it.item.uid == item.item.uid) {
|
||||
it.notifyPropertyChanged(BR.shouldLog)
|
||||
}
|
||||
}
|
||||
SnackbarEvent(res.asText(item.appName)).publish()
|
||||
}
|
||||
SnackbarEvent(res.asText(policy.appName)).publish()
|
||||
}
|
||||
|
||||
fun togglePolicy(item: PolicyRvItem, enable: Boolean) {
|
||||
fun updateState() {
|
||||
val policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
|
||||
item.item.policy = policy
|
||||
item.notifyPropertyChanged(BR.enabled)
|
||||
|
||||
viewModelScope.launch {
|
||||
val res = if (enable) R.string.su_snack_grant else R.string.su_snack_deny
|
||||
item.item.policy = if (enable) SuPolicy.ALLOW else SuPolicy.DENY
|
||||
db.update(item.item)
|
||||
val res = if (item.item.policy == SuPolicy.ALLOW) R.string.su_snack_grant
|
||||
else R.string.su_snack_deny
|
||||
SnackbarEvent(res.asText(item.item.appName)).publish()
|
||||
itemsPolicies.forEach {
|
||||
if (it.item.uid == item.item.uid) {
|
||||
it.notifyPropertyChanged(BR.enabled)
|
||||
}
|
||||
}
|
||||
SnackbarEvent(res.asText(item.appName)).publish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.arch.BaseViewModel
|
||||
import com.topjohnwu.magisk.core.Config
|
||||
import com.topjohnwu.magisk.core.magiskdb.PolicyDao
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.ALLOW
|
||||
import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.DENY
|
||||
import com.topjohnwu.magisk.core.su.SuRequestHandler
|
||||
@@ -31,6 +30,7 @@ import com.topjohnwu.magisk.di.AppContext
|
||||
import com.topjohnwu.magisk.events.DieEvent
|
||||
import com.topjohnwu.magisk.events.ShowUIEvent
|
||||
import com.topjohnwu.magisk.events.dialog.BiometricEvent
|
||||
import com.topjohnwu.magisk.ktx.getLabel
|
||||
import com.topjohnwu.magisk.utils.TextHolder
|
||||
import com.topjohnwu.magisk.utils.Utils
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -100,17 +100,21 @@ class SuRequestViewModel(
|
||||
fun handleRequest(intent: Intent) {
|
||||
viewModelScope.launch {
|
||||
if (handler.start(intent))
|
||||
showDialog(handler.policy)
|
||||
showDialog()
|
||||
else
|
||||
DieEvent().publish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDialog(policy: SuPolicy) {
|
||||
icon = policy.icon
|
||||
title = policy.appName
|
||||
packageName = policy.packageName
|
||||
selectedItemPosition = timeoutPrefs.getInt(policy.packageName, 0)
|
||||
private fun showDialog() {
|
||||
val pm = handler.pm
|
||||
val info = handler.pkgInfo
|
||||
val prefix = if (info.sharedUserId == null) "" else "[SharedUID] "
|
||||
|
||||
icon = info.applicationInfo.loadIcon(pm)
|
||||
title = "$prefix${info.applicationInfo.getLabel(pm)}"
|
||||
packageName = info.packageName
|
||||
selectedItemPosition = timeoutPrefs.getInt(packageName, 0)
|
||||
|
||||
// Set timer
|
||||
val millis = SECONDS.toMillis(Config.suDefaultTimeout.toLong())
|
||||
@@ -124,7 +128,7 @@ class SuRequestViewModel(
|
||||
timer?.cancel()
|
||||
|
||||
val pos = selectedItemPosition
|
||||
timeoutPrefs.edit().putInt(handler.policy.packageName, pos).apply()
|
||||
timeoutPrefs.edit().putInt(handler.pkgInfo.packageName, pos).apply()
|
||||
|
||||
viewModelScope.launch {
|
||||
handler.respond(action, Config.Value.TIMEOUT_LIST[pos])
|
||||
|
||||
Reference in New Issue
Block a user