Reorganize code

This commit is contained in:
topjohnwu 2023-04-03 17:46:49 -07:00
parent 620fd7d124
commit 5117dc1a31
25 changed files with 106 additions and 120 deletions

View File

@ -7,11 +7,12 @@ import androidx.databinding.PropertyChangeRegistry
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableHost
import com.topjohnwu.magisk.events.BackPressEvent
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.events.DialogEvent
import com.topjohnwu.magisk.events.NavigationEvent
import com.topjohnwu.magisk.events.PermissionEvent
import com.topjohnwu.magisk.events.SnackbarEvent
@ -65,13 +66,12 @@ abstract class BaseViewModel : ViewModel(), ObservableHost {
fun back() = BackPressEvent().publish()
fun <Event : ViewEvent> Event.publish() {
fun ViewEvent.publish() {
_viewEvents.postValue(this)
}
fun <Event : ViewEventWithScope> Event.publish() {
scope = viewModelScope
_viewEvents.postValue(this)
fun DialogBuilder.show() {
DialogEvent(this).publish()
}
fun NavDirections.navigate(pop: Boolean = false) {

View File

@ -1,7 +1,6 @@
package com.topjohnwu.magisk.arch
import android.content.Context
import kotlinx.coroutines.CoroutineScope
/**
* Class for passing events from ViewModels to Activities/Fragments
@ -9,10 +8,6 @@ import kotlinx.coroutines.CoroutineScope
*/
abstract class ViewEvent
abstract class ViewEventWithScope: ViewEvent() {
lateinit var scope: CoroutineScope
}
interface ContextExecutor {
operator fun invoke(context: Context)
}

View File

@ -1,13 +1,14 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import android.app.Activity
import androidx.appcompat.app.AppCompatDelegate
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.UIActivity
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.view.MagiskDialog
class DarkThemeDialog : DialogEvent() {
class DarkThemeDialog : DialogBuilder {
override fun build(dialog: MagiskDialog) {
val activity = dialog.ownerActivity!!

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.BuildConfig
@ -6,11 +6,12 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.ui.home.HomeViewModel
import com.topjohnwu.magisk.view.MagiskDialog
import kotlinx.coroutines.launch
class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : DialogEvent() {
class EnvFixDialog(private val vm: HomeViewModel, private val code: Int) : DialogBuilder {
override fun build(dialog: MagiskDialog) {
dialog.apply {

View File

@ -1,17 +1,18 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import android.net.Uri
import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.utils.MediaStoreUtils.displayName
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.ui.module.ModuleViewModel
import com.topjohnwu.magisk.view.MagiskDialog
class ConfirmInstallLocalModuleDialog(
class LocalModuleInstallDialog(
private val viewModel: ModuleViewModel,
private val uri: Uri
) : DialogEvent() {
) : DialogBuilder {
override fun build(dialog: MagiskDialog) {
dialog.apply {
setTitle(R.string.confirm_install_title)

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import android.view.LayoutInflater
import android.widget.TextView
@ -6,6 +6,7 @@ import androidx.annotation.CallSuper
import androidx.lifecycle.lifecycleScope
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.view.MagiskDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -13,7 +14,7 @@ import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.IOException
abstract class MarkDownDialog : DialogEvent() {
abstract class MarkDownDialog : DialogBuilder {
abstract suspend fun getMarkdownText(): String

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.di.ServiceLocator
@ -8,7 +8,7 @@ import com.topjohnwu.magisk.core.download.Subject
import com.topjohnwu.magisk.core.model.module.OnlineModule
import com.topjohnwu.magisk.view.MagiskDialog
class ModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog() {
class OnlineModuleInstallDialog(private val item: OnlineModule) : MarkDownDialog() {
private val svc get() = ServiceLocator.networkService

View File

@ -1,9 +1,10 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.view.MagiskDialog
class SecondSlotWarningDialog : DialogEvent() {
class SecondSlotWarningDialog : DialogBuilder {
override fun build(dialog: MagiskDialog) {
dialog.apply {

View File

@ -0,0 +1,25 @@
package com.topjohnwu.magisk.dialog
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.view.MagiskDialog
class SuperuserRevokeDialog(
private val appName: String,
private val onSuccess: () -> Unit
) : DialogBuilder {
override fun build(dialog: MagiskDialog) {
dialog.apply {
setTitle(R.string.su_revoke_title)
setMessage(R.string.su_revoke_msg, appName)
setButton(MagiskDialog.ButtonType.POSITIVE) {
text = android.R.string.ok
onClick { onSuccess() }
}
setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = android.R.string.cancel
}
}
}
}

View File

@ -1,16 +1,17 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.dialog
import android.app.ProgressDialog
import android.content.Context
import android.widget.Toast
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.NavigationActivity
import com.topjohnwu.magisk.events.DialogBuilder
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.superuser.Shell
class UninstallDialog : DialogEvent() {
class UninstallDialog : DialogBuilder {
override fun build(dialog: MagiskDialog) {
dialog.apply {

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events.dialog
package com.topjohnwu.magisk.events
import com.topjohnwu.magisk.arch.ActivityExecutor
import com.topjohnwu.magisk.arch.UIActivity
@ -9,8 +9,8 @@ class BiometricEvent(
builder: Builder.() -> Unit
) : ViewEvent(), ActivityExecutor {
private var listenerOnFailure: GenericDialogListener = {}
private var listenerOnSuccess: GenericDialogListener = {}
private var listenerOnFailure: () -> Unit = {}
private var listenerOnSuccess: () -> Unit = {}
init {
builder(Builder())
@ -26,11 +26,11 @@ class BiometricEvent(
inner class Builder internal constructor() {
fun onFailure(listener: GenericDialogListener) {
fun onFailure(listener: () -> Unit) {
listenerOnFailure = listener
}
fun onSuccess(listener: GenericDialogListener) {
fun onSuccess(listener: () -> Unit) {
listenerOnSuccess = listener
}
}

View File

@ -5,10 +5,15 @@ import android.view.View
import androidx.annotation.StringRes
import androidx.navigation.NavDirections
import com.google.android.material.snackbar.Snackbar
import com.topjohnwu.magisk.arch.*
import com.topjohnwu.magisk.arch.ActivityExecutor
import com.topjohnwu.magisk.arch.ContextExecutor
import com.topjohnwu.magisk.arch.NavigationActivity
import com.topjohnwu.magisk.arch.UIActivity
import com.topjohnwu.magisk.arch.ViewEvent
import com.topjohnwu.magisk.core.base.ContentResultCallback
import com.topjohnwu.magisk.utils.TextHolder
import com.topjohnwu.magisk.utils.asText
import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.Shortcuts
class PermissionEvent(
@ -95,3 +100,15 @@ class SnackbarEvent(
activity.showSnackbar(msg.getText(activity.resources), length, builder)
}
}
class DialogEvent(
private val builder: DialogBuilder
) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: UIActivity<*>) {
MagiskDialog(activity).apply(builder::build).show()
}
}
interface DialogBuilder {
fun build(dialog: MagiskDialog)
}

View File

@ -1,18 +0,0 @@
package com.topjohnwu.magisk.events.dialog
import com.topjohnwu.magisk.arch.ActivityExecutor
import com.topjohnwu.magisk.arch.UIActivity
import com.topjohnwu.magisk.arch.ViewEvent
import com.topjohnwu.magisk.view.MagiskDialog
abstract class DialogEvent : ViewEvent(), ActivityExecutor {
override fun invoke(activity: UIActivity<*>) {
MagiskDialog(activity).apply(this::build).show()
}
abstract fun build(dialog: MagiskDialog)
}
typealias GenericDialogListener = () -> Unit

View File

@ -1,35 +0,0 @@
package com.topjohnwu.magisk.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.apply {
setTitle(R.string.su_revoke_title)
setMessage(R.string.su_revoke_msg, callbacks.appName)
setButton(MagiskDialog.ButtonType.POSITIVE) {
text = android.R.string.ok
onClick { callbacks.listenerOnSuccess() }
}
setButton(MagiskDialog.ButtonType.NEGATIVE) {
text = android.R.string.cancel
}
}
}
inner class Builder internal constructor() {
var appName: String = ""
internal var listenerOnSuccess: GenericDialogListener = {}
fun onSuccess(listener: GenericDialogListener) {
listenerOnSuccess = listener
}
}
}

View File

@ -183,12 +183,8 @@ fun ApplicationInfo.getLabel(pm: PackageManager): String {
fun Context.unwrap(): Context {
var context = this
while (true) {
if (context is ContextWrapper)
context = context.baseContext
else
break
}
while (context is ContextWrapper)
context = context.baseContext
return context
}

View File

@ -16,7 +16,6 @@ import com.topjohnwu.magisk.arch.viewModel
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.databinding.FragmentHomeMd2Binding
import com.topjohnwu.magisk.events.RebootEvent
class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
@ -69,7 +68,7 @@ class HomeFragment : BaseFragment<FragmentHomeMd2Binding>(), MenuProvider {
when (item.itemId) {
R.id.action_settings ->
HomeFragmentDirections.actionHomeFragmentToSettingsFragment().navigate()
R.id.action_reboot -> activity?.let { RebootEvent.inflateMenu(it).show() }
R.id.action_reboot -> activity?.let { RebootMenu.inflate(it).show() }
else -> return super.onOptionsItemSelected(item)
}
return true

View File

@ -6,7 +6,11 @@ import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.BuildConfig
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.*
import com.topjohnwu.magisk.arch.ActivityExecutor
import com.topjohnwu.magisk.arch.AsyncLoadViewModel
import com.topjohnwu.magisk.arch.ContextExecutor
import com.topjohnwu.magisk.arch.UIActivity
import com.topjohnwu.magisk.arch.ViewEvent
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.download.Subject
@ -14,10 +18,10 @@ import com.topjohnwu.magisk.core.download.Subject.App
import com.topjohnwu.magisk.core.repository.NetworkService
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.dialog.EnvFixDialog
import com.topjohnwu.magisk.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.dialog.UninstallDialog
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.EnvFixDialog
import com.topjohnwu.magisk.events.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.events.dialog.UninstallDialog
import com.topjohnwu.magisk.ktx.await
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.utils.asText
@ -111,14 +115,14 @@ class HomeViewModel(
override fun invoke(context: Context) = Utils.openLink(context, link.toUri())
}.publish()
fun onDeletePressed() = UninstallDialog().publish()
fun onDeletePressed() = UninstallDialog().show()
fun onManagerPressed() = when (appState) {
State.LOADING -> SnackbarEvent(R.string.loading).publish()
State.INVALID -> SnackbarEvent(R.string.no_connection).publish()
else -> withExternalRW {
withInstallPermission {
ManagerInstallDialog().publish()
ManagerInstallDialog().show()
}
}
}
@ -137,7 +141,7 @@ class HomeViewModel(
val cmd = "env_check ${Info.env.versionString} ${Info.env.versionCode}"
val code = Shell.cmd(cmd).await().code
if (code != 0) {
EnvFixDialog(this, code).publish()
EnvFixDialog(this, code).show()
}
checkedEnv = true
}

View File

@ -1,4 +1,4 @@
package com.topjohnwu.magisk.events
package com.topjohnwu.magisk.ui.home
import android.os.Build
import android.os.PowerManager
@ -10,7 +10,7 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.ktx.reboot as systemReboot
object RebootEvent {
object RebootMenu {
private fun reboot(item: MenuItem): Boolean {
when (item.itemId) {
@ -25,14 +25,14 @@ object RebootEvent {
return true
}
fun inflateMenu(activity: BaseActivity): PopupMenu {
fun inflate(activity: BaseActivity): PopupMenu {
val themeWrapper = ContextThemeWrapper(activity, R.style.Foundation_PopupMenu)
val menu = PopupMenu(themeWrapper, activity.findViewById(R.id.action_reboot))
activity.menuInflater.inflate(R.menu.menu_reboot, menu.menu)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
activity.getSystemService<PowerManager>()?.isRebootingUserspaceSupported == true)
menu.menu.findItem(R.id.action_reboot_userspace).isVisible = true
menu.setOnMenuItemClickListener(::reboot)
menu.setOnMenuItemClickListener(RebootMenu::reboot)
return menu
}

View File

@ -22,8 +22,8 @@ import com.topjohnwu.magisk.core.di.AppContext
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.repository.NetworkService
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.events.GetContentEvent
import com.topjohnwu.magisk.events.dialog.SecondSlotWarningDialog
import com.topjohnwu.magisk.ui.flash.FlashFragment
import com.topjohnwu.magisk.utils.Utils
import kotlinx.coroutines.Dispatchers
@ -57,7 +57,7 @@ class InstallViewModel(
GetContentEvent("*/*", UriCallback()).publish()
}
R.id.method_inactive_slot -> {
SecondSlotWarningDialog().publish()
SecondSlotWarningDialog().show()
}
}
}

View File

@ -15,10 +15,10 @@ import com.topjohnwu.magisk.databinding.MergeObservableList
import com.topjohnwu.magisk.databinding.RvItem
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.dialog.LocalModuleInstallDialog
import com.topjohnwu.magisk.dialog.OnlineModuleInstallDialog
import com.topjohnwu.magisk.events.GetContentEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.ConfirmInstallLocalModuleDialog
import com.topjohnwu.magisk.events.dialog.ModuleInstallDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
@ -75,7 +75,7 @@ class ModuleViewModel : AsyncLoadViewModel() {
fun downloadPressed(item: OnlineModule?) =
if (item != null && Info.isConnected.value == true) {
withExternalRW { ModuleInstallDialog(item).publish() }
withExternalRW { OnlineModuleInstallDialog(item).show() }
} else {
SnackbarEvent(R.string.no_connection).publish()
}
@ -85,7 +85,7 @@ class ModuleViewModel : AsyncLoadViewModel() {
}
fun requestInstallLocalModule(uri: Uri) {
ConfirmInstallLocalModuleDialog(this, uri).publish()
LocalModuleInstallDialog(this, uri).show()
}
@Parcelize

View File

@ -16,8 +16,8 @@ import com.topjohnwu.magisk.core.isRunningAsStub
import com.topjohnwu.magisk.core.tasks.HideAPK
import com.topjohnwu.magisk.databinding.bindExtra
import com.topjohnwu.magisk.events.AddHomeIconEvent
import com.topjohnwu.magisk.events.BiometricEvent
import com.topjohnwu.magisk.events.SnackbarEvent
import com.topjohnwu.magisk.events.dialog.BiometricEvent
import com.topjohnwu.magisk.ktx.activity
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.superuser.Shell

View File

@ -16,9 +16,9 @@ import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.dialog.SuperuserRevokeDialog
import com.topjohnwu.magisk.events.BiometricEvent
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
@ -116,10 +116,7 @@ class SuperuserViewModel(
onSuccess { updateState() }
}.publish()
} else {
SuperuserRevokeDialog {
appName = item.title
onSuccess { updateState() }
}.publish()
SuperuserRevokeDialog(item.title) { updateState() }.show()
}
}

View File

@ -27,9 +27,9 @@ import com.topjohnwu.magisk.core.model.su.SuPolicy.Companion.DENY
import com.topjohnwu.magisk.core.su.SuRequestHandler
import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.databinding.set
import com.topjohnwu.magisk.events.BiometricEvent
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

View File

@ -2,8 +2,8 @@ package com.topjohnwu.magisk.ui.theme
import com.topjohnwu.magisk.arch.BaseViewModel
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.dialog.DarkThemeDialog
import com.topjohnwu.magisk.events.RecreateEvent
import com.topjohnwu.magisk.events.dialog.DarkThemeDialog
import com.topjohnwu.magisk.view.TappableHeadlineItem
class ThemeViewModel : BaseViewModel(), TappableHeadlineItem.Listener {
@ -11,7 +11,7 @@ class ThemeViewModel : BaseViewModel(), TappableHeadlineItem.Listener {
val themeHeadline = TappableHeadlineItem.ThemeMode
override fun onItemPressed(item: TappableHeadlineItem) = when (item) {
is TappableHeadlineItem.ThemeMode -> DarkThemeDialog().publish()
is TappableHeadlineItem.ThemeMode -> DarkThemeDialog().show()
}
fun saveTheme(theme: Theme) {