You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Redesign is now the new norm
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package com.topjohnwu.magisk.ui.superuser
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.graphics.Insets
|
||||
import com.topjohnwu.magisk.R
|
||||
import com.topjohnwu.magisk.databinding.FragmentSuperuserMd2Binding
|
||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||
import com.topjohnwu.magisk.ui.compat.CompatFragment
|
||||
import com.topjohnwu.magisk.utils.PinchZoomTouchListener
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
class SuperuserFragment : CompatFragment<SuperuserViewModel, FragmentSuperuserMd2Binding>() {
|
||||
|
||||
override val layoutRes = R.layout.fragment_superuser_md2
|
||||
override val viewModel by viewModel<SuperuserViewModel>()
|
||||
|
||||
override fun consumeSystemWindowInsets(insets: Insets) = insets
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
activity.title = resources.getString(R.string.section_superuser)
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
PinchZoomTouchListener.attachTo(binding.superuserList)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
PinchZoomTouchListener.clear(binding.superuserList)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_superuser_md2, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.action_log -> Navigation.log().dispatchOnSelf()
|
||||
else -> null
|
||||
}?.let { true } ?: super.onOptionsItemSelected(item)
|
||||
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.topjohnwu.magisk.ui.superuser
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import androidx.databinding.ObservableArrayList
|
||||
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.PolicyItem
|
||||
import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem
|
||||
import com.topjohnwu.magisk.model.entity.recycler.TextItem
|
||||
import com.topjohnwu.magisk.model.events.PolicyUpdateEvent
|
||||
import com.topjohnwu.magisk.model.events.SnackbarEvent
|
||||
import com.topjohnwu.magisk.model.events.dialog.BiometricDialog
|
||||
import com.topjohnwu.magisk.model.events.dialog.SuperuserRevokeDialog
|
||||
import com.topjohnwu.magisk.model.navigation.Navigation
|
||||
import com.topjohnwu.magisk.ui.compat.CompatViewModel
|
||||
import com.topjohnwu.magisk.ui.compat.adapterOf
|
||||
import com.topjohnwu.magisk.ui.compat.diffListOf
|
||||
import com.topjohnwu.magisk.ui.compat.itemBindingOf
|
||||
import com.topjohnwu.magisk.utils.BiometricHelper
|
||||
import com.topjohnwu.magisk.utils.currentLocale
|
||||
import io.reactivex.Single
|
||||
import me.tatarka.bindingcollectionadapter2.collections.MergeObservableList
|
||||
|
||||
class SuperuserViewModel(
|
||||
private val db: PolicyDao,
|
||||
private val packageManager: PackageManager,
|
||||
private val resources: Resources
|
||||
) : CompatViewModel(), TappableHeadlineItem.Listener {
|
||||
|
||||
private val itemNoData = TextItem(R.string.superuser_policy_none)
|
||||
|
||||
private val itemsPolicies = diffListOf<PolicyItem>()
|
||||
private val itemsHelpers = ObservableArrayList<TextItem>().also {
|
||||
it.add(itemNoData)
|
||||
}
|
||||
|
||||
val adapter = adapterOf<ComparableRvItem<*>>()
|
||||
val items = MergeObservableList<ComparableRvItem<*>>()
|
||||
.insertItem(TappableHeadlineItem.Hide)
|
||||
.insertItem(TappableHeadlineItem.Safetynet)
|
||||
.insertList(itemsHelpers)
|
||||
.insertList(itemsPolicies)
|
||||
val itemBinding = itemBindingOf<ComparableRvItem<*>> {
|
||||
it.bindExtra(BR.viewModel, this)
|
||||
it.bindExtra(BR.listener, this)
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
override fun refresh() = db.fetchAll()
|
||||
.flattenAsFlowable { it }
|
||||
.parallel()
|
||||
.map { PolicyItem(it, it.applicationInfo.loadIcon(packageManager)) }
|
||||
.sequential()
|
||||
.sorted { o1, o2 ->
|
||||
compareBy<PolicyItem>(
|
||||
{ it.item.appName.toLowerCase(currentLocale) },
|
||||
{ it.item.packageName }
|
||||
).compare(o1, o2)
|
||||
}
|
||||
.toList()
|
||||
.map { it to itemsPolicies.calculateDiff(it) }
|
||||
.applySchedulers()
|
||||
.applyViewModel(this)
|
||||
.subscribeK {
|
||||
itemsPolicies.update(it.first, it.second)
|
||||
if (itemsPolicies.isNotEmpty()) {
|
||||
itemsHelpers.remove(itemNoData)
|
||||
}
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
@Suppress("REDUNDANT_ELSE_IN_WHEN")
|
||||
override fun onItemPressed(item: TappableHeadlineItem) = when (item) {
|
||||
TappableHeadlineItem.Hide -> hidePressed()
|
||||
TappableHeadlineItem.Safetynet -> safetynetPressed()
|
||||
else -> Unit
|
||||
}
|
||||
|
||||
private fun safetynetPressed() = Navigation.safetynet().publish()
|
||||
private fun hidePressed() = Navigation.hide().publish()
|
||||
|
||||
fun deletePressed(item: PolicyItem) {
|
||||
fun updateState() = deletePolicy(item.item)
|
||||
.subscribeK {
|
||||
itemsPolicies.removeAll { it.genericItemSameAs(item) }
|
||||
if (itemsPolicies.isEmpty() && itemsHelpers.isEmpty()) {
|
||||
itemsHelpers.add(itemNoData)
|
||||
}
|
||||
}
|
||||
.add()
|
||||
|
||||
if (BiometricHelper.isEnabled) {
|
||||
BiometricDialog {
|
||||
onSuccess { updateState() }
|
||||
}.publish()
|
||||
} else {
|
||||
SuperuserRevokeDialog {
|
||||
appName = item.item.appName
|
||||
onSuccess { updateState() }
|
||||
}.publish()
|
||||
}
|
||||
}
|
||||
|
||||
//---
|
||||
|
||||
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()
|
||||
|
||||
fun togglePolicy(item: PolicyItem, 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 (BiometricHelper.isEnabled) {
|
||||
BiometricDialog {
|
||||
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))
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user