Commit a86d5b3e authored by topjohnwu's avatar topjohnwu

Remove unnecessary abstractions

parent b2bece9e
...@@ -66,7 +66,7 @@ abstract class BaseActivity : AppCompatActivity() { ...@@ -66,7 +66,7 @@ abstract class BaseActivity : AppCompatActivity() {
} }
resultCallbacks[requestCode]?.also { resultCallbacks[requestCode]?.also {
resultCallbacks.remove(requestCode) resultCallbacks.remove(requestCode)
it(this@BaseActivity, if (success) 1 else -1, null) it(this, if (success) 1 else -1, null)
} }
} }
...@@ -75,7 +75,7 @@ abstract class BaseActivity : AppCompatActivity() { ...@@ -75,7 +75,7 @@ abstract class BaseActivity : AppCompatActivity() {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
resultCallbacks[requestCode]?.also { resultCallbacks[requestCode]?.also {
resultCallbacks.remove(requestCode) resultCallbacks.remove(requestCode)
it(this@BaseActivity, resultCode, data) it(this, resultCode, data)
} }
} }
......
package com.topjohnwu.magisk.model.events
import android.content.Context
import androidx.fragment.app.Fragment
import com.topjohnwu.magisk.core.base.BaseActivity
interface ContextExecutor {
operator fun invoke(context: Context)
}
interface ActivityExecutor {
operator fun invoke(activity: BaseActivity)
}
interface FragmentExecutor {
operator fun invoke(fragment: Fragment)
}
package com.topjohnwu.magisk.model.events
internal interface EventHandler {
/**
* Called for all [ViewEvent]s published by associated viewModel.
* For [SimpleViewEvent]s, both this and [onSimpleEventDispatched]
* methods are called - you can choose the way how you handle them.
*/
fun onEventDispatched(event: ViewEvent) {}
/**
* Called for all [SimpleViewEvent]s published by associated viewModel.
* Both this and [onEventDispatched] methods are called - you can choose
* the way how you handle them.
*/
fun onSimpleEventDispatched(event: Int) {}
val viewEventObserver get() = ViewEventObserver {
onEventDispatched(it)
if (it is SimpleViewEvent) {
onSimpleEventDispatched(it.event)
}
}
}
package com.topjohnwu.magisk.model.events
class SimpleViewEvent(
val event: Int
) : ViewEvent()
\ No newline at end of file
...@@ -3,6 +3,7 @@ package com.topjohnwu.magisk.model.events ...@@ -3,6 +3,7 @@ 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
...@@ -32,7 +33,6 @@ import java.lang.reflect.InvocationHandler ...@@ -32,7 +33,6 @@ import java.lang.reflect.InvocationHandler
* Use [ViewEventObserver] for observing these events * Use [ViewEventObserver] for observing these events
*/ */
abstract class ViewEvent { abstract class ViewEvent {
var handled = false var handled = false
} }
...@@ -40,6 +40,18 @@ abstract class ViewEventsWithScope: ViewEvent() { ...@@ -40,6 +40,18 @@ abstract class ViewEventsWithScope: ViewEvent() {
lateinit var scope: CoroutineScope 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 { ) : ViewEventsWithScope(), ContextExecutor, KoinComponent, SafetyNetHelper.Callback {
...@@ -146,7 +158,7 @@ class CheckSafetyNetEvent( ...@@ -146,7 +158,7 @@ class CheckSafetyNetEvent(
} }
class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), ActivityExecutor { class ViewActionEvent(val action: BaseActivity.() -> Unit) : ViewEvent(), ActivityExecutor {
override fun invoke(activity: BaseActivity) = activity.run(action) override fun invoke(activity: BaseActivity) = action(activity)
} }
class OpenChangelogEvent(val item: Repo) : ViewEventsWithScope(), ContextExecutor { class OpenChangelogEvent(val item: Repo) : ViewEventsWithScope(), ContextExecutor {
......
...@@ -7,26 +7,23 @@ import android.view.View ...@@ -7,26 +7,23 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.core.content.res.use import androidx.core.content.res.use
import androidx.core.graphics.Insets
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.databinding.OnRebindCallback import androidx.databinding.OnRebindCallback
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import androidx.navigation.NavDirections import androidx.navigation.NavDirections
import androidx.navigation.findNavController import androidx.navigation.findNavController
import com.topjohnwu.magisk.BR 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.snackbar
import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.model.events.EventHandler import com.topjohnwu.magisk.model.events.ActivityExecutor
import com.topjohnwu.magisk.model.events.SnackbarEvent import com.topjohnwu.magisk.model.events.ContextExecutor
import com.topjohnwu.magisk.model.events.ViewEvent import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.theme.Theme import com.topjohnwu.magisk.ui.theme.Theme
abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBinding> : abstract class BaseUIActivity<VM : BaseViewModel, Binding : ViewDataBinding> :
BaseActivity(), CompatView<ViewModel>, EventHandler { BaseActivity(), BaseUIComponent<VM> {
protected lateinit var binding: Binding protected lateinit var binding: Binding
protected abstract val layoutRes: Int protected abstract val layoutRes: Int
...@@ -37,12 +34,10 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -37,12 +34,10 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
protected val currentFragment get() = topFragment as? BaseUIFragment<*, *> protected val currentFragment get() = topFragment as? BaseUIFragment<*, *>
override val viewRoot: View get() = binding.root override val viewRoot: View get() = binding.root
override val navigation by lazy { open val navigation by lazy {
kotlin.runCatching { findNavController(navHost) }.getOrNull() runCatching { findNavController(navHost) }.getOrNull()
} }
private val delegate by lazy { CompatDelegate(this) }
open val navHost: Int = 0 open val navHost: Int = 0
open val snackbarView get() = binding.root open val snackbarView get() = binding.root
...@@ -66,35 +61,33 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -66,35 +61,33 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
.also { window.setBackgroundDrawable(it) } .also { window.setBackgroundDrawable(it) }
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
startObserveEvents()
viewModel.viewEvents.observe(this, viewEventObserver)
binding = DataBindingUtil.setContentView<Binding>(this, layoutRes).also {
binding = DataBindingUtil.setContentView<Binding>(this, layoutRes).apply { it.setVariable(BR.viewModel, viewModel)
setVariable(BR.viewModel, viewModel) it.lifecycleOwner = this
lifecycleOwner = this@BaseUIActivity it.addOnRebindCallback(object : OnRebindCallback<Binding>() {
override fun onPreBind(binding: Binding): Boolean {
(binding.root as? ViewGroup)?.startAnimations()
return super.onPreBind(binding)
}
})
} }
binding.addOnRebindCallback(object : OnRebindCallback<Binding>() { ensureInsets()
override fun onPreBind(binding: Binding): Boolean {
(binding.root as? ViewGroup)?.startAnimations()
return super.onPreBind(binding)
}
})
delegate.onCreate()
directionsDispatcher.observe(this, Observer { directionsDispatcher.observe(this) {
it?.navigate() it?.navigate()
// we don't want the directions to be re-dispatched, so we preemptively set them to null // we don't want the directions to be re-dispatched, so we preemptively set them to null
if (it != null) { if (it != null) {
directionsDispatcher.value = null directionsDispatcher.value = null
} }
}) }
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
delegate.onResume() viewModel.requestRefresh()
} }
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
...@@ -102,10 +95,8 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -102,10 +95,8 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
} }
override fun onEventDispatched(event: ViewEvent) { override fun onEventDispatched(event: ViewEvent) {
delegate.onEventExecute(event, this) (event as? ContextExecutor)?.invoke(this)
when (event) { (event as? ActivityExecutor)?.invoke(this)
is SnackbarEvent -> snackbar(snackbarView, event.message(this), event.length, event.f)
}
} }
override fun onBackPressed() { override fun onBackPressed() {
...@@ -114,12 +105,6 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -114,12 +105,6 @@ abstract class BaseUIActivity<ViewModel : BaseViewModel, Binding : ViewDataBindi
} }
} }
override fun peekSystemWindowInsets(insets: Insets) {
viewModel.insets = insets
}
protected fun ViewEvent.dispatchOnSelf() = onEventDispatched(this)
fun NavDirections.navigate() { fun NavDirections.navigate() {
navigation?.navigate(this) navigation?.navigate(this)
} }
......
package com.topjohnwu.magisk.ui.base
import android.view.View
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<VM : BaseViewModel>: LifecycleOwner {
val viewRoot: View
val viewModel: VM
fun startObserveEvents() {
viewModel.viewEvents.observe(this) {
onEventDispatched(it)
}
}
fun consumeSystemWindowInsets(insets: Insets): Insets? = null
/**
* Called for all [ViewEvent]s published by associated viewModel.
*/
fun onEventDispatched(event: ViewEvent) {}
fun ensureInsets() {
ViewCompat.setOnApplyWindowInsetsListener(viewRoot) { _, insets ->
insets.asInsets()
.also { viewModel.insets = it }
.let { consumeSystemWindowInsets(it) }
?.subtractBy(insets) ?: insets
}
if (ViewCompat.isAttachedToWindow(viewRoot)) {
ViewCompat.requestApplyInsets(viewRoot)
} else {
viewRoot.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(v: View) = Unit
override fun onViewAttachedToWindow(v: View) {
ViewCompat.requestApplyInsets(v)
}
})
}
}
private fun WindowInsetsCompat.asInsets() = Insets.of(
systemWindowInsetLeft,
systemWindowInsetTop,
systemWindowInsetRight,
systemWindowInsetBottom
)
private fun Insets.subtractBy(insets: WindowInsetsCompat) =
WindowInsetsCompat.Builder(insets).setSystemWindowInsets(
Insets.of(
insets.systemWindowInsetLeft - left,
insets.systemWindowInsetTop - top,
insets.systemWindowInsetRight - right,
insets.systemWindowInsetBottom - bottom
)
).build()
}
...@@ -13,25 +13,26 @@ import androidx.fragment.app.Fragment ...@@ -13,25 +13,26 @@ 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.EventHandler 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 import com.topjohnwu.magisk.model.events.ViewEvent
abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBinding> : abstract class BaseUIFragment<VM : BaseViewModel, Binding : ViewDataBinding> :
Fragment(), CompatView<ViewModel>, EventHandler { Fragment(), BaseUIComponent<VM> {
protected val activity get() = requireActivity() as BaseUIActivity<*, *> protected val activity get() = requireActivity() as BaseUIActivity<*, *>
protected lateinit var binding: Binding protected lateinit var binding: Binding
protected abstract val layoutRes: Int protected abstract val layoutRes: Int
override val viewRoot: View get() = binding.root override val viewRoot: View get() = binding.root
override val navigation get() = activity.navigation private val navigation get() = activity.navigation
private val delegate by lazy { CompatDelegate(this) }
override fun consumeSystemWindowInsets(insets: Insets) = insets override fun consumeSystemWindowInsets(insets: Insets) = insets
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
viewModel.viewEvents.observe(this, viewEventObserver) startObserveEvents()
} }
override fun onCreateView( override fun onCreateView(
...@@ -39,17 +40,17 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -39,17 +40,17 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
binding = DataBindingUtil.inflate<Binding>(inflater, layoutRes, container, false).apply { binding = DataBindingUtil.inflate<Binding>(inflater, layoutRes, container, false).also {
setVariable(BR.viewModel, viewModel) it.setVariable(BR.viewModel, viewModel)
lifecycleOwner = this@BaseUIFragment it.lifecycleOwner = this
} }
return binding.root return binding.root
} }
override fun onEventDispatched(event: ViewEvent) { override fun onEventDispatched(event: ViewEvent) {
super.onEventDispatched(event) (event as? ContextExecutor)?.invoke(requireContext())
delegate.onEventExecute(event, this) (event as? FragmentExecutor)?.invoke(this)
(event as? ActivityExecutor)?.invoke(activity)
} }
open fun onKeyEvent(event: KeyEvent): Boolean { open fun onKeyEvent(event: KeyEvent): Boolean {
...@@ -61,29 +62,24 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi ...@@ -61,29 +62,24 @@ abstract class BaseUIFragment<ViewModel : BaseViewModel, Binding : ViewDataBindi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding.addOnRebindCallback(object : OnRebindCallback<Binding>() { binding.addOnRebindCallback(object : OnRebindCallback<Binding>() {
override fun onPreBind(binding: Binding): Boolean { override fun onPreBind(binding: Binding): Boolean {
this@BaseUIFragment.onPreBind(binding) this@BaseUIFragment.onPreBind(binding)
return true return true
} }
}) })
ensureInsets()
delegate.onCreate()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.requestRefresh()
delegate.onResume()
} }
protected open fun onPreBind(binding: Binding) { protected open fun onPreBind(binding: Binding) {
(binding.root as? ViewGroup)?.startAnimations() (binding.root as? ViewGroup)?.startAnimations()
} }
protected fun ViewEvent.dispatchOnSelf() = delegate.onEventExecute(this, this@BaseUIFragment)
fun NavDirections.navigate() { fun NavDirections.navigate() {
navigation?.navigate(this) navigation?.navigate(this)
} }
......
...@@ -107,10 +107,6 @@ abstract class BaseViewModel( ...@@ -107,10 +107,6 @@ abstract class BaseViewModel(
_viewEvents.postValue(this) _viewEvents.postValue(this)
} }
fun Int.publish() {
_viewEvents.postValue(SimpleViewEvent(this))
}
fun NavDirections.publish() { fun NavDirections.publish() {
_viewEvents.postValue(NavigationWrapper(this)) _viewEvents.postValue(NavigationWrapper(this))
} }
......
package com.topjohnwu.magisk.ui.base
import android.view.View
import androidx.core.graphics.Insets
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
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
import timber.log.Timber
class CompatDelegate internal constructor(
private val view: CompatView<*>
) {
fun onCreate() {
ensureInsets()
}
fun onResume() {
view.viewModel.requestRefresh()
}
fun onEventExecute(event: ViewEvent, activity: BaseUIActivity<*, *>) {
(event as? ContextExecutor)?.invoke(activity)
(event as? ActivityExecutor)?.invoke(activity)
(event as? FragmentExecutor)?.let {
Timber.e("Cannot run ${FragmentExecutor::class.java.simpleName} in Activity. Consider adding ${ContextExecutor::class.java.simpleName} as fallback.")
}
}
fun onEventExecute(event: ViewEvent, fragment: Fragment) {
(event as? ContextExecutor)?.invoke(fragment.requireContext())
(event as? FragmentExecutor)?.invoke(fragment)
(event as? ActivityExecutor)?.invoke(fragment.requireActivity() as BaseUIActivity<*, *>)
}
private fun ensureInsets() {
ViewCompat.setOnApplyWindowInsetsListener(view.viewRoot) { _, insets ->
insets.asInsets()
.also { view.peekSystemWindowInsets(it) }
.let { view.consumeSystemWindowInsets(it) }
?.also { view.viewModel.insets = it }
?.subtractBy(insets) ?: insets
}
if (ViewCompat.isAttachedToWindow(view.viewRoot)) {
ViewCompat.requestApplyInsets(view.viewRoot)
} else {
view.viewRoot.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewDetachedFromWindow(v: View) = Unit
override fun onViewAttachedToWindow(v: View) {
ViewCompat.requestApplyInsets(v)
}
})
}
}
private fun WindowInsetsCompat.asInsets() = Insets.of(
systemWindowInsetLeft,
systemWindowInsetTop,
systemWindowInsetRight,
systemWindowInsetBottom
)
private fun Insets.subtractBy(insets: WindowInsetsCompat) = insets.replaceSystemWindowInsets(
insets.systemWindowInsetLeft - left,
insets.systemWindowInsetTop - top,
insets.systemWindowInsetRight - right,
insets.systemWindowInsetBottom - bottom
)
}
package com.topjohnwu.magisk.ui.base
import android.view.View
import androidx.core.graphics.Insets
import androidx.navigation.NavController
internal interface CompatView<ViewModel : BaseViewModel> {
val viewRoot: View
val viewModel: ViewModel
val navigation: NavController?
fun peekSystemWindowInsets(insets: Insets) = Unit
fun consumeSystemWindowInsets(insets: Insets): Insets? = null
}
...@@ -12,9 +12,6 @@ import com.topjohnwu.magisk.R ...@@ -12,9 +12,6 @@ import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.su.SuCallbackHandler import com.topjohnwu.magisk.core.su.SuCallbackHandler
import com.topjohnwu.magisk.core.su.SuCallbackHandler.REQUEST import com.topjohnwu.magisk.core.su.SuCallbackHandler.REQUEST
import com.topjohnwu.magisk.databinding.ActivityRequestBinding import com.topjohnwu.magisk.databinding.ActivityRequestBinding
import com.topjohnwu.magisk.model.events.DieEvent
import com.topjohnwu.magisk.model.events.ViewActionEvent
import com.topjohnwu.magisk.model.events.ViewEvent
import com.topjohnwu.magisk.ui.base.BaseUIActivity import com.topjohnwu.magisk.ui.base.BaseUIActivity
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
...@@ -22,7 +19,6 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques ...@@ -22,7 +19,6 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
override val layoutRes: Int = R.layout.activity_request override val layoutRes: Int = R.layout.activity_request
override val viewModel: SuRequestViewModel by viewModel() override val viewModel: SuRequestViewModel by viewModel()
override val navigation: NavController? = null override val navigation: NavController? = null
override fun onBackPressed() { override fun onBackPressed() {
...@@ -65,14 +61,6 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques ...@@ -65,14 +61,6 @@ open class SuRequestActivity : BaseUIActivity<SuRequestViewModel, ActivityReques
return theme return theme
} }
override fun onEventDispatched(event: ViewEvent) {
super.onEventDispatched(event)
when (event) {
is ViewActionEvent -> event.action(this)
is DieEvent -> finish()
}
}
private fun lockOrientation() { private fun lockOrientation() {
requestedOrientation = if (Build.VERSION.SDK_INT < 18) requestedOrientation = if (Build.VERSION.SDK_INT < 18)
resources.configuration.orientation resources.configuration.orientation
......
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