Skip to content

Android Usage

This page explains how to use Context Decision to calibrate your custom model, and how to make decisions based on the predictions.

  • Step 1: Add ContextSDK to your app
  • Step 2: Ship app into production in Calibration Mode
  • Step 3: Once the model is ready, you can start making decisions based on the SDK's predictions

Usage

  • Use a different, unique flowName for each upsell funnel (e.g. upsell_prompt, upsell_onboarding, etc.)
    • If you have the same purchase flow, but from different parts of your app, please use different a different flowName for each.
    • We automatically analyze the overlap between different flows, to decide if there should be one, or more models.
  • The timing is crucial: You need to create the context object right before showing your prompt, and then log the outcome of the user interaction after the user has either accepted or dismissed the prompt.
  • For each context object you create, be sure to log an outcome, even if the user dismisses the prompt.

We recommend using this approach for most apps, unless you have a specific reason to use the advanced methods.

In most cases, ContextSDK will execute your block instantly. Only if your app was recently launched, or resumed from the background, it will take up to a few seconds to get a full context.

ContextSDK.optimize("upsell") { context ->
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE) // or EventOutcome.NEGATIVE
}
ContextSDK.Companion.optimize("upsell", null, null, context -> {
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE);
  return Unit.INSTANCE;
});

It's critical you trigger your prompt inside the block, which behaves as follows:

  • The callback is instantly executed if your app has been running for at least 3 seconds
  • The callback being executed within 3 seconds if your app was just put into the foreground recently

Use Instant Callbacks if you want to show a prompt right after a user action (e.g. opening a screen)

There are cases where you may prefer to always instantly have your callback executed. For example, when you want to show a prompt right after a certain user-action, to prevent the prompt showing up when the user already left the screen.

ContextSDK.optimize("upsell", maxDelayS = 0) { context ->
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE) // or EventOutcome.NEGATIVE
}
ContextSDK.Companion.optimize("upsell", 0, null, context -> {
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE);
  return Unit.INSTANCE;
});

Using maxDelayS = 0 means that the context object may not be complete, which may reduce data quality. We recommend not using this approach right after the app start. Use in places where the app usually has already been running for a few seconds.

If you want full control, you can use the instantContext method to get a Context object instantly. This comes with some extra complexity, as you also need to check the .shouldUpsell property yourself to decide if you should show the prompt or not.

val context = ContextSDK.instantContext("upsell", durationS = 3)
if (context.shouldUpsell) {
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE) // or EventOutcome.NEGATIVE
} else{
  context.log(EventOutcome.SKIPPED)
}
RealWorldContext context = ContextSDK.Companion.instantContext("upsell", 3, null);
if (context.getShouldUpsell()) {
  // [Show the upgrade prompt here right after fetching the context]
  // Once you know if the user purchased or dismissed the upsell, log the outcome:
  context.log(EventOutcome.POSITIVE); // or EventOutcome.NEGATIVE
} else{
  context.log(EventOutcome.SKIPPED);
}

During calibration phase, .shouldUpsell will always be true, so your app's behavior won't change. Once your custom model is rolled out, the .shouldUpsell property will reflect the prediction of your model.

Helpers

Some apps may not have an easy way to pass the context object from where you initially generate it, to where you know the outcome of the user interaction. In those cases, you can use the recentContext method to get the most recent context object you created based on the unique flowName.

  • This approach requires you to create the context object first.
  • Only the most recent context for each flowName name will be stored, and older context objects will be discarded.
  • The context object must already be created before you show the prompt.
OnboardingActivity.kt
ContextSDK.optimize("upsell") { context ->
  // [Prompt the user for an upgrade right after getting the context]
  // Don't log any outcomes here
}
UpsellActivity.kt
// Once the user either finished the purchase, or dismissed the upgrade:
val outcome = EventOutcome.POSITIVE // or EventOutcome.NEGATIVE

ContextSDK.recentContext("upsell")?.let { context ->
  context.log(outcome)
}
OnboardingActivity.java
ContextSDK.Companion.optimize("upsell", null, null, context -> {
  // [Prompt the user for an upgrade right after getting the context]
  // Don't log any outcomes here
  return Unit.INSTANCE;
});
UpsellActivity.java
// Once the user either finished the purchase, or dismissed the upgrade:
EventOutcome outcome = EventOutcome.POSITIVE; // or EventOutcome.NEGATIVE
RealWorldContext context = ContextSDK.Companion.recentContext("upsell");
if (context != null) {
  context.log(outcome);
}

You can provide additional outcome details, for example to indicate which type of product the user has purchased.

ContextSDK.optimize("show_all_deals") { context ->
  // [Show the user a list of deals]
  val selectedDeal = user.selectedDeal
  if (selectedDeal != null) {
    context.outcomeMetadata["deal_selected"] = selectedDeal.number
    context.outcomeMetadata["deal_value_in_usd"] = selectedDeal.usdValue
    context.log(EventOutcome.POSITIVE)
  } else {
    context.log(EventOutcome.NEGATIVE)
  }
}
ContextSDK.Companion.optimize("show_all_deals", null, null, context -> {
  // [Show the user a list of deals]
  Deal selectedDeal = user.selectedDeal;
  if (selectedDeal != null) {
    context.getOutcomeMetadata().set("deal_selected",selectedDeal.number);
    context.getOutcomeMetadata().set("deal_value_in_usd", selectedDeal.usdValue);
    context.log(EventOutcome.POSITIVE);
  } else {
    context.log(EventOutcome.NEGATIVE);
  }
  return Unit.INSTANCE;
});

The information provided via the outcomeMetadata will not be used to determine if it's currently a good moment, you can use Custom Signals for that.

The information provided is used to help you decide on variants. For example, you may have different types of offers, or different price points, and you want to train ContextSDK to recommend the best one based on the current user context.

Custom Signals

ContextSDK uses more than 200 signals based on various built-in sensors to make its predictions. However, you can improve its performance by providing additional data to the ContextSDK that's specific to your app. Some examples of what you could provide: In-game progress, number of friends/messages, number of entries in a list, etc.

Global signals will be used across all ContextSDK calls, and are an easy way to provide additional data to the ContextSDK.

ContextSDK.globalCustomSignals["number_of_levels_completed"] = 21
ContextSDK.Companion.getGlobalCustomSignals().set("number_of_levels_completed", 21);

You can easily override the global custom signals by providing the same key again using a different value. To remove a global custom signal, call the globalCustomSignals method with a null value.

In general, we recommend using global signals. But if have custom signals that are specific to a certain flow, you can provide them as a parameter when getting your context object:

val customSignals = CustomSignals(
  mapOf(
    "number_of_friends" to SignalValue.IntValue(4)
  )
)

ContextSDK.optimize("onboarding_upsell", customSignals = customSignals) { context ->
  // ...
}
CustomSignals customSignals = new CustomSignals();
customSignals.set("number_of_friends", 4);
ContextSDK.Companion.optimize("onboarding_upsell", 0, customSignals, realWorldContext -> {
    // ...
});

Please be sure to consider the user's privacy and don't include any sensitive data. You are responsible for ensuring that you have the necessary rights to provide the data to us. Please at no point include any PII (including but not limited to emails, user IDs, IP addresses, exact locations, etc.) in your custom signals.