Commit d7a26dbf authored by topjohnwu's avatar topjohnwu

Tidy up ViewEvents

parent a86d5b3e
...@@ -8,6 +8,8 @@ import androidx.navigation.NavDirections ...@@ -8,6 +8,8 @@ import androidx.navigation.NavDirections
import com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.MainDirections
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.ui.base.ViewEvent
class InstallExternalModuleEvent : ViewEvent(), ActivityExecutor { class InstallExternalModuleEvent : ViewEvent(), ActivityExecutor {
......
...@@ -7,6 +7,8 @@ import androidx.annotation.AttrRes ...@@ -7,6 +7,8 @@ import androidx.annotation.AttrRes
import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri import androidx.core.net.toUri
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.ui.base.ContextExecutor
import com.topjohnwu.magisk.ui.base.ViewEvent
data class OpenInappLinkEvent( data class OpenInappLinkEvent(
private val link: String private val link: String
...@@ -28,4 +30,4 @@ data class OpenInappLinkEvent( ...@@ -28,4 +30,4 @@ data class OpenInappLinkEvent(
resolveRefs: Boolean = true resolveRefs: Boolean = true
) = TypedValue().also { resolveAttribute(attribute, it, resolveRefs) } ) = TypedValue().also { resolveAttribute(attribute, it, resolveRefs) }
} }
\ No newline at end of file
...@@ -5,7 +5,9 @@ import androidx.annotation.StringRes ...@@ -5,7 +5,9 @@ import androidx.annotation.StringRes
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.ktx.snackbar import com.topjohnwu.magisk.ktx.snackbar
import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.ui.base.BaseUIActivity import com.topjohnwu.magisk.ui.base.BaseUIActivity
import com.topjohnwu.magisk.ui.base.ViewEvent
class SnackbarEvent private constructor( class SnackbarEvent private constructor(
@StringRes private val messageRes: Int, @StringRes private val messageRes: Int,
......
package com.topjohnwu.magisk.model.events
import androidx.lifecycle.Observer
/**
* Observer for [ViewEvent]s, which automatically checks if event was handled
*/
class ViewEventObserver(private val onEventUnhandled: (ViewEvent) -> Unit) : Observer<ViewEvent> {
override fun onChanged(event: ViewEvent?) {
event?.let {
if (!it.handled) {
it.handled = true
onEventUnhandled(it)
}
}
}
}
\ No newline at end of file
...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.events ...@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.events
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Const
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
...@@ -12,12 +11,19 @@ import com.topjohnwu.magisk.core.utils.SafetyNetHelper ...@@ -12,12 +11,19 @@ import com.topjohnwu.magisk.core.utils.SafetyNetHelper
import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.data.network.GithubRawServices
import com.topjohnwu.magisk.ktx.DynamicClassLoader import com.topjohnwu.magisk.ktx.DynamicClassLoader
import com.topjohnwu.magisk.ktx.writeTo import com.topjohnwu.magisk.ktx.writeTo
import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.ui.base.ContextExecutor
import com.topjohnwu.magisk.ui.base.ViewEvent
import com.topjohnwu.magisk.ui.base.ViewEventWithScope
import com.topjohnwu.magisk.ui.safetynet.SafetyNetResult import com.topjohnwu.magisk.ui.safetynet.SafetyNetResult
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import dalvik.system.DexFile import dalvik.system.DexFile
import kotlinx.coroutines.* import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject import org.json.JSONObject
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.inject import org.koin.core.inject
...@@ -26,35 +32,9 @@ import java.io.File ...@@ -26,35 +32,9 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.lang.reflect.InvocationHandler import java.lang.reflect.InvocationHandler
/**
* Class for passing events from ViewModels to Activities/Fragments
* Variable [handled] used so each event is handled only once
* (see https://medium.com/google-developers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150)
* Use [ViewEventObserver] for observing these events
*/
abstract class ViewEvent {
var handled = false
}
abstract class ViewEventsWithScope: ViewEvent() {
lateinit var scope: CoroutineScope
}
interface ContextExecutor {
operator fun invoke(context: Context)
}
interface ActivityExecutor {
operator fun invoke(activity: BaseActivity)
}
interface FragmentExecutor {
operator fun invoke(fragment: Fragment)
}
class CheckSafetyNetEvent( class CheckSafetyNetEvent(
private val callback: (SafetyNetResult) -> Unit = {} private val callback: (SafetyNetResult) -> Unit = {}
) : ViewEventsWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback { ) : ViewEventWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
private val svc by inject<GithubRawServices>() private val svc by inject<GithubRawServices>()
...@@ -161,7 +141,7 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi ...@@ -161,7 +141,7 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi
override fun invoke(activity: BaseActivity) = action(activity) override fun invoke(activity: BaseActivity) = action(activity)
} }
class OpenChangelogEvent(val item: Repo) : ViewEventsWithScope(), ContextExecutor { class OpenChangelogEvent(val item: Repo) : ViewEventWithScope(), ContextExecutor {
override fun invoke(context: Context) { override fun invoke(context: Context) {
scope.launch { scope.launch {
MarkDownWindow.show(context, null, item::readme) MarkDownWindow.show(context, null, item::readme)
......
...@@ -2,8 +2,8 @@ package com.topjohnwu.magisk.model.events.dialog ...@@ -2,8 +2,8 @@ package com.topjohnwu.magisk.model.events.dialog
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.core.utils.BiometricHelper import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.model.events.ActivityExecutor import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ui.base.ViewEvent
class BiometricDialog( class BiometricDialog(
builder: Builder.() -> Unit builder: Builder.() -> Unit
......
...@@ -5,7 +5,7 @@ import androidx.appcompat.app.AppCompatDelegate ...@@ -5,7 +5,7 @@ import androidx.appcompat.app.AppCompatDelegate
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.ActivityExecutor import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
......
package com.topjohnwu.magisk.model.events.dialog package com.topjohnwu.magisk.model.events.dialog
import android.content.Context import android.content.Context
import com.topjohnwu.magisk.model.events.ContextExecutor import com.topjohnwu.magisk.ui.base.ContextExecutor
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ui.base.ViewEvent
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
abstract class DialogEvent : ViewEvent(), ContextExecutor { abstract class DialogEvent : ViewEvent(), ContextExecutor {
...@@ -17,4 +17,4 @@ abstract class DialogEvent : ViewEvent(), ContextExecutor { ...@@ -17,4 +17,4 @@ abstract class DialogEvent : ViewEvent(), ContextExecutor {
} }
typealias GenericDialogListener = () -> Unit typealias GenericDialogListener = () -> Unit
\ No newline at end of file
...@@ -2,9 +2,9 @@ package com.topjohnwu.magisk.model.navigation ...@@ -2,9 +2,9 @@ package com.topjohnwu.magisk.model.navigation
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.ActivityExecutor import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity import com.topjohnwu.magisk.ui.base.BaseUIActivity
import com.topjohnwu.magisk.ui.base.ViewEvent
class NavigationWrapper( class NavigationWrapper(
private val directions: NavDirections private val directions: NavDirections
...@@ -15,4 +15,4 @@ class NavigationWrapper( ...@@ -15,4 +15,4 @@ class NavigationWrapper(
directions.navigate() directions.navigate()
} }
} }
} }
\ No newline at end of file
...@@ -17,9 +17,6 @@ import com.topjohnwu.magisk.BR ...@@ -17,9 +17,6 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.ContextExecutor
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.theme.Theme import com.topjohnwu.magisk.ui.theme.Theme
abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
...@@ -94,9 +91,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> : ...@@ -94,9 +91,10 @@ abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event) return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event)
} }
override fun onEventDispatched(event: ViewEvent) { override fun onEventDispatched(event: ViewEvent) = when(event) {
(event as? ContextExecutor)?.invoke(this) is ContextExecutor -> event(this)
(event as? ActivityExecutor)?.invoke(this) is ActivityExecutor -> event(this)
else -> Unit
} }
override fun onBackPressed() { override fun onBackPressed() {
......
...@@ -5,7 +5,6 @@ import androidx.core.graphics.Insets ...@@ -5,7 +5,6 @@ import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import com.topjohnwu.magisk.model.events.ViewEvent
interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner { interface BaseUIComponent<VM : BaseViewModel>: LifecycleOwner {
......
...@@ -13,10 +13,6 @@ import androidx.fragment.app.Fragment ...@@ -13,10 +13,6 @@ import androidx.fragment.app.Fragment
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.ContextExecutor
import com.topjohnwu.magisk.model.events.FragmentExecutor
import com.topjohnwu.magisk.model.events.ViewEvent
abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> : abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
Fragment(), BaseUIComponent<VM> { Fragment(), BaseUIComponent<VM> {
...@@ -47,10 +43,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> : ...@@ -47,10 +43,11 @@ abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
return binding.root return binding.root
} }
override fun onEventDispatched(event: ViewEvent) { override fun onEventDispatched(event: ViewEvent) = when(event) {
(event as? ContextExecutor)?.invoke(requireContext()) is ContextExecutor -> event(requireContext())
(event as? FragmentExecutor)?.invoke(this) is ActivityExecutor -> event(activity)
(event as? ActivityExecutor)?.invoke(activity) is FragmentExecutor -> event(this)
else -> Unit
} }
open fun onKeyEvent(event: KeyEvent): Boolean { open fun onKeyEvent(event: KeyEvent): Boolean {
......
...@@ -15,7 +15,10 @@ import com.topjohnwu.magisk.BR ...@@ -15,7 +15,10 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Info import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.base.BaseActivity
import com.topjohnwu.magisk.model.events.* import com.topjohnwu.magisk.model.events.BackPressEvent
import com.topjohnwu.magisk.model.events.PermissionEvent
import com.topjohnwu.magisk.model.events.SnackbarEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.navigation.NavigationWrapper import com.topjohnwu.magisk.model.navigation.NavigationWrapper
import com.topjohnwu.magisk.utils.ObservableHost import com.topjohnwu.magisk.utils.ObservableHost
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
...@@ -102,7 +105,7 @@ abstract class BaseViewModel( ...@@ -102,7 +105,7 @@ abstract class BaseViewModel(
_viewEvents.postValue(this) _viewEvents.postValue(this)
} }
fun <Event : ViewEventsWithScope> Event.publish() { fun <Event : ViewEventWithScope> Event.publish() {
scope = viewModelScope scope = viewModelScope
_viewEvents.postValue(this) _viewEvents.postValue(this)
} }
......
package com.topjohnwu.magisk.ui.base
import android.content.Context
import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.core.base.BaseActivity
import kotlinx.coroutines.CoroutineScope
/**
* Class for passing events from ViewModels to Activities/Fragments
* (see https://medium.com/google-developers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150)
*/
abstract class ViewEvent
abstract class ViewEventWithScope: ViewEvent() {
lateinit var scope: CoroutineScope
}
interface ContextExecutor {
operator fun invoke(context: Context)
}
interface ActivityExecutor {
operator fun invoke(activity: BaseActivity)
}
interface FragmentExecutor {
operator fun invoke(fragment: Fragment)
}
...@@ -17,13 +17,13 @@ import com.topjohnwu.magisk.ktx.packageName ...@@ -17,13 +17,13 @@ import com.topjohnwu.magisk.ktx.packageName
import com.topjohnwu.magisk.ktx.res import com.topjohnwu.magisk.ktx.res
import com.topjohnwu.magisk.model.entity.IconLink import com.topjohnwu.magisk.model.entity.IconLink
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Manager import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Manager
import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.OpenInappLinkEvent import com.topjohnwu.magisk.model.events.OpenInappLinkEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.model.events.dialog.EnvFixDialog import com.topjohnwu.magisk.model.events.dialog.EnvFixDialog
import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog
import com.topjohnwu.magisk.model.events.dialog.UninstallDialog import com.topjohnwu.magisk.model.events.dialog.UninstallDialog
import com.topjohnwu.magisk.ui.base.ActivityExecutor
import com.topjohnwu.magisk.ui.base.BaseViewModel import com.topjohnwu.magisk.ui.base.BaseViewModel
import com.topjohnwu.magisk.ui.base.ViewEvent
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.set import com.topjohnwu.magisk.utils.set
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
......
...@@ -12,10 +12,10 @@ import com.topjohnwu.magisk.R ...@@ -12,10 +12,10 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding
import com.topjohnwu.magisk.ktx.hideKeyboard import com.topjohnwu.magisk.ktx.hideKeyboard
import com.topjohnwu.magisk.model.events.InstallExternalModuleEvent import com.topjohnwu.magisk.model.events.InstallExternalModuleEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.MainActivity import com.topjohnwu.magisk.ui.MainActivity
import com.topjohnwu.magisk.ui.base.BaseUIFragment import com.topjohnwu.magisk.ui.base.BaseUIFragment
import com.topjohnwu.magisk.ui.base.ReselectionTarget import com.topjohnwu.magisk.ui.base.ReselectionTarget
import com.topjohnwu.magisk.ui.base.ViewEvent
import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener import com.topjohnwu.magisk.utils.EndlessRecyclerScrollListener
import com.topjohnwu.magisk.utils.MotionRevealHelper import com.topjohnwu.magisk.utils.MotionRevealHelper
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
......
...@@ -4,7 +4,7 @@ import androidx.recyclerview.widget.GridLayoutManager ...@@ -4,7 +4,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.ui.base.ViewEvent
class EndlessRecyclerScrollListener( class EndlessRecyclerScrollListener(
private val layoutManager: RecyclerView.LayoutManager, private val layoutManager: RecyclerView.LayoutManager,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment