diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt index 2dd84ef79..8172b0fa8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/InstallExternalModuleEvent.kt @@ -8,6 +8,8 @@ import androidx.navigation.NavDirections import com.topjohnwu.magisk.MainDirections import com.topjohnwu.magisk.core.Const 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 { diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/OpenInappLinkEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/OpenInappLinkEvent.kt index 6c02e267f..b23f49615 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/OpenInappLinkEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/OpenInappLinkEvent.kt @@ -7,6 +7,8 @@ import androidx.annotation.AttrRes import androidx.browser.customtabs.CustomTabsIntent import androidx.core.net.toUri import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.ui.base.ContextExecutor +import com.topjohnwu.magisk.ui.base.ViewEvent data class OpenInappLinkEvent( private val link: String @@ -28,4 +30,4 @@ data class OpenInappLinkEvent( resolveRefs: Boolean = true ) = TypedValue().also { resolveAttribute(attribute, it, resolveRefs) } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt index c24a95d3a..910f1cbf5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/SnackbarEvent.kt @@ -5,7 +5,9 @@ import androidx.annotation.StringRes import com.google.android.material.snackbar.Snackbar import com.topjohnwu.magisk.core.base.BaseActivity 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.ViewEvent class SnackbarEvent private constructor( @StringRes private val messageRes: Int, diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt deleted file mode 100644 index 7f98f966b..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEventObserver.kt +++ /dev/null @@ -1,17 +0,0 @@ -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 { - override fun onChanged(event: ViewEvent?) { - event?.let { - if (!it.handled) { - it.handled = true - onEventUnhandled(it) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt index 43cc88231..2f6dcca86 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/ViewEvents.kt @@ -3,7 +3,6 @@ package com.topjohnwu.magisk.model.events import android.app.Activity import android.content.Context import android.content.Intent -import androidx.fragment.app.Fragment import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.base.BaseActivity @@ -12,12 +11,19 @@ import com.topjohnwu.magisk.core.utils.SafetyNetHelper import com.topjohnwu.magisk.data.network.GithubRawServices import com.topjohnwu.magisk.ktx.DynamicClassLoader 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.view.MagiskDialog import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.superuser.Shell 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.koin.core.KoinComponent import org.koin.core.inject @@ -26,35 +32,9 @@ import java.io.File import java.io.IOException 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( private val callback: (SafetyNetResult) -> Unit = {} -) : ViewEventsWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback { +) : ViewEventWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback { private val svc by inject() @@ -161,7 +141,7 @@ class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), Activi 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) { scope.launch { MarkDownWindow.show(context, null, item::readme) diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/BiometricDialog.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/BiometricDialog.kt index 839a5a21c..dd4f81cc3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/BiometricDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/BiometricDialog.kt @@ -2,8 +2,8 @@ package com.topjohnwu.magisk.model.events.dialog import com.topjohnwu.magisk.core.base.BaseActivity import com.topjohnwu.magisk.core.utils.BiometricHelper -import com.topjohnwu.magisk.model.events.ActivityExecutor -import com.topjohnwu.magisk.model.events.ViewEvent +import com.topjohnwu.magisk.ui.base.ActivityExecutor +import com.topjohnwu.magisk.ui.base.ViewEvent class BiometricDialog( builder: Builder.() -> Unit diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DarkThemeDialog.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DarkThemeDialog.kt index 4d864faea..6aca87d48 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DarkThemeDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DarkThemeDialog.kt @@ -5,7 +5,7 @@ import androidx.appcompat.app.AppCompatDelegate import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Config 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 java.lang.ref.WeakReference diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DialogEvent.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DialogEvent.kt index 60355c914..9146e8452 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DialogEvent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/DialogEvent.kt @@ -1,8 +1,8 @@ package com.topjohnwu.magisk.model.events.dialog import android.content.Context -import com.topjohnwu.magisk.model.events.ContextExecutor -import com.topjohnwu.magisk.model.events.ViewEvent +import com.topjohnwu.magisk.ui.base.ContextExecutor +import com.topjohnwu.magisk.ui.base.ViewEvent import com.topjohnwu.magisk.view.MagiskDialog abstract class DialogEvent : ViewEvent(), ContextExecutor { @@ -17,4 +17,4 @@ abstract class DialogEvent : ViewEvent(), ContextExecutor { } -typealias GenericDialogListener = () -> Unit \ No newline at end of file +typealias GenericDialogListener = () -> Unit diff --git a/app/src/main/java/com/topjohnwu/magisk/model/navigation/NavigationWrapper.kt b/app/src/main/java/com/topjohnwu/magisk/model/navigation/NavigationWrapper.kt index 300a1b0aa..f514b9275 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/navigation/NavigationWrapper.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/navigation/NavigationWrapper.kt @@ -2,9 +2,9 @@ package com.topjohnwu.magisk.model.navigation import androidx.navigation.NavDirections import com.topjohnwu.magisk.core.base.BaseActivity -import com.topjohnwu.magisk.model.events.ActivityExecutor -import com.topjohnwu.magisk.model.events.ViewEvent +import com.topjohnwu.magisk.ui.base.ActivityExecutor import com.topjohnwu.magisk.ui.base.BaseUIActivity +import com.topjohnwu.magisk.ui.base.ViewEvent class NavigationWrapper( private val directions: NavDirections @@ -15,4 +15,4 @@ class NavigationWrapper( directions.navigate() } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt index 2e17d5ddf..d6bef312b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIActivity.kt @@ -17,9 +17,6 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.base.BaseActivity 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 abstract class BaseUIActivity : @@ -94,9 +91,10 @@ abstract class BaseUIActivity : return currentFragment?.onKeyEvent(event) == true || super.dispatchKeyEvent(event) } - override fun onEventDispatched(event: ViewEvent) { - (event as? ContextExecutor)?.invoke(this) - (event as? ActivityExecutor)?.invoke(this) + override fun onEventDispatched(event: ViewEvent) = when(event) { + is ContextExecutor -> event(this) + is ActivityExecutor -> event(this) + else -> Unit } override fun onBackPressed() { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIComponent.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIComponent.kt index 0da2ad917..ba15ad186 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIComponent.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIComponent.kt @@ -5,7 +5,6 @@ import androidx.core.graphics.Insets import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.lifecycle.LifecycleOwner -import com.topjohnwu.magisk.model.events.ViewEvent interface BaseUIComponent: LifecycleOwner { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt index 23deb4bfd..6c4dcfd69 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseUIFragment.kt @@ -13,10 +13,6 @@ import androidx.fragment.app.Fragment import androidx.navigation.NavDirections import com.topjohnwu.magisk.BR 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 : Fragment(), BaseUIComponent { @@ -47,10 +43,11 @@ abstract class BaseUIFragment : return binding.root } - override fun onEventDispatched(event: ViewEvent) { - (event as? ContextExecutor)?.invoke(requireContext()) - (event as? FragmentExecutor)?.invoke(this) - (event as? ActivityExecutor)?.invoke(activity) + override fun onEventDispatched(event: ViewEvent) = when(event) { + is ContextExecutor -> event(requireContext()) + is ActivityExecutor -> event(activity) + is FragmentExecutor -> event(this) + else -> Unit } open fun onKeyEvent(event: KeyEvent): Boolean { diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt index 48fecb942..a11b5780f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/BaseViewModel.kt @@ -15,7 +15,10 @@ import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Info 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.utils.ObservableHost import com.topjohnwu.magisk.utils.set @@ -102,7 +105,7 @@ abstract class BaseViewModel( _viewEvents.postValue(this) } - fun Event.publish() { + fun Event.publish() { scope = viewModelScope _viewEvents.postValue(this) } diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/base/ViewEvent.kt b/app/src/main/java/com/topjohnwu/magisk/ui/base/ViewEvent.kt new file mode 100644 index 000000000..72cb2a4b1 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/ui/base/ViewEvent.kt @@ -0,0 +1,28 @@ +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) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt index 10df546a1..7278e7416 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/home/HomeViewModel.kt @@ -17,13 +17,13 @@ import com.topjohnwu.magisk.ktx.packageName import com.topjohnwu.magisk.ktx.res import com.topjohnwu.magisk.model.entity.IconLink 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.ViewEvent import com.topjohnwu.magisk.model.events.dialog.EnvFixDialog import com.topjohnwu.magisk.model.events.dialog.ManagerInstallDialog 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.ViewEvent import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.utils.set import com.topjohnwu.superuser.Shell diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt index aad0c5475..8140aec93 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/module/ModuleFragment.kt @@ -12,10 +12,10 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.FragmentModuleMd2Binding import com.topjohnwu.magisk.ktx.hideKeyboard 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.base.BaseUIFragment 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.MotionRevealHelper import org.koin.androidx.viewmodel.ext.android.viewModel diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/EndlessRecyclerScrollListener.kt b/app/src/main/java/com/topjohnwu/magisk/utils/EndlessRecyclerScrollListener.kt index 97e8e7f77..8577d0c8c 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/EndlessRecyclerScrollListener.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/EndlessRecyclerScrollListener.kt @@ -4,7 +4,7 @@ import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager -import com.topjohnwu.magisk.model.events.ViewEvent +import com.topjohnwu.magisk.ui.base.ViewEvent class EndlessRecyclerScrollListener( private val layoutManager: RecyclerView.LayoutManager,