Skip to content

Commit

Permalink
[Fragment] Add onNewInstance callback.
Browse files Browse the repository at this point in the history
  • Loading branch information
Laimiux committed Dec 12, 2023
1 parent 877827f commit ab6dd4c
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 57 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.instacart.formula.android

import android.content.Context
import android.os.Bundle
import android.os.SystemClock
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand Down Expand Up @@ -30,25 +28,38 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
requireArguments().getParcelable<FragmentKey>(ARG_CONTRACT)!!
}

private val formulaFragmentId: FragmentId by lazy {
getFormulaFragmentId()
}

private val environment: FragmentEnvironment
get() = FormulaFragmentDelegate.fragmentEnvironment()

private val fragmentDelegate: FragmentEnvironment.FragmentDelegate
get() = environment.fragmentDelegate

private var calledNewInstance = false

private var featureView: FeatureView<Any>? = null
private var output: Any? = null

private var initializedAtMillis: Long? = SystemClock.uptimeMillis()
private var firstRender = true

private val lifecycleCallback: FragmentLifecycleCallback?
get() = featureView?.lifecycleCallbacks

override fun onAttach(context: Context) {
super.onAttach(context)
if (initializedAtMillis == null) {
initializedAtMillis = SystemClock.uptimeMillis()
override fun setArguments(args: Bundle?) {
super.setArguments(args)

/**
* To ensure that we have both fragment key and formula instance id, we need
* to wait for arguments to be set.
*/
if (!calledNewInstance) {
calledNewInstance = true
fragmentDelegate.onNewInstance(formulaFragmentId)
}
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
firstRender = true

val viewFactory = FormulaFragmentDelegate.viewFactory(this) ?: run {
// No view factory, no view
return null
Expand Down Expand Up @@ -102,8 +113,6 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
}

override fun onDestroyView() {
initializedAtMillis = null

lifecycleCallback?.onDestroyView()
super.onDestroyView()
featureView = null
Expand All @@ -130,22 +139,8 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
val output = output ?: return
val view = featureView ?: return

val fragmentId = getFormulaFragmentId()
val environment = FormulaFragmentDelegate.fragmentEnvironment()
val fragmentDelegate = environment.fragmentDelegate

try {
fragmentDelegate.setOutput(fragmentId, output, view.setOutput)

if (firstRender) {
val end = SystemClock.uptimeMillis()

firstRender = false
fragmentDelegate.onFirstModelRendered(
fragmentId = fragmentId,
durationInMillis = end - (initializedAtMillis ?: SystemClock.uptimeMillis()),
)
}
fragmentDelegate.setOutput(formulaFragmentId, output, view.setOutput)
} catch (exception: Exception) {
environment.onScreenError(key, exception)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ data class FragmentEnvironment(
*/
open class FragmentDelegate {

/**
* Called when new instance of [FormulaFragment] is created.
*/
open fun onNewInstance(
fragmentId: FragmentId
) = Unit

/**
* Instantiates the feature.
*/
Expand Down Expand Up @@ -44,11 +51,5 @@ data class FragmentEnvironment(
open fun setOutput(fragmentId: FragmentId, output: Any, applyOutputToView: (Any) -> Unit) {
applyOutputToView(output)
}

/**
* Called after first render model is rendered. The [durationInMillis] starts
* when formula fragment is initialized and ends after first render model is applied.
*/
open fun onFirstModelRendered(fragmentId: FragmentId, durationInMillis: Long) = Unit
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ internal class FragmentFlowRenderView(

override fun onFragmentAttached(fm: FragmentManager, f: Fragment, context: Context) {
super.onFragmentAttached(fm, f, context)
initializeFragmentInstanceIdIfNeeded(f)

if (FragmentLifecycle.shouldTrack(f)) {
onLifecycleEvent(FragmentLifecycle.createAddedEvent(f))
} else {
Expand Down Expand Up @@ -141,7 +139,6 @@ internal class FragmentFlowRenderView(
}

fun viewFactory(fragment: FormulaFragment): ViewFactory<Any> {
initializeFragmentInstanceIdIfNeeded(fragment)
return FormulaFragmentViewFactory(
environment = fragmentEnvironment,
fragmentId = fragment.getFormulaFragmentId(),
Expand All @@ -162,23 +159,4 @@ internal class FragmentFlowRenderView(
}
}
}

/**
* Creates a unique identifier the first time fragment is attached that
* is persisted across configuration changes.
*/
private fun initializeFragmentInstanceIdIfNeeded(f: Fragment) {
if (f is BaseFormulaFragment<*>) {
val arguments = f.arguments ?: run {
Bundle().apply {
f.arguments = this
}
}
val id = arguments.getString(FormulaFragment.ARG_FORMULA_ID, "")
if (id.isNullOrBlank()) {
val initializedId = UUID.randomUUID().toString()
arguments.putString(FormulaFragment.ARG_FORMULA_ID, initializedId)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.instacart.formula.android.internal

import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentInspector
import androidx.fragment.app.FragmentManager
Expand All @@ -8,6 +9,7 @@ import com.instacart.formula.android.FragmentKey
import com.instacart.formula.android.BaseFormulaFragment
import com.instacart.formula.android.FormulaFragment
import com.instacart.formula.android.events.FragmentLifecycleEvent
import java.util.UUID

/**
* Provides utility method [lifecycleEvents] to track what fragments are added and removed from the backstack.
Expand Down Expand Up @@ -37,8 +39,28 @@ private fun Fragment.getFragmentKey(): FragmentKey {
return fragment?.getFragmentKey() ?: EmptyFragmentKey(tag.orEmpty())
}

/**
* Gets a persisted across configuration changes fragment identifier or initializes
* one if it doesn't exist.
*/
private fun Fragment.getFragmentInstanceId(): String {
return arguments?.getString(FormulaFragment.ARG_FORMULA_ID) ?: ""
return if (this is BaseFormulaFragment<*>) {
val arguments = arguments ?: run {
Bundle().apply {
arguments = this
}
}
val id = arguments.getString(FormulaFragment.ARG_FORMULA_ID, "")
if (id.isNullOrBlank()) {
val initializedId = UUID.randomUUID().toString()
arguments.putString(FormulaFragment.ARG_FORMULA_ID, initializedId)
initializedId
} else {
id
}
} else {
""
}
}

internal fun Fragment.getFormulaFragmentId(): FragmentId {
Expand Down

0 comments on commit ab6dd4c

Please sign in to comment.