diff --git a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt index 9e994616..2f6cf898 100644 --- a/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt +++ b/formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt @@ -24,6 +24,7 @@ abstract class ComposeViewFactory : ViewFactory return FeatureView( view = view, setOutput = outputRelay::accept, + lifecycleCallbacks = null, ) } diff --git a/formula-android-tests/src/main/java/com/instacart/formula/test/TestBackCallbackRenderModel.kt b/formula-android-tests/src/main/java/com/instacart/formula/test/TestBackCallbackRenderModel.kt new file mode 100644 index 00000000..6065ffdb --- /dev/null +++ b/formula-android-tests/src/main/java/com/instacart/formula/test/TestBackCallbackRenderModel.kt @@ -0,0 +1,13 @@ +package com.instacart.formula.test + +import com.instacart.formula.android.BackCallback + +data class TestBackCallbackRenderModel( + private val onBackPressed: () -> Unit, + val blockBackCallback: Boolean = false, +) : BackCallback { + override fun onBackPressed(): Boolean { + this.onBackPressed.invoke() + return blockBackCallback + } +} \ No newline at end of file diff --git a/formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt b/formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt index acd23c2f..e7e1152a 100644 --- a/formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt +++ b/formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt @@ -15,6 +15,7 @@ import com.instacart.formula.android.BackCallback import com.instacart.formula.android.FormulaFragment import com.instacart.formula.android.FragmentEnvironment import com.instacart.formula.android.FragmentStore +import com.instacart.formula.test.TestBackCallbackRenderModel import com.instacart.formula.test.TestKey import com.instacart.formula.test.TestKeyWithId import com.instacart.formula.test.TestFragmentActivity @@ -275,6 +276,37 @@ class FormulaFragmentTest { assertThat(updateThreads).containsExactly(Thread.currentThread()) } + @Test fun `back callback blocks navigation`() { + val key = TestKeyWithId(1) + navigateToTaskDetail(id = key.id) + + Shadows.shadowOf(Looper.getMainLooper()).idle() + + var onBackPressed = 0 + sendStateUpdate(key, TestBackCallbackRenderModel( + onBackPressed = { + onBackPressed += 1 + }, + blockBackCallback = true + )) + + navigateBack() + + // We blocked navigation so visible fragment should still be details + assertThat(onBackPressed).isEqualTo(1) + assertVisibleContract(key) + + sendStateUpdate(key, TestBackCallbackRenderModel( + onBackPressed = { onBackPressed += 1 }, + blockBackCallback = false + )) + + navigateBack() + + assertThat(onBackPressed).isEqualTo(2) + assertVisibleContract(TestKey()) + } + @Test fun `notify fragment environment if setOutput throws an error`() { val key = TestKeyWithId(1) navigateToTaskDetail(id = key.id) diff --git a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt index 3d80ee2a..5e7fce28 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import com.instacart.formula.FormulaAndroid import com.instacart.formula.android.internal.FormulaFragmentDelegate import com.instacart.formula.android.internal.getFormulaFragmentId +import com.instacart.formula.android.internal.getOrSetArguments import java.lang.Exception class FormulaFragment : Fragment(), BaseFormulaFragment { @@ -18,7 +19,7 @@ class FormulaFragment : Fragment(), BaseFormulaFragment { @JvmStatic fun newInstance(key: FragmentKey): FormulaFragment { val fragment = FormulaFragment() - fragment.arguments = Bundle().apply { + fragment.getOrSetArguments().apply { putParcelable(ARG_CONTRACT, key) } FormulaAndroid.fragmentEnvironment().fragmentDelegate.onNewInstance( diff --git a/formula-android/src/main/java/com/instacart/formula/android/internal/FormulaFragmentDelegate.kt b/formula-android/src/main/java/com/instacart/formula/android/internal/FormulaFragmentDelegate.kt index 3f3f7aa4..9b12e71a 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/internal/FormulaFragmentDelegate.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/internal/FormulaFragmentDelegate.kt @@ -8,12 +8,7 @@ import com.instacart.formula.android.ViewFactory internal object FormulaFragmentDelegate { fun viewFactory(fragment: FormulaFragment): ViewFactory? { val appManager = FormulaAndroid.appManagerOrThrow() - - val activity = fragment.activity ?: run { - fragmentEnvironment().logger("FormulaFragment has no activity attached: ${fragment.getFragmentKey()}") - return null - } - + val activity = fragment.requireActivity() val viewFactory = appManager.findStore(activity)?.viewFactory(fragment) ?: run { // Log view factory is missing if (activity.isDestroyed) { diff --git a/formula-android/src/main/java/com/instacart/formula/android/internal/FragmentLifecycle.kt b/formula-android/src/main/java/com/instacart/formula/android/internal/FragmentLifecycle.kt index dd574f1b..6c9f2dd7 100644 --- a/formula-android/src/main/java/com/instacart/formula/android/internal/FragmentLifecycle.kt +++ b/formula-android/src/main/java/com/instacart/formula/android/internal/FragmentLifecycle.kt @@ -45,11 +45,7 @@ private fun Fragment.getFragmentKey(): FragmentKey { */ private fun Fragment.getFragmentInstanceId(): String { return if (this is BaseFormulaFragment<*>) { - val arguments = arguments ?: run { - Bundle().apply { - arguments = this - } - } + val arguments = getOrSetArguments() val id = arguments.getString(FormulaFragment.ARG_FORMULA_ID, "") if (id.isNullOrBlank()) { val initializedId = UUID.randomUUID().toString() @@ -68,4 +64,12 @@ internal fun Fragment.getFormulaFragmentId(): FragmentId { instanceId = getFragmentInstanceId(), key = getFragmentKey() ) +} + +internal fun Fragment.getOrSetArguments(): Bundle { + return arguments ?: run { + Bundle().apply { + arguments = this + } + } } \ No newline at end of file diff --git a/formula-android/src/test/java/com/instacart/formula/android/FragmentDataClassTest.kt b/formula-android/src/test/java/com/instacart/formula/android/FragmentDataClassTest.kt index 7adcd1ed..ae0a6812 100644 --- a/formula-android/src/test/java/com/instacart/formula/android/FragmentDataClassTest.kt +++ b/formula-android/src/test/java/com/instacart/formula/android/FragmentDataClassTest.kt @@ -1,6 +1,9 @@ package com.instacart.formula.android import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat +import com.instacart.formula.android.events.ActivityResult +import com.instacart.formula.android.events.FragmentLifecycleEvent import com.instacart.formula.android.fakes.MainKey import org.junit.Test @@ -13,8 +16,8 @@ class FragmentDataClassTest { key = fragmentKey ) - Truth.assertThat(fragmentId.instanceId).isEqualTo("instanceId") - Truth.assertThat(fragmentId.key).isEqualTo(fragmentKey) + assertThat(fragmentId.instanceId).isEqualTo("instanceId") + assertThat(fragmentId.key).isEqualTo(fragmentKey) } @Test fun fragmentOutput() { @@ -23,7 +26,33 @@ class FragmentDataClassTest { key = key, renderModel = Unit ) - Truth.assertThat(output.key).isEqualTo(key) - Truth.assertThat(output.renderModel).isEqualTo(Unit) + assertThat(output.key).isEqualTo(key) + assertThat(output.renderModel).isEqualTo(Unit) + } + + @Test fun activityResult() { + val result = ActivityResult( + requestCode = 0, + resultCode = 1, + data = null + ) + assertThat(result.requestCode).isEqualTo(0) + assertThat(result.resultCode).isEqualTo(1) + assertThat(result.data).isNull() + } + + @Test fun fragmentLifecycleEventRemoved() { + val fragmentKey = MainKey(id = 1) + val fragmentId = FragmentId( + instanceId = "instanceId", + key = fragmentKey + ) + + val event = FragmentLifecycleEvent.Removed( + fragmentId = fragmentId, + lastState = "last-state" + ) + assertThat(event.fragmentId).isEqualTo(fragmentId) + assertThat(event.lastState).isEqualTo("last-state") } } \ No newline at end of file