Android
If your app's targetSdk is 35 or higher, you will need to use tapsdk:3.5.2 or higher. Android 15 introduced a change that will cut off the top title bar of the Survey Wall.
If your app is in test mode, you MUST use a test user. These are defined in "Test Devices" on the dashboard.
If you are experiencing issues or need assistance, please reach out to developers@tapresearch.com
Installation
Example apps are available here
Minimum SDK version: 23
Add the following to your build.gradle file
Add the following to your app-level build.gradle
file:
repositories {
maven { url "https://artifactory.tools.tapresearch.io/artifactory/tapresearch-android-sdk/" }
...
}
And then add the following to your dependencies section of your settings.gradle
file
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven { url "https://artifactory.tools.tapresearch.io/artifactory/tapresearch-android-sdk/" }
}
}
3.5.2+*:
Required for you to import the following dependencies.
If you're using a kotlin project, add the following to your build.gradle
file
The latest version available via artifactory can be seen here
If you're using a java project, add the following to your build.gradle
file
implementation 'com.tapresearch:tapsdk:3.5.2'
implementation 'androidx.lifecycle:lifecycle-process:2.6.1'
implementation 'androidx.core:core-ktx:1.10.1'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3'
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
implementation 'com.google.android.gms:play-services-appset:16.1.0'
Or if you're using a kotlin project
implementation("com.tapresearch:tapsdk:3.5.2")
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.0.1")
implementation("com.google.android.gms:play-services-appset:16.1.0")
Integration
Callbacks
There are a few callbacks that you will need to provide to the SDK upon initialization.
Override | 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. | No |
contentCallback (TRContentCallback ) | Lets you know when the content (survey wall, banner, interstitial, etc) is shown or dismissed. | No |
qqDataCallback (TRQQDataCallback ) | The callback hit every time a quick question is answered by your users. | No |
Reward callback (TRRewardCallback
): This is called when the user completes a survey and is passed an array of TRReward
's.
If you are using in-app callbacks to return rewards, you'll need to have this set up before initialization.
The SDK will respond with a TRReward object that contains the reward amount and currency code.
Error callback (TRErrorCallback
): This is called when there is an error and is passed an instance of TRError
.
sdkReadyCallback (TRSdkReadyCallback
): (optional) This is called when the SDK is ready to show content. This is a good time to show content.
contentCallback (TRContentCallback
): (optional) This is called when the content is shown or dismissed. This is a good time to log analytics.
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 Initializations 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)
Log.d("MainActivity", "API Token: $myApiToken")
Log.d("MainActivity", "User identifier: $myUserIdentifier")
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 = true,
),
)
// Your api token will likely be hardcoded in your app, but for the sake of this example
String myUserIdentifier = "public-demo-test-user";
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);
},
(error) -> {
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,
true // Optional parameter to clear previous user attributes
) // Optional initialization options
);
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
TapResearch.isReady()
// Example
if (TapResearch.isReady() == false) {
...
} else {
...
}
TapResearch.INSTANCE.isReady()
// Example
if (TapResearch.INSTANCE.isReady() == false) {
...
} 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
rewardCallback = { rewards -> showRewardToast(rewards) }
TapResearch.setRewardCallback(rewardCallback)
private fun showRewardToast(rewards: MutableList<TRReward>) {
var rewardCount = 0
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()
}
// important: when you no longer need to wait for rewards, such as Activity onPause, do:
TapResearch.setRewardCallback(null)
TapResearch.INSTANCE.setRewardCallback(rewardCallback)
private void showRewardToast(List<TRReward> rewards) {
int rewardCount = 0;
for (TRReward reward : rewards) {
if (reward.getRewardAmount() != null) {
rewardCount += reward.getRewardAmount();
}
}
String currencyName = rewards.getCurrencyName();
String eventType = rewards.getPayoutEventType();
Toast.makeText(MainActivity.this,"Congrats! You've earned " + rewardCount + " " + currencyName + ". Event type is " + eventType,Toast.LENGTH_LONG).show();
}
// important: when you no longer need to wait for rewards, such as Activity onPause, do:
TapResearch.INSTANCE.setRewardCallback(null)
Reward object
Note: in Java, you'll need to use the
getRewardAmount()
andgetCurrencyName()
, etc. methods
The reward object contains the following properties:
transactionIdentifier
will be the unique identifier for the reward - this is used to prevent duplicate rewardsplacementIdentifier
will be the unique identifier for the placement - this can be used by your analytics to show which placement rewarded the usercurrencyName
will be the name of the currency name that was rewarded. This is theCurrency Name
that you set up in the dashboard. This is different from theDisplay Name
rewardAmount
will be the amount of the currency that was rewarded, in your currencyplacementTag
will be the tag of the placement that rewarded the user - its human readable and unique
A TRReward object will look like this:
TRReward(
transactionIdentifier="tap_pr_062ff34493dff7123691f79921978391",
placementIdentifier="ffbd56740417114bc70c57f93151fa74",
currencyName="tokens",
rewardAmount=10,
payoutEventType="profileReward",
placementTag="normal-offer"
)
Quick Question callback setup additional information
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: $quickQuestionData")
}
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 ErrorCallback() {
@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:
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.
User attributes
See more on user attributes here.
User attributes are used to target specific users with content (ex: an offer). They can be set at any time and the most recent values received will be used when determining which content to show.
User attributes are passed as a dictionary of key-value pairs.
User attributes prefixed with tapresearch_
are reserved for internal use. Please do not use this prefix for user attributes as doing so will result in an error
The keys must be strings and the values must be one of:
- String
- Float
- Integer
If you want to use a date, please stringify an ISO8601 date or use a timestamp.
There are two ways of sending user attributes. Firstly, you are able to pass them in initialization in initOptions. This works well any time you have user attributes avaiable at sdk initialization time. clearPreviousAttributes allows you to remove any attributes previously sent to us for the user identifier.
initialize(
// ...
initOptions =
TapInitOptions(
userAttributes =
hashMapOf(
"is_vip" to 1,
"something_else" to 2,
),
clearPreviousAttributes = true,
),
//...
)
You can send user attributes as soon as the SDK is ready. This is done by calling sendUserAttributes
with a HashMap
of attributes.
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"] = true
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", true);
userAttributes.put("name", "John Doe");
userAttributes.put("first_seen", Instant.now().toString());
TapResearch.INSTANCE.sendUserAttributes(userAttributes, 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. This is done by calling setUserIdentifier
with a string.
- Kotlin
- Java
TapResearch.setUserIdentifier(
userIdentifier = userId,
errorCallback = object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
showErrorToast(trError)
}
},
)
TapResearch.INSTANCE.setUserIdentifier(
userIdentifier,
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
showErrorToast(trError);
}
}
);
Displaying a placement
Checking if the placement is available
A placement is a survey that is content 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")
.
The error callback is optional.
- 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("placement tag")
.
The onTapResearchContentShown
, onTapResearchContentDismissed
, and onTapResearchDidError
callbacks are optional. As hashmap of customParameters is also optional.
- Kotlin
- Java
TapResearch.showContentForPlacement(
placementTag,
customParameters,
object : TRContentCallback {
override fun onTapResearchContentShown(placement: TRPlacement) {
tapResearchDidDismiss(placement)
}
override fun onTapResearchContentDismissed(placement: TRPlacement) {
tapResearchContentShown(placement)
}
},
object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
showErrorToast(trError)
}
},
)
TapResearch.INSTANCE.showContentForPlacement(
placementTag,
application,
customParameters,
new TRContentCallback() {
@Override
public void onTapResearchContentShown(TRPlacement trPlacement) {
Log.d("TRLOG", "Content shown for placement " + trPlacement.toString());
}
@Override
public void onTapResearchContentDismissed(TRPlacement trPlacement) {
Log.d("TRLOG", "Content dismissed for placement " + trPlacement.toString());
}
},
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 via server-to-server callbacks.
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"] = true
customParameters["name"] = "John Doe"
TapResearch.showContentForPlacement(
placementTag,
application,
customParameters,
object : TRContentCallback {
override fun onTapResearchContentShown(placement: TRPlacement) {
tapResearchDidDismiss(placement)
}
override fun onTapResearchContentDismissed(placement: TRPlacement) {
tapResearchContentShown(placement)
}
},
object : TRErrorCallback {
override fun onTapResearchDidError(trError: TRError) {
showErrorToast(trError)
}
},
)
HashMap<String, Object> customParameters = new HashMap<>();
customParameters.put("age", 25);
customParameters.put("VIP", true);
customParameters.put("name", "John Doe");
TapResearch.INSTANCE.showContentForPlacement(
selectedItem,
application,
placementCustomParameters,
new TRContentCallback() {
@Override
public void onTapResearchContentShown(TRPlacement trPlacement) {
Log.d("TRLOG", "Content shown for placement " + trPlacement.toString());
}
@Override
public void onTapResearchContentDismissed(TRPlacement trPlacement) {
Log.d("TRLOG", "Content dismissed for placement " + trPlacement.toString());
}
},
new TRErrorCallback() {
@Override
public void onTapResearchDidError(TRError trError) {
showErrorToast(trError);
}
}
);
Android Proguard
When creating a release version of your app, please use our proguard rules to your existing proguard file.