Android
Later sections cover user attributes, the user identifier, placement details, custom parameters, and more.
Testing? Your app starts in test mode, so you'll need a test device to see content while you build.
Installation
See the Android example apps on GitHub.
Minimum SDK version: 23.
Repository
- SDK 3.7.3 and later are published to Maven Central. Make sure
mavenCentral()is in your repositories; new Android projects include it by default. - SDK 3.7.2 and earlier are published to the TapResearch Artifactory repository. Add it to the
dependencyResolutionManagementrepositories in yoursettings.gradle(orsettings.gradle.kts) file:
- Groovy DSL
- Kotlin DSL
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url "https://artifactory.tools.tapresearch.io/artifactory/tapresearch-android-sdk/" }
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://artifactory.tools.tapresearch.io/artifactory/tapresearch-android-sdk/") }
}
}
Dependencies
Your app must also declare the libraries the SDK uses — the SDK does not pull them in. Add the following to the dependencies block of your app-level build.gradle (or build.gradle.kts) file:
Replace <version> with your SDK version from the matching repository above.
- Groovy DSL
- Kotlin DSL
implementation 'com.tapresearch:tapsdk:<version>'
implementation 'androidx.lifecycle:lifecycle-process:2.6.1'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0'
implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
implementation 'com.google.android.gms:play-services-ads-identifier:18.1.0'
implementation 'com.google.android.gms:play-services-appset:16.1.0'
implementation("com.tapresearch:tapsdk:<version>")
implementation("androidx.lifecycle:lifecycle-process:2.6.1")
implementation("androidx.core:core-ktx:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.8.0"))
implementation("com.google.android.gms:play-services-ads-identifier:18.1.0")
implementation("com.google.android.gms:play-services-appset:16.1.0")
Integration
Callbacks
These are the callbacks you provide to the SDK at initialization.
| Parameter | Description | Required? |
|---|---|---|
rewardCallback (TRRewardCallback) | The callback hit every time a reward is received for your users. | No |
errorCallback (TRErrorCallback) | The error message channel. Used if the SDK experiences an issue. | Yes |
sdkReadyCallback (TRSdkReadyCallback) | Lets you know when the SDK is done booting. | Yes |
qqDataCallback (TRQQDataCallback) | The callback hit every time a quick question is answered by your users. | No |
A content callback (TRContentCallback) is passed when you show a placement, not at initialization. It lets you know when content (survey wall, banner, interstitial, etc.) is shown or dismissed — a good time to log analytics.
Reward callback (TRRewardCallback): This is called when the user completes a survey and is passed a list of TRReward objects.
Pass a reward callback to initialize to receive in-app rewards.
Error callback (TRErrorCallback): This is called when there is an error and is passed an instance of TRError.
sdkReadyCallback (TRSdkReadyCallback): This is called when the SDK is ready to show content. This is a good time to show content.
SDK initialization
You will need to initialize the SDK with both a unique user identifier and an api token.
We suggest initializing the TapResearch SDK as early in the boot process as possible (once the user identifier is available). The demo app initializes in the onCreate method of the MainActivity.
Similarly, there is a callback for when the SDK is ready. This is called when the SDK has finished initializing and is ready to show content. We suggest showing content only when the SDK is ready.
Optionally, you may pass an initOptions parameter through initialization for additional configuration. See example below and Initialization Options.
If user attributes are known at SDK initialization, it is preferable to pass them through initOptions compared to using sendUserAttributes. This will result in quicker load times for targeted content.
The user identifier passed through initialization must be unique! Only initialize once you are able to provide a unique identifier. Re-using the same user identifier is not supported and will prevent your users from accessing our service.
- Kotlin
- Java
val myUserIdentifier = "public-demo-test-user"
val myApiToken = getString(R.string.api_token)
TapResearch.initialize(
apiToken = myApiToken,
// userIdentifier must be unique for your player!
userIdentifier = myUserIdentifier,
context = this@MainActivity,
// optional, handle rewards here or pass null and call setRewardCallback to handle later
rewardCallback = { rewards ->
showRewardToast(rewards)
},
errorCallback = { error ->
showErrorToast(error)
},
sdkReadyCallback = {
Log.d(LOG_TAG, "Tap SDK initialized and ready")
},
// optional, handle here or pass null and call setQuickQuestionCallback to handle later
qqDataCallback = { quickQuestionPayload ->
Log.d(LOG_TAG, "Quick Question completed payload: $quickQuestionPayload")
},
// optional, can pass null
initOptions =
TapInitOptions(
userAttributes =
hashMapOf(
"is_vip" to 1,
"something_else" to 2,
),
clearPreviousAttributes = false,
),
)
String myUserIdentifier = "public-demo-test-user";
String myApiToken = getString(R.string.api_token);
HashMap<String, Object> userAttributes = new HashMap<>();
userAttributes.put("is_vip", 1);
userAttributes.put("something_else", 2);
TapResearch.INSTANCE.initialize(
myApiToken,
myUserIdentifier, // user identifier must be unique for your player!
MainActivity.this,
// optional, handle rewards here or pass null and call setRewardCallback to handle later
(rewards) -> {
showRewardToast(rewards);
},
(trError) -> {
showErrorToast(trError);
},
() -> {
Log.d(LOG_TAG, "SDK is ready");
// now that the SDK is ready, we can show content and send user attributes
sendUserAttributes();
// showContent();
},
// optional quick question completed handler, can pass null here and call setQuickQuestionCallback later
(quickQuestionPayload) -> {
Log.d(LOG_TAG, "Quick Question was completed. Payload: " + quickQuestionPayload);
},
// optional, can pass in options or null
new TapInitOptions(
userAttributes,
false // true replaces previously sent user attributes
)
);
Checking if SDK is ready to use
In addition to waiting for the sdkReadyCallback via initialize (which is our recommended method),
you can also determine if the SDK is ready to use by checking isReady
- Kotlin
- Java
if (TapResearch.isReady()) {
// ...
} else {
// ...
}
if (TapResearch.INSTANCE.isReady()) {
// ...
} else {
// ...
}
Reward callback setup additional information
The reward callback can be set in two ways; via initialize and also via setRewardCallback.
It is required for apps using in-app callbacks for rewards.
Here we are just defining something that you are able to do with the rewards that are returned.
- Kotlin
- Java
// this can be set via initialize and also via setRewardCallback method
val rewardCallback = TRRewardCallback { rewards -> showRewardToast(rewards) }
TapResearch.setRewardCallback(rewardCallback)
private fun showRewardToast(rewards: MutableList<TRReward>) {
var rewardCount = 0f
for (reward: TRReward in rewards) {
reward.rewardAmount?.let { rewardCount += it }
}
val currencyName = rewards.first().currencyName
val eventType = rewards.first().payoutEventType
Toast.makeText(
this@MainActivity,
"Congrats! You've earned $rewardCount $currencyName. Event type is $eventType",
Toast.LENGTH_LONG,
).show()
}
// Note: in your Activity.onPause(), do:
TapResearch.setRewardCallback(null)
// Similarly, in your Activity.onResume(), do:
TapResearch.setRewardCallback(rewardCallback)
TRRewardCallback rewardCallback = rewards -> showRewardToast(rewards);
TapResearch.INSTANCE.setRewardCallback(rewardCallback);
private void showRewardToast(List<TRReward> rewards) {
float rewardCount = 0;
for (TRReward reward : rewards) {
if (reward.getRewardAmount() != null) {
rewardCount += reward.getRewardAmount();
}
}
String currencyName = rewards.isEmpty() ? "" : rewards.get(0).getCurrencyName();
String eventType = rewards.isEmpty() ? "" : rewards.get(0).getPayoutEventType();
Toast.makeText(MainActivity.this, "Congrats! You've earned " + rewardCount + " " + currencyName + ". Event type is " + eventType, Toast.LENGTH_LONG).show();
}
// Note: in your Activity.onPause(), do:
TapResearch.INSTANCE.setRewardCallback(null);
// Similarly, in your Activity.onResume(), do:
TapResearch.INSTANCE.setRewardCallback(rewardCallback);
Reward object
Each reward is a TRReward. See TRReward for its properties.
In Java, use the getters: getRewardAmount(), getCurrencyName(), getPayoutEventType(), and so on.
A TRReward object looks like this when printed:
TRReward(transactionIdentifier=tap_pr_062ff34493dff7123691f79921978391, placementIdentifier=ffbd56740417114bc70c57f93151fa74, currencyName=tokens, rewardAmount=10.0, payoutEventType=PROFILE_REWARD, placementTag=normal-offer)
Quick Question callback setup additional information
New to Quick Questions? See Quick Questions for what they are and the response payload.
The quick question callback can be set in two ways; via initialize and also via setQuickQuestionCallback.
It is optional for apps using quick questions that want to receive additional information when a user
completes a quick question.
Here we are just defining something that you are able to do with the quick question data that is returned.
- Kotlin
- Java
// this can be set via initialize and also via setQuickQuestionCallback method
val quickQuestionCallback = TRQQDataCallback { quickQuestionPayload ->
Log.d(LOG_TAG, "Quick Question payload data: $quickQuestionPayload")
}
TapResearch.setQuickQuestionCallback(quickQuestionCallback)
// important: when you no longer need to wait for quick question data, such as Activity onPause, do:
TapResearch.setQuickQuestionCallback(null)
// this can be set via initialize and also via setQuickQuestionCallback method
TRQQDataCallback quickQuestionCallback = new TRQQDataCallback() {
@Override
public void onQuickQuestionDataReceived(QQPayload qqPayload) {
Log.d(LOG_TAG, "quick question data received: " + qqPayload);
}
};
TapResearch.INSTANCE.setQuickQuestionCallback(quickQuestionCallback);
// important: when you no longer need to wait for quick question data, such as Activity onPause, do:
TapResearch.INSTANCE.setQuickQuestionCallback(null);
Quick Question data payload object
A QQPayload object will look like this:
data class QQPayload(
val surveyIdentifier: String,
val appName: String,
val apiToken: String,
val sdkVersion: String,
val platform: String,
val placementTag: String,
val userIdentifier: String,
val userLocale: String,
val seenAt: String,
val complete: Complete?,
val questions: List<Question>,
val targetAudience: Array<Map<String, String>>?,
)
Error callbacks
You can use the error callback to log or report errors.
See the Error Codes page for more information on the error codes.
- Kotlin
- Java
// This is set in the initialization callback
errorCallback = { trError -> showErrorToast(trError) }
...and the error toast
private fun showErrorToast(trError: TRError) {
Toast.makeText(
this@MainActivity,
"Error: ${trError.description}",
Toast.LENGTH_LONG,
).show()
}
// This is set in the error callback
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
showErrorToast(trError);
}
}
...and the error toast
private void showErrorToast(TRError trError) {
Toast.makeText(MainActivity.this, "Error: " + trError.getDescription(), Toast.LENGTH_LONG).show();
}
TRError object
The error object contains the following properties (both nullable). See TRError for details.
code: The error code. This can be used when debugging as there is an associated list of error codes and descriptions.description: The error message. This is a human readable description of the error.
Send user attributes
See User attributes and targeting for more.
User attributes are used to target specific users with content (ex: an offer). The most recent values received are used when determining which content to show.
Pass user attributes in initOptions during initialization, or call sendUserAttributes after the SDK is ready.
Attribute names must be strings and must not begin with tapresearch_, which is reserved for TapResearch. Use string or numeric values for targeting. For booleans, send "true"/"false" or 0/1 instead of native booleans.
Set clearPreviousAttributes to false to merge new values with existing attributes, or true to replace the attributes you previously sent.
initialize(
// ...
initOptions =
TapInitOptions(
userAttributes =
hashMapOf(
"is_vip" to 1,
"something_else" to 2,
),
clearPreviousAttributes = true,
),
//...
)
To update attributes after initialization, pass a HashMap to sendUserAttributes.
We suggest sending as many attributes as you think that you'll want to target on for surveys, special awards, etc.
- Kotlin
- Java
initialize(
// ...
sdkReadyCallback = object : TRSdkReadyCallback {
override fun onTapResearchSdkReady() {
Log.d(LOG_TAG, "SDK is ready")
val userAttributes: HashMap<String, Any> = HashMap()
userAttributes["age"] = 25
userAttributes["VIP"] = 1
userAttributes["name"] = "John Doe"
userAttributes["first_seen"] = Instant.now().toString()
TapResearch.sendUserAttributes(
userAttributes,
) { trError -> showErrorToast(trError) }
//...
}
},
//...
)
initialize(
//...
public void onTapResearchSdkReady() {
Log.d(LOG_TAG, "SDK is ready");
HashMap<String, Object> userAttributes = new HashMap<>();
userAttributes.put("age", 25);
userAttributes.put("VIP", 1);
userAttributes.put("name", "John Doe");
userAttributes.put("first_seen", Instant.now().toString());
// Java must pass every argument: attributes, clearPreviousAttributes, error callback
TapResearch.INSTANCE.sendUserAttributes(userAttributes, false, new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
Log.d(LOG_TAG, "Error sending user attributes: " + trError.toString());
}
});
//...
}
//...
)
Setting the user identifier
You can set the user identifier at any time by calling setUserIdentifier with a string.
- Kotlin
- Java
TapResearch.setUserIdentifier(userId)
TapResearch.INSTANCE.setUserIdentifier(userIdentifier);
Displaying a placement
Checking if the placement is available
A placement is a location in your app where TapResearch content is displayed to the user.
Ideally, you check to see if the placement can be shown before actually showing it.
To find out if the content is available for the given user, call canShowContentForPlacement("placement tag", errorCallback).
The error callback is required; it receives the reason when the placement can't be shown.
- Kotlin
- Java
if (TapResearch.canShowContentForPlacement(
placementTag,
errorCallback = object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
trError.description?.let { Log.e("TRERROR", it) }
}
},
)
) {
//...
}
if (TapResearch.INSTANCE.canShowContentForPlacement(placementTag, new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
Log.d("TRLOG", "Whoops " + trError.toString());
}
})) {
//...
}
Showing the placement
The placement is shown by calling showContentForPlacement. Its parameters, in order, are the placement tag, an optional TRContentCallback, optional custom parameters (a HashMap), and a required TRErrorCallback.
The content callback receives the placement tag (a String) in onTapResearchContentShown and onTapResearchContentDismissed.
- Kotlin
- Java
TapResearch.showContentForPlacement(
tag = placementTag,
contentCallback = object : TRContentCallback {
override fun onTapResearchContentShown(placementTag: String) {
tapResearchContentShown(placementTag)
}
override fun onTapResearchContentDismissed(placementTag: String) {
tapResearchDidDismiss(placementTag)
}
},
customParameters = customParameters,
errorCallback = object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
showErrorToast(trError)
}
},
)
TapResearch.INSTANCE.showContentForPlacement(
placementTag,
new TRContentCallback() {
@Override
public void onTapResearchContentShown(String placementTag) {
Log.d("TRLOG", "Content shown for placement " + placementTag);
}
@Override
public void onTapResearchContentDismissed(String placementTag) {
Log.d("TRLOG", "Content dismissed for placement " + placementTag);
}
},
customParameters, // or null
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
showErrorToast(trError);
}
}
);
Passing custom parameters
Custom parameters allow you to receive additional information about the user when they complete a survey. This can be used to filter out users who have already completed a survey, or to target specific users.
These params will be returned in your server-to-server postbacks.
There are a maximum of 5 custom parameters that can be passed per placement.
- Kotlin
- Java
val customParameters: HashMap<String, Any> = HashMap()
customParameters["age"] = 25
customParameters["VIP"] = 1
customParameters["name"] = "John Doe"
TapResearch.showContentForPlacement(
tag = placementTag,
contentCallback = object : TRContentCallback {
override fun onTapResearchContentShown(placementTag: String) {
tapResearchContentShown(placementTag)
}
override fun onTapResearchContentDismissed(placementTag: String) {
tapResearchDidDismiss(placementTag)
}
},
customParameters = customParameters,
errorCallback = object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
showErrorToast(trError)
}
},
)
HashMap<String, Object> customParameters = new HashMap<>();
customParameters.put("age", 25);
customParameters.put("VIP", 1);
customParameters.put("name", "John Doe");
TapResearch.INSTANCE.showContentForPlacement(
placementTag,
new TRContentCallback() {
@Override
public void onTapResearchContentShown(String placementTag) {
Log.d("TRLOG", "Content shown for placement " + placementTag);
}
@Override
public void onTapResearchContentDismissed(String placementTag) {
Log.d("TRLOG", "Content dismissed for placement " + placementTag);
}
},
customParameters,
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
showErrorToast(trError);
}
}
);
Getting placement details
Placement details can change as our SDK refreshes placements in the background, so it is not recommended to store or cache these values. Instead, refetch the details each time you need to use them, e.g. to display something in your UI.
Get placement details by calling getPlacementDetails("placement tag", optional errorCallback).
It will return a TRPlacementDetails object or null:
data class TRPlacementDetails(
val name: String? = null,
val contentType: String? = null,
val currencyName: String? = null,
val isSale: Boolean? = null,
val saleType: String? = null,
val saleEndDate: String? = null, // UTC date string. i.e. SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
val saleMultiplier: Float? = null,
val saleDisplayName: String? = null,
val saleTag: String? = null,
val bonusBarProgress: TRBonusBarProgress? = null,
)
data class TRBonusBarProgress(
val isActive: Boolean? = null,
val currentCompletes: Int? = null,
val bonusWindowEndAt: String? = null, // UTC date string. i.e. SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
val bonusTiers: List<TRBonusTier>? = null,
val error: TRError? = null,
)
data class TRBonusTier(
val tierNumber: Int? = null,
val completesNeeded: Int? = null,
val rewardAmount: Float? = null,
val status: String? = null,
)
- Kotlin
- Java
val placementDetails = TapResearch.getPlacementDetails("my-placement-tag") { error -> println(error) }
TRPlacementDetails placementDetails = TapResearch.INSTANCE.getPlacementDetails(
"my-placement-tag",
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
Log.d("Error", "Error getting placement details " + trError.toString());
}
}
);
Granting boosts
Call grantBoost with a configured User Boosts tag. The result callback is optional:
- Kotlin
- Java
import kotlinx.serialization.InternalSerializationApi
@OptIn(InternalSerializationApi::class)
fun grantBoost() {
TapResearch.grantBoost("BOOST_TAG") { response ->
if (response.success == true) {
// Boost granted
} else {
response.error?.let { Log.e(LOG_TAG, "Grant boost failed: ${it.code} ${it.description}") }
}
}
}
TapResearch.INSTANCE.grantBoost("BOOST_TAG", response -> {
if (Boolean.TRUE.equals(response.getSuccess())) {
// Boost granted
} else if (response.getError() != null) {
Log.e(LOG_TAG, "Grant boost failed: " + response.getError().getCode() + " " + response.getError().getDescription());
}
});
Android ProGuard
Add these rules to your app's ProGuard/R8 rules file:
-keep class com.tapresearch.** { *; }
-dontwarn com.tapresearch.**
-keepattributes Exceptions, InnerClasses, Signature, Deprecated, SourceFile, LineNumberTable, *Annotation*, EnclosingMethod, MethodParameters, LocalVariableTable, LocalVariableTypeTable
-keepparameternames
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
Need help?
If you're experiencing issues or need assistance, reach out to developers@tapresearch.com.
Stay updated: watch TapResearch Android SDK releases on GitHub to get notified about new SDK releases.