square / papa

PAPA: Performance of Android Production Applications
Apache License 2.0
351 stars 15 forks source link

Interaction latency rules #38

Closed pyricau closed 2 years ago

pyricau commented 2 years ago

Redesigning the API and implementation for tracking interaction latency, with a primary goal of decoupling the code where we send signals (events) from the place where we tie those events together an interaction.

Example usage code

interface PosInteractionEvent
interface PosInteraction

object OpenCheckoutApplet : PosInteraction

class Navigation(val destination: Any) : PosInteractionEvent

class CheckoutApplet : PosInteraction

object TapItem : PosInteractionEvent {
  val itemId = ""
}

object ItemVisibleInCart : PosInteractionEvent {
  val itemId = ""
}

class AddItemToCart(val itemId: String) : PosInteraction

object KeypadVisible : PosInteractionEvent
object UserPressedBack : PosInteractionEvent

fun setup() {

  val client = InteractionRuleClient<PosInteractionEvent, PosInteraction>()

  client.sendEvent(KeypadVisible)
  client.sendEvent(Navigation(Any()))

  val configuredRule = client.addInteractionRule<OpenCheckoutApplet> {
    onEvent<Navigation> { navigation ->
      if (navigation.destination is CheckoutApplet) {
        cancelRunningInteractions()
        startInteraction(OpenCheckoutApplet, onCancel = {
          Log.d("event canceled", it.cancelReason)
        })
      }
    }
    onEvent<UserPressedBack> {
      runningInteractions().singleOrNull()?.cancel("user pressed back")
    }
    onEvent<KeypadVisible> {
      runningInteractions().singleOrNull()?.finishOnFrameRendered { result ->
        logToAnalytics(result)
      }
    }
  }

  client.addInteractionRule<AddItemToCart> {
    onEvent<TapItem> { tapItem ->
      startInteraction(AddItemToCart(tapItem.itemId))
    }
    onEvent<ItemVisibleInCart> { itemVisibleInCart ->
      val addItemToCart = runningInteractions().firstOrNull {
        it.interaction.itemId == itemVisibleInCart.itemId
      }
      addItemToCart?.let { interaction ->
        interaction.finishOnFrameRendered { result ->
          logToAnalytics(result)
        }
      }
    }
  }

  // when we don't need this rule anymore
  configuredRule.remove()
}