For #4066: Create InflationAwareFeature for lazy inflation
parent
519c3bde5d
commit
23f5ac0fb9
@ -0,0 +1,84 @@
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import androidx.annotation.UiThread
|
||||
import mozilla.components.support.base.feature.BackHandler
|
||||
import mozilla.components.support.base.feature.LifecycleAwareFeature
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* A base feature class that enables lazy inflation of a view needed by a feature.
|
||||
*
|
||||
* When a feature needs to be launched (e.g. by user interaction) calling [launch]
|
||||
* will inflate the view only then, start the feature, and then executes [onLaunch]
|
||||
* for any feature-specific startup needs.
|
||||
*/
|
||||
abstract class InflationAwareFeature(
|
||||
private val stub: ViewStub
|
||||
) : LifecycleAwareFeature, BackHandler {
|
||||
|
||||
internal lateinit var view: WeakReference<View>
|
||||
internal var feature: LifecycleAwareFeature? = null
|
||||
private val stubListener = ViewStub.OnInflateListener { _, inflated ->
|
||||
view = WeakReference(inflated)
|
||||
feature = onViewInflated(inflated).also {
|
||||
it.start()
|
||||
onLaunch(inflated, it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a view-dependent feature needs to be started along with the feature itself.
|
||||
*/
|
||||
@UiThread
|
||||
fun launch() {
|
||||
// If we have a feature and view, we can launch immediately.
|
||||
if (feature != null && view.get() != null) {
|
||||
onLaunch(view.get()!!, feature!!)
|
||||
} else {
|
||||
stub.apply {
|
||||
setOnInflateListener(stubListener)
|
||||
inflate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation notes: This implemented method does nothing since we only start the feature
|
||||
* when the view is inflated.
|
||||
*/
|
||||
override fun start() {
|
||||
// We don't do anything because we only want to start the feature when it's being used.
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
feature?.stop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the feature gets the option to handle the user pressing the back key.
|
||||
*
|
||||
* @return true if the feature also implements [BackHandler] and the feature has been initiated.
|
||||
*/
|
||||
override fun onBackPressed(): Boolean {
|
||||
return (feature as? BackHandler)?.onBackPressed() ?: false
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when the view has been inflated for the feature to be created with it.
|
||||
*
|
||||
* @param view The newly created view.
|
||||
* @return The feature initiated with the view.
|
||||
*/
|
||||
abstract fun onViewInflated(view: View): LifecycleAwareFeature
|
||||
|
||||
/**
|
||||
* Invoked after the feature is instantiated. If the feature already exists,
|
||||
* this is invoked immediately.
|
||||
*
|
||||
* @param view The view that is attached to the feature.
|
||||
* @param feature The feature that was instantiated.
|
||||
*/
|
||||
abstract fun onLaunch(view: View, feature: LifecycleAwareFeature)
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package org.mozilla.fenix.components
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewStub
|
||||
import mozilla.components.support.base.feature.BackHandler
|
||||
import mozilla.components.support.base.feature.LifecycleAwareFeature
|
||||
import mozilla.components.support.test.any
|
||||
import mozilla.components.support.test.mock
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class InflationAwareFeatureTest {
|
||||
@Test
|
||||
fun `stub inflates if no feature or view exists`() {
|
||||
val stub: ViewStub = mock()
|
||||
val feature: InflationAwareFeature = spy(TestableInflationAwareFeature(stub))
|
||||
|
||||
feature.launch()
|
||||
|
||||
verify(stub).setOnInflateListener(any())
|
||||
verify(stub).inflate()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `stub immediately launches if the feature is available`() {
|
||||
val stub: ViewStub = mock()
|
||||
val feature: InflationAwareFeature = spy(TestableInflationAwareFeature(stub))
|
||||
|
||||
feature.feature = mock()
|
||||
feature.view = WeakReference(mock())
|
||||
|
||||
feature.launch()
|
||||
|
||||
verify(stub, never()).setOnInflateListener(any())
|
||||
verify(stub, never()).inflate()
|
||||
verify(feature).onLaunch(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `feature calls stop if created`() {
|
||||
val stub: ViewStub = mock()
|
||||
val inflationFeature: InflationAwareFeature = spy(TestableInflationAwareFeature(stub))
|
||||
val innerFeature: LifecycleAwareFeature = mock()
|
||||
|
||||
inflationFeature.stop()
|
||||
|
||||
verify(innerFeature, never()).stop()
|
||||
|
||||
inflationFeature.feature = innerFeature
|
||||
|
||||
inflationFeature.stop()
|
||||
|
||||
verify(innerFeature).stop()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `start does nothing`() {
|
||||
val stub: ViewStub = mock()
|
||||
val inflationFeature: InflationAwareFeature = spy(TestableInflationAwareFeature(stub))
|
||||
val innerFeature: LifecycleAwareFeature = mock()
|
||||
|
||||
inflationFeature.feature = innerFeature
|
||||
inflationFeature.view = WeakReference(mock())
|
||||
|
||||
inflationFeature.start()
|
||||
|
||||
verifyNoMoreInteractions(innerFeature)
|
||||
verifyNoMoreInteractions(stub)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `if feature has implemented BackHandler invoke it`() {
|
||||
val stub: ViewStub = mock()
|
||||
val inflationFeature: InflationAwareFeature = spy(TestableInflationAwareFeature(stub))
|
||||
val innerFeature: LifecycleAwareFeature = mock()
|
||||
val backHandlerFeature = object : LifecycleAwareFeature, BackHandler {
|
||||
override fun onBackPressed() = true
|
||||
|
||||
override fun start() {}
|
||||
|
||||
override fun stop() {}
|
||||
}
|
||||
|
||||
assert(!inflationFeature.onBackPressed())
|
||||
|
||||
inflationFeature.feature = innerFeature
|
||||
|
||||
assert(!inflationFeature.onBackPressed())
|
||||
|
||||
inflationFeature.feature = backHandlerFeature
|
||||
|
||||
assert(inflationFeature.onBackPressed())
|
||||
}
|
||||
}
|
||||
|
||||
class TestableInflationAwareFeature(stub: ViewStub) : InflationAwareFeature(stub) {
|
||||
override fun onViewInflated(view: View): LifecycleAwareFeature {
|
||||
return mock()
|
||||
}
|
||||
|
||||
override fun onLaunch(view: View, feature: LifecycleAwareFeature) {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue