- Requirements
- Installation
- Configure the codeless Thunderhead SDK for Android
- Additional features
- Opt an end-user out of tracking
- Opt an end user out of city country level tracking
- Exclude an Interaction
- Disable automatic Interaction detection
- Programmatic Interactions and Properties API
- Retrieve a response for an automatically triggered Interaction request
- Assign an Interaction to a View
- Ability to include identity transfer links
- Ability to exclude identity transfer links
- Disable automatic identity transfer
- Disable automatic outbound link tracking
- Send a location object
- Get Tid
- Configuring Logging
- Identify the SDK version
- Clear the user profile
- Further integration details
- Troubleshooting guide
- Questions or need help
- Android Gradle Plugin 3.6.x
- Android 5.0+ (API 21) and above
- Gradle 5.6.4
-
Open your existing Android application in Android Studio.
-
Include the Thunderhead SDK as a dependency in your project: Navigate to your app-level build.gradle.
Add the following, under the dependencies section:
For Thunderhead ONE integrations:
dependencies { implementation "com.thunderhead.android:one-sdk:11.1.4" }
For Salesforce Interaction Studio integrations:
dependencies { implementation "com.thunderhead.android:is-sdk:11.1.4" }
-
Add the Thunderhead SDK configuration within the same app-level
build.gradle
file.Add
RenderScript
support under thedefaultConfig
section:defaultConfig { renderscriptTargetApi 22 renderscriptSupportModeEnabled true }
Add the following, under the repositories section:
repositories { maven { url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android' } }
Append the following configuration, for both Thunderhead ONE and Salesforce Interaction Studio integrations:
apply plugin: 'com.thunderhead.android.orchestration-plugin'
-
Add Java 8 Support
Add the following, under the
android
sectioncompileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 }
-
Update your
build.gradle
file to add codeless identity transfer support.Navigate to the top-level
build.gradle
file and add both a maven repository url and class path dependencies as shown below:buildscript { repositories { google() mavenCentral() maven { name 'Thunderhead' url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android' } } dependencies { classpath 'com.android.tools.build:gradle:4.2.0' classpath 'com.thunderhead.android:orchestration-plugin:7.0.0' } }
buildscript {
repositories {
google()
mavenCentral()
maven {
name 'Thunderhead'
url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.thunderhead.android:orchestration-plugin:7.0.0'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.application'
apply plugin: 'com.thunderhead.android.orchestration-plugin'
android {
compileSdkVersion 29
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
defaultConfig {
applicationId "com.thunderhead.android.demo"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
renderscriptTargetApi 22
renderscriptSupportModeEnabled true
}
}
dependencies {
implementation "com.thunderhead.android:one-sdk:11.1.4"
}
repositories {
maven {
url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android'
}
}
buildscript {
repositories {
google()
mavenCentral()
maven {
name 'Thunderhead'
url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.2.0'
classpath 'com.thunderhead.android:orchestration-plugin:7.0.0'
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: 'com.android.application'
apply plugin: 'com.thunderhead.android.orchestration-plugin'
android {
compileSdkVersion 29
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
defaultConfig {
applicationId "com.thunderhead.android.demo"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
renderscriptTargetApi 22
renderscriptSupportModeEnabled true
}
}
dependencies {
implementation "com.thunderhead.android:is-sdk:11.1.4"
}
repositories {
maven {
url 'https://thunderhead.mycloudrepo.io/public/repositories/one-sdk-android'
}
}
For further documentation on the orchestration-plugin
please see the Orchestration Plugin Readme.
Enable your app to automatically recognize Interactions in your app, by executing the following steps:
- Developer note: Android Studio
Instant Run
is not currently supported and must be disabled.
To start capturing insights and configuring orchestrations in User mode, you must first configure the Thunderhead SDK with your Thunderhead API parameters. You can find your Thunderhead API parameters on the API Credentials page in Thunderhead ONE or Salesforce Interaction Studio.
For more information on finding these parameters:
- For Thunderhead ONE integrations, see Find the Information required when Integrating ONE with your Mobile Solutions
- For Salesforce Interaction Studio integrations, see Find the Information required when Integrating Interaction Studio with your Mobile App
When you have your parameters, configure the SDK. We recommend adding the following lines of code for User Mode under the Application’s subclass onCreate()
method, though this is not required.
You must ensure the oneConfigure
top-level Kotlin function or setConfiguration
Java method is invoked after super.onCreate()
is called.
Kotlin
import com.thunderhead.mobile.oneConfigure
import com.thunderhead.mobile.configuration.OneMode;
// The rest of the imports
class YourApplication : Application() {
override fun onCreate() {
super.onCreate()
oneConfigure {
siteKey = SITE_KEY
apiKey = API_KEY
sharedSecret = SHARED_SECRET
userId = USER_ID
host = URI(HOST)
touchpoint = URI(TOUCHPOINT)
mode = OneMode.USER
}
}
companion object {
const val SITE_KEY = "ONE-XXXXXXXXXX-1022"
const val API_KEY = "f713d44a-8af0-4e79-ba7e-xxxxxxxxx"
const val SHARED_SECRET = "bb8bacb2-ffc2-4c52-aaf4-xxx"
const val USER_ID = "yourUsername@yourCompanyName" // For Interaction Studio integrations use a numeric user id - see https://permalink.thunderhead.com/mobile-docs/is-mobile-integration-info-credentials
const val HOST = "https://xx.thunderhead.com"
const val TOUCHPOINT = "myAppsNameURI"
}
}
Java
import com.thunderhead.mobile.configuration.OneConfiguration;
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.configuration.OneMode;
// The rest of the imports
public class YourApplication extends Application {
private static final String siteKey = "ONE-XXXXXXXXXX-1022";
private static final String apiKey = "f713d44a-8af0-4e79-ba7e-xxxxxxxxx";
private static final String sharedSecret = "bb8bacb2-ffc2-4c52-aaf4-xxx";
private static final String userId = "yourUsername@yourCompanyName"; // For Interaction Studio integrations use a numeric user id - see https://permalink.thunderhead.com/mobile-docs/is-mobile-integration-info-credentials
private static final String host = "https://xx.thunderhead.com";
private static final String touchpointURI = "myAppsNameURI";
@Override
public void onCreate() {
super.onCreate();
final OneConfiguration oneConfiguration = new OneConfiguration.Builder()
.siteKey(siteKey)
.apiKey(apiKey)
.sharedSecret(sharedSecret)
.userId(userId)
.host(URI.create(host))
.touchpoint(URI.create(touchpointURI))
.mode(OneMode.USER)
.build();
One.setConfiguration(oneConfiguration);
}
}
To use the SDK in Admin mode, change the mode
parameter to OneMode.ADMIN
.
Note:
- If you are running in Admin mode on Android 6.0+, you must enable the “draw over other apps” permission through your OS settings.
- Dynamic configuration of both Admin and User mode is supported.
You have now successfully integrated the codeless Thunderhead SDK for Android.
In order to reduce the number of unnecessary Interaction requests sent automatically by the SDK, only codeless Interactions with explicit Interaction paths created under a Touchpoint and configured with at least one point are sent to Thunderhead ONE or Salesforce Interaction Studio. This configuration change has been introduced in version 8.1.0 of the Android SDK.
Note:
- The SDK will only send codeless Interactions if they have been created under a Touchpoint and/or if they match wildcard rules defined under a Touchpoint.
- For a codeless Interaction to be sent by the SDK this Interaction needs to contain at least one Activity Capture Point, Attribute Capture Point, and/or Optimization Point.
- If you are running the SDK in User mode, you need to ensure that all Interactions and related points have been fully published, before the SDK will trigger a request.
The following permissions are included in the Thunderhead SDK's AndroidManifest.xml
and will be merged with your applications AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
Note:
- The
SYSTEM_ALERT_WINDOW
permission is needed only for Admin mode builds. Add this as a flavor-specific permission in your setup to avoid having to show it as a permission change to your Play Store users. - You can remove this permission in User mode builds by adding the following to your manifest:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" tools:node="remove" />
You can configure and reconfigure the SDK as many times as necessary.
- The SDK does not support partial, or piecemeal, configuration. You must provide all parameters, either all valid or invalid (
empty string
ornull
). - When configured with invalid parameters, the SDK is set in an unconfigured state.
See here for an example app that demonstrates dynamic configuration.
The Thunderhead SDK is automatically initialized in an unconfigured state.
- When unconfigured, the SDK queues end-user data locally and uploads that data to the server once the SDK is configured with valid parameters.
- You can disable this functionality, at any time, by setting the
oneOptOutConfiguration
totrue
. See more about opt out here.
Follow any of the steps below to access further functions of the SDK.
To opt an end-user out of tracking, when the end-user does not give permission to be tracked in the client app, call the oneConfigureOptOut
top-level Kotlin function or the One.setOptOutConfiguration
Java method as shown below:
Kotlin
import com.thunderhead.mobile.oneConfigureOptOut
oneConfigureOptOut {
optOut = true
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.optout.OneOptOutConfiguration;
final OneOptOutConfiguration optOutConfiguration = new OneOptOutConfiguration.Builder()
.optOut(true)
.build();
One.setOptOutConfiguration(optOutConfiguration);
Note:
- When a user is opted out, tracking stops and locally queued data is removed.
- You can opt a user back in, at any point, by setting the
optOut
parameter tofalse
using the same method. - For instructions on completely removing a user's data from Thunderhead ONE or Salesforce Interaction Studio, see our API Documentation.
Use this option to opt an end-user out or in of all city/country level tracking.
Examples of how to opt in to city/country level tracking
Kotlin
import com.thunderhead.mobile.oneConfigureOptOut
import com.thunderhead.mobile.optout.OneOptInOptions
val options = EnumSet.noneOf(OneOptInOptions::class.java)
options.add(OneOptInOptions.CITY_COUNTRY_DETECTION)
oneConfigureOptOut {
optOut = false
optInOptions = options
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.optout.OneOptOutConfiguration;
import com.thunderhead.mobile.optout.OneOptInOptions;
Set<OneOptInOptions> options = EnumSet.noneOf(OneOptInOptions.class);
options.add(OneOptInOptions.CITY_COUNTRY_DETECTION);
final OneOptOutConfiguration optOutConfiguration = new OneOptOutConfiguration.Builder()
.optOut(false)
.optInOptions(options)
.build();
One.setOptOutConfiguration(optOutConfiguration);
Examples of how to opt out of city/country level tracking
Kotlin
import com.thunderhead.mobile.oneConfigureOptOut
import com.thunderhead.mobile.optout.OneOptInOptions
val options = EnumSet.noneOf(OneOptInOptions::class.java)
oneConfigureOptOut {
optOut = false
optInOptions = options
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.optout.OneOptOutConfiguration;
import com.thunderhead.mobile.optout.OneOptInOptions;
Set<OneOptInOptions> options = EnumSet.noneOf(OneOptInOptions.class);
final OneOptOutConfiguration optOutConfiguration = new OneOptOutConfiguration.Builder()
.optOut(false)
.optInOptions(options)
.build();
One.setOptOutConfiguration(optOutConfiguration);
Note:
- By default a user is opted in and would need to be specifically opted out using the method mentioned above, depending on your specific privacy requirements.
- When a user is opted out, all opt in options are ignored.
Exclude a specific view from being automatically recognized as an Interaction, using the excludeAutomaticInteraction
Kotlin extension function or One.excludeAutomaticInteraction
Java method in an Activity's onCreate
method or a Fragment's onCreateView
, as shown below.
Kotlin
import com.thunderhead.mobile.excludeAutomaticInteraction
// rest of imports
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
findViewById<LinearLayout>(R.id.root)
.excludeAutomaticInteraction()
}
Java
import com.thunderhead.mobile.interactions.OneAutomaticInteractionExclusion;
// rest of imports
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_layout_2, null);
final OneAutomaticInteractionExclusion rootViewExclusion = new OneAutomaticInteractionExclusion.Builder()
.view(rootView)
.build();
One.excludeAutomaticInteraction(rootViewExclusion);
return rootView;
}
You can disable automatic Interaction detection by calling the oneConfigureCodelessInteractionTracking
Kotlin top-level function or
the One.setCodelessInteractionTrackingConfiguration
Java method with the appropriate configuration, as shown below:
Kotlin
import com.thunderhead.mobile.oneConfigureCodelessInteractionTracking
oneConfigureCodelessInteractionTracking {
// disables Fragment/Activity Interaction Tracking
disableCodelessInteractionTracking = true
// disables WebView URL Interaction Tracking
disableWebViewInteractionTracking = true
// disables Outbound Link Tracking
disableOutboundLinkTracking = true
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.codeless.OneCodelessInteractionTrackingConfiguration;
final OneCodelessInteractionTrackingConfiguration codelessInteractionTrackingConfiguration =
new OneCodelessInteractionTrackingConfiguration.Builder()
// disables Fragment/Activity Interaction Tracking
.disableCodelessInteractionTracking(true)
// disables WebView URL Interaction Tracking
.disableWebViewInteractionTracking(true)
// disables Outbound Link Tracking
.disableOutboundLinkTracking(true)
.build();
One.setCodelessInteractionTrackingConfiguration(codelessInteractionTrackingConfiguration);
When automatic Interaction detection is disabled, the SDK does not automatically send Interaction requests. You must send Interaction requests manually, as and when needed, using the methods provided in the sections below.
Set this back to false
at any point to restart automatic Interaction detection.
You can manually send Interaction Requests and Properties to ONE or Interaction Studio using
the oneSendInteraction
Kotlin top-level function or the One.sendInteraction
Java method.
The oneSendInteraction
Kotlin top-level function uses Kotlin Coroutines. The One.sendInteraction
Java method uses the Java Future
model and returns a Completable Future.
Send an Interaction request programmatically by calling the oneSendInteraction
Kotlin top-level function in a Coroutine, as shown below:
Kotlin
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
// rest of imports
scope.launch {
oneSendInteraction {
interactionPath = OneInteractionPath(URI("/interactionPath"))
}
}
To capture errors, set the throwErrors
parameter to true
and wrap the method in a try/catch
block, as shown below:
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
import com.thunderhead.mobile.responsetypes.OneAPIError
import com.thunderhead.mobile.responsetypes.OneSDKError
// rest of imports
scope.launch {
try {
oneSendInteraction(throwErrors = true) {
interactionPath = OneInteractionPath(URI("/interactionPath"))
}
} catch (error: OneSDKError) {
Log.e(TAG, "SDK Error: ${error.errorMessage}")
} catch (error: OneAPIError) {
Log.e(TAG, "Api Error: ${error.errorMessage}")
}
}
Send an Interaction request programmatically by calling the One.sendInteraction
Java method, as shown below:
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneInteractionPath;
import com.thunderhead.mobile.interactions.OneRequest;
// rest of imports
final OneRequest sendInteractionRequest = new OneRequest.Builder()
.interactionPath(new OneInteractionPath(URI.create("/interactionPath")))
.build();
One.sendInteraction(sendInteractionRequest);
Note:
- Sends a
POST
request to Thunderhead ONE or Salesforce Interaction Studio. Only thetid
from the response isused by the SDK; all other response objects are ignored. - When sending Interaction requests programmatically please ensure the Interaction starts with a
/
and contains only letters, numbers, and/or dashes. - When in Java be sure to perform the interaction request in a thread that is NOT the
Main
thread.
Send an Interaction request programmatically, access its response, and then process that response by calling the oneSendInteraction
Kotlin top-level function in a Coroutine, as shown below:
Kotlin
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
import com.thunderhead.mobile.process
// rest of imports
scope.launch {
val response = oneSendInteraction {
interactionPath = OneInteractionPath(URI("/interactionPath"))
}
response?.process()
}
To capture errors, set the throwErrors
parameter to true
and wrap the method in a try/catch
block, as shown below:
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
import com.thunderhead.mobile.process
import com.thunderhead.mobile.responsetypes.OneAPIError
import com.thunderhead.mobile.responsetypes.OneSDKError
// rest of imports
scope.launch {
try {
val response = oneSendInteraction(throwErrors = true) {
interactionPath = OneInteractionPath(URI("/interactionPath"))
}
response?.process()
} catch (error: OneSDKError) {
Log.e(TAG, "SDK Error: ${error.errorMessage}")
} catch (error: OneAPIError) {
Log.e(TAG, "Api Error: ${error.errorMessage}")
}
}
Send an Interaction request programmatically and process the response by calling the One.sendInteraction
Java method and enqueue with a callback, as shown below:
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneInteractionPath;
import com.thunderhead.mobile.interactions.OneRequest;
import com.thunderhead.mobile.responsetypes.OneAPIError;
import com.thunderhead.mobile.responsetypes.OneSDKError;
import com.thunderhead.mobile.responsetypes.OneResponse;
// rest of imports
final OneRequest sendInteractionRequest = new OneRequest.Builder()
.interactionPath(new OneInteractionPath(URI.create("/interactionPath")))
.build();
try {
final OneResponse response = One.sendResponseCode(true, sendInteractionRequest).join();
One.processResponse(response);
} catch (CompletionException error) {
Log.e(TAG, error.getCause());
} catch(OneSDKError error) {
Log.e(TAG, error.getErrorMessage());
} catch(OneAPIError error) {
Log.e(TAG, error.getErrorMessage());
}
The response can be passed to the One.processResponse
method, as shown above. This method returns the response to the SDK to process, attaching any activity capture, attribute capture, or optimize instructions to the interaction.
Note:
- Sends a
POST
request to Thunderhead ONE or Salesforce Interaction Studio. - When sending Interaction requests programmatically, please ensure the Interaction starts with a
/
and contains only letters, numbers, and/or dashes. - When in Java be sure to perform the interaction request in a thread that is NOT the
Main
thread.
Properties in the form of key/value pair strings can be sent to Thunderhead ONE or Salesforce Interaction Studio using the SDK's public methods.
Send an Interaction request with Properties, programmatically, and ignore the response
by calling the oneSendInteraction
Kotlin top-level function in a Coroutine, as shown below:
Kotlin
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
// rest of imports
scope.launch {
oneSendInteraction {
interactionPath = OneInteractionPath(URI("/interactionPath"))
properties = mapOf("keyA" to "valueA", "keyB" to "valueB")
}
}
To capture errors, set the throwErrors
parameter to true
and wrap the method in a try/catch
block, as shown below:
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendInteraction
import com.thunderhead.mobile.responsetypes.OneAPIError
import com.thunderhead.mobile.responsetypes.OneSDKError
// rest of imports
scope.launch {
try {
oneSendInteraction(throwErrors = true) {
interactionPath = OneInteractionPath(URI("/interactionPath"))
properties = mapOf("keyA" to "valueA", "keyB" to "valueB")
}
} catch (error: OneSDKError) {
Log.e(TAG, "SDK Error: ${error.errorMessage}")
} catch (error: OneAPIError) {
Log.e(TAG, "Api Error: ${error.errorMessage}")
}
}
Send an Interaction request with Properties, programmatically, and ignore the response
by calling the One.sendInteraction
Java method, as shown below:
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneCall;
import com.thunderhead.mobile.interactions.OneInteractionPath;
import com.thunderhead.mobile.interactions.OneRequest;
// rest of imports
final Map<String, String> properties = new HashMap<>();
properties.put("keyA", "valueA");
properties.put("keyB", "valueB");
final OneRequest sendInteractionRequest = new OneRequest.Builder()
.interactionPath(new OneInteractionPath(URI.create("/interactionPath")))
.properties(properties)
.build();
try {
One.sendInteraction(sendInteractionRequest);
} catch (ExecutionException e) {
e.printStackTrace();
}
Send Properties programmatically and ignore the response by calling the oneSendProperties
Kotlin top-level function in a Coroutine, as shown below:
Kotlin
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendProperties
// rest of imports
scope.launch {
oneSendProperties {
properties = mapOf("keyA" to "valueA", "keyB" to "valueB")
}
}
To capture errors, set the throwErrors
parameter to true
and wrap the method in a try/catch
block, as shown below:
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendProperties
import com.thunderhead.mobile.responsetypes.OneAPIError
import com.thunderhead.mobile.responsetypes.OneSDKError
// rest of imports
scope.launch {
try {
oneSendProperties(throwErrors = true) {
properties = mapOf("keyA" to "valueA", "keyB" to "valueB")
}
} catch (error: OneSDKError) {
Log.e(TAG, "SDK Error: ${error.errorMessage}")
} catch (error: OneAPIError) {
Log.e(TAG, "Api Error: ${error.errorMessage}")
}
}
Send Properties programmatically and ignore the response by calling the One.sendProperties
Java method, as shown below:
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneRequest;
// rest of imports
final Map<String, String> properties = new HashMap<>();
properties.put("keyA", "valueA");
properties.put("keyB", "valueB");
final OneRequest sendPropertiesRequest = new OneRequest.Builder()
.properties(properties)
.build();
try {
One.sendProperties(sendPropertiesRequest);
} catch (ExecutionException e) {
e.printStackTrace();
}
Note:
- Sends a
PUT
request to Thunderhead ONE or Salesforce Interaction Studio. - Properties sent to a base Touchpoint are captured under a base (
/
) or wildcard (/*
) Interaction in Thunderhead ONE or Salesforce Interaction Studio. The Attribute Capture Point API name in Thunderhead ONE, or Salesforce Interaction Studio, must match the key name sent above.
Send a response code by calling the oneSendResponseCode
Kotlin top-level function, with the response code and the corresponding Interaction path as parameters, as shown below:
Kotlin
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendResponseCode
// rest of imports
scope.launch {
oneSendResponseCode {
interactionPath = OneInteractionPath(URI("/interactionPath"))
code = OneResponseCode("code")
}
}
To capture errors, set the throwErrors
parameter to true
and wrap the method in a try/catch
block, as shown below:
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneSendResponseCode
import com.thunderhead.mobile.responsetypes.OneAPIError
import com.thunderhead.mobile.responsetypes.OneSDKError
// rest of imports
scope.launch {
try {
oneSendResponseCode(throwErrors = true) {
interactionPath = OneInteractionPath(URI("/interactionPath"))
code = OneResponseCode("code")
}
} catch (error: OneSDKError) {
Log.e(TAG, "SDK Error: ${error.errorMessage}")
} catch (error: OneAPIError) {
Log.e(TAG, "Api Error: ${error.errorMessage}")
}
}
Send a response code, by calling the One.sendResponseCode
Java method, with the response code
and the corresponding interaction path as parameters, as shown below:
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneResponseCodeRequest;
import com.thunderhead.mobile.interactions.OneInteractionPath;
import com.thunderhead.mobile.interactions.OneResponseCode;
final OneResponseCodeRequest responseCodeRequest = new OneResponseCodeRequest.Builder()
.responseCode(new OneResponseCode("code"))
.interactionPath(new OneInteractionPath(URI.create("/interactionPath")))
.build();
try {
One.sendResponseCode(responseCodeRequest);
} catch (ExecutionException e) {
e.printStackTrace();
}
Note:
- Use this method when you are displaying optimizations programmatically and need to capture the user's response.
- Sends a
PUT
request to Thunderhead ONE or Salesforce Interaction Studio. - When sending Interaction requests programmatically, please ensure the Interaction starts with a
/
and contains only letters, numbers, and/or dashes.
The Thunderhead SDK considers Android Activities and Fragments as Interactions. When configured correctly the SDK will automatically send an Interaction request to ONE and process the response which may contain points (optimizations, capture, etc). If desired, you can be notified of these automatic Interactions to take additional action on each Interaction request, by using the automatic Interaction callback API.
Notes
- It is incumbent on you to then process the response in order for the Thunderhead SDK to perform automatic capture and optimization.
- Assigning a manual/custom Interaction to a view should be done before setting an automatic Interaction callback.
- If you set a callback for an automatically triggered Interaction, you are advised to remove that callback as soon as it is no longer needed under your Activity's
onStop
method.
Kotlin
import com.thunderhead.mobile.process
import com.thunderhead.mobile.setAutomaticInteractionCallback
import com.thunderhead.mobile.removeAutomaticInteractionCallback
// rest of imports
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onStart() {
super.onStart()
setAutomaticInteractionCallback {
onError { error ->
Log.e(TAG, "SDK Error", error)
}
onFailure { error ->
Log.e(TAG, "API Error", error)
}
onSuccess { response ->
Log.d(TAG, "Success: ${response.tid}")
// Do something with response
response.process()
}
}
}
override fun onStop() {
super.onStop()
removeAutomaticInteractionCallback()
}
companion object {
const val TAG = "MainActivity"
}
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneCallback;
import com.thunderhead.mobile.responsetypes.OneAPIError;
import com.thunderhead.mobile.responsetypes.OneResponse;
import com.thunderhead.mobile.responsetypes.OneSDKError;
// rest of imports
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
One.setAutomaticInteractionCallback(this, new OneCallback() {
@Override
public void onError(@NotNull OneSDKError error) {
Log.e(TAG, "SDK Error", error);
}
@Override
public void onFailure(@NotNull OneAPIError error) {
Log.e(TAG, "API Error", error);
}
@Override
public void onSuccess(@NotNull OneResponse response) {
Log.d(TAG, response.getTid());
// Do something with response
One.processResponse(response);
}
});
}
@Override
protected void onStop() {
super.onStop();
One.removeAutomaticInteractionCallback(this);
}
}
Kotlin
import com.thunderhead.mobile.process
import com.thunderhead.mobile.setAutomaticInteractionCallback
import com.thunderhead.mobile.removeAutomaticInteractionCallback
// rest of imports
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, TestFragment())
.setReorderingAllowed(true)
.commit()
}
}
override fun onStart() {
super.onStart()
supportFragmentManager
.findFragmentById(R.id.fragment_container)
?.setAutomaticInteractionCallback {
onError { error ->
Log.e(TAG, "SDK Error", error)
}
onFailure { error ->
Log.e(TAG, "API Error", error)
}
onSuccess { response ->
Log.d(TAG, "Success: ${response.tid}")
// Do something with response
response.process()
}
}
}
override fun onStop() {
super.onStop()
supportFragmentManager
.findFragmentById(R.id.fragment_container)
?.removeAutomaticInteractionCallback()
}
class TestFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = inflater.inflate(R.layout.fragment_test, container, false)
}
companion object {
const val TAG = "MainActivity"
}
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneCallback;
import com.thunderhead.mobile.responsetypes.OneAPIError;
import com.thunderhead.mobile.responsetypes.OneResponse;
import com.thunderhead.mobile.responsetypes.OneSDKError;
// rest of imports
public class MainActivity extends FragmentActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_container, new TestFragment());
fragmentTransaction.commit();
}
}
@Override
protected void onStart() {
super.onStart();
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
One.setAutomaticInteractionCallback(fragment, new OneCallback() {
@Override
@Override
public void onError(@NotNull OneSDKError error) {
Log.e(TAG, "SDK Error", error);
}
@Override
public void onFailure(@NotNull OneAPIError error) {
Log.e(TAG, "API Error", error);
}
public void onSuccess(@NotNull OneResponse response) {
Log.d(TAG, response.getTid());
// Do something with response
One.processResponse(response);
}
});
}
@Override
protected void onStop() {
super.onStop();
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_container);
One.removeAutomaticInteractionCallback(fragment);
}
}
Kotlin
import com.thunderhead.mobile.oneSetAutomaticInteractionCallback
import com.thunderhead.mobile.interactions.OneInteractionPath
import com.thunderhead.mobile.oneRemoveAutomaticInteractionCallback
import com.thunderhead.mobile.process
// rest of imports
oneSetAutomaticInteractionCallback(OneInteractionPath(URI("/ManualInteraction"))) {
onError { error ->
Log.e(TAG, "SDK Error", error)
}
onFailure { error ->
Log.e(TAG, "API Error", error)
}
onSuccess { response ->
Log.d(TAG, "Success: ${response.tid}")
// Do something with response
response.process()
}
}
override fun onStop() {
super.onStop()
oneRemoveAutomaticInteractionCallback(OneInteractionPath(URI("/ManualInteraction")))
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.interactions.OneInteractionPath;
import com.thunderhead.mobile.responsetypes.OneAPIError;
import com.thunderhead.mobile.responsetypes.OneResponse;
import com.thunderhead.mobile.responsetypes.OneSDKError;
// rest of imports
One.setAutomaticInteractionCallback(new OneInteractionPath(URI.create("/ManualInteraction")), new OneCallback() {
@Override
public void onFailure(@NotNull OneAPIError error) {
Log.e(TAG, "ApiError", error);
}
@Override
public void onError(@NotNull OneSDKError error) {
Log.e(TAG, "SdkError", error);
}
@Override
public void onSuccess(@NotNull OneResponse response) {
Log.d(TAG, "Success: ${response.tid}");
// Do something with response
One.processResponse(response);
}
});
@Override
protected void onStop() {
super.onStop();
One.removeAutomaticInteractionCallback(new OneInteractionPath(URI.create("/ManualInteraction")));
}
The response can be passed to the processResponse
method, as shown above. By calling this method the response is returned to the SDK to process, attaching any captures, trackers, and/or optimizations to the Interaction.
Explicitly define a view as an Interaction by calling the assignInteractionPath
Kotlin extension function
or the One.assignInteractionPath
Java method with a valid desired Interaction path, as shown below:
Kotlin
import com.thunderhead.mobile.assignInteractionPath
import com.thunderhead.mobile.interactions.OneInteractionPath
findViewById<LinearLayout>(R.id.linear_layout)
.assignInteractionPath(OneInteractionPath(URI("/viewAsInteraction")))
Java
LinearLayout linearLayout = findViewById<LinearLayout>(R.id.linear_layout);
final OneInteractionPathAssignment oneInteractionPathAssignment = new OneInteractionPathAssignment.Builder()
.view(linearLayout)
.interactionPath(new OneInteractionPath(URI.create("/viewAsInteraction")))
.build();
One.assignInteractionPath(oneInteractionPathAssignment);
This can be useful in the following cases:
- If an activity with the same layout implements generic functionality and is used to represent various Interactions within the same application. For example, you may have a list view, that is reused across the application to display branch locations in one use case and cash point locations in a second use case.
Kotlin
import com.thunderhead.mobile.assignInteractionPath
import com.thunderhead.mobile.interactions.OneInteractionPath
// rest of imports
class LocationsList : ListActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val interactionPath = if (presenterType == CASH_POINT_LOCATION) {
"/cashPointList"
} else {
"/branchList"
}
getListView().assignInteractionPath(OneInteractionPath(URI(interactionPath)))
}
}
Java
public class LocationsList extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final OneInteractionPathAssignment.Builder assignmentBuilder =
new OneInteractionPathAssignment.Builder()
.view(getListView());
if (presenterType == CASH_POINT_LOCATION) {
assignmentBuilder
.interactionPath(new OneInteractionPath(URI.create("/cashPointList")));
} else {
assignmentBuilder
.interactionPath(new OneInteractionPath(URI.create("/branchList")));
}
One.assignInteractionPath(assignmentBuilder.build());
}
}
- If a fragment implements generic functionality and may represent various Interactions. For example, in one case it may show a screen containing laptops and, in another, a screen containing cameras.
Java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.products_tiles_view, container, false);
final OneInteractionPathAssignment.Builder assignmentBuilder =
new OneInteractionPathAssignment.Builder()
.view(view);
if (category == Product.Category.LAPTOP) {
assignmentBuilder
.interactionPath(new OneInteractionPath(URI.create("/laptopsList")));
} else if (category == Product.Category.CAMERA) {
assignmentBuilder
.interactionPath(new OneInteractionPath(URI.create("/camerasList")));
}
One.assignInteractionPath(assignmentBuilder.build());
return view;
}
- If an Interaction is represented by a custom view.
Kotlin
import com.thunderhead.mobile.assignInteractionPath
import com.thunderhead.mobile.interactions.OneInteractionPath
// rest of imports
fun showVariants() {
if (varientsView == null) {
variantsView = inflater.inflate(R.layout.variants_slide, mainPanelView, false)
variantsView.assignInteractionPath(OneInteractionPath(URI("/variants")))
}
}
Java
void showVariants() {
if (variantsView == null) {
variantsView = inflater.inflate(R.layout.variants_slide, mainPaneView, false);
final OneInteractionPathAssignment assignment =
new OneInteractionPathAssignment.Builder()
.view(variantsView)
.interactionPath(new OneInteractionPath(URI.create("/variants")))
.build();
One.assignInteractionPath(assignment);
}
}
The SDK appends a one-tid
URL
parameter to all links opened from a mobile app. To limit this behaviour, and allow the SDK to append a one-tid
URL
parameter only to a specific set of links. Include that set of links by assigning oneIdentityTransferIncludeList
top level property or One.setIdentityTransferIncludeList
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.oneIdentityTransferIncludeList
import com.thunderhead.mobile.identitytransfer.OneIdentityTransferUriMatcher
// rest of imports
// This example shows how to include links for specific uri(s) which do have SSL protocols:
// https://www.google.com and https://www.uber.com
oneIdentityTransferIncludeList = setOf(
OneIdentityTransferUriMatcher.ExactMatch(URI("https://www.google.com")),
OneIdentityTransferUriMatcher.ExactMatch(URI("https://www.uber.com"))
)
// This example shows how to include the sub domains for wikipedia.org but not wikipedia.org directly.
oneIdentityTransferIncludeList = setOf(
OneIdentityTransferUriMatcher.RegexMatch("https://(simple|en)\\.wikipedia\\.org".toRegex())
)
// This example shows how to retrieve the include list currently in use
val includeList = oneIdentityTransferIncludeList
Java
// This example shows how to include links for specific uri(s) which do have SSL protocols:
// https://www.google.com and https://www.uber.com
Set<OneIdentityTransferUriMatcher> includeList = new HashSet<>();
try {
includeList.add(new OneIdentityTransferUriMatcher.ExactMatch(URI.create("https://www.google.com")));
includeList.add(new OneIdentityTransferUriMatcher.ExactMatch(URI.create("https://www.uber.com")));
} catch (Exception e) {
e.printStackTrace();
}
One.setIdentityTransferIncludeList(includeList);
// This example shows how to include the sub domains for wikipedia.org but not wikipedia.org directly.
Set<OneIdentityTransferUriMatcher> includeList = new HashSet<>();
try {
final Regex wikipediaSubDomains = new Regex("https://(simple|en)\\.wikipedia\\.org");
includeList.add(new OneIdentityTransferUriMatcher.RegexMatch(wikipediaSubDomains));
} catch (Exception e) {
e.printStackTrace();
}
One.setIdentityTransferIncludeList(includeList);
// This example shows how to retrieve the include list currently in use
Set<OneIdentityTransferUriMatcher> includeList = One.getIdentityTransferIncludeList();
Note:
- When a link is included, a
one-tid
is appended only to the allowed links, superseding any excluded identity transfer links. - Setting the allowed list to an
empty list
clears the existing list.
The SDK appends a one-tid
URL
parameter to all links opened from a mobile app. To limit this behaviour, and allow the SDK to append a one-tid
URL
parameter only to a specific set of links. Exclude the links by assigning list to the oneIdentityTransferExcludeList
top level property or One.setIdentityTransferExcludeList
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.oneIdentityTransferExcludeList
import com.thunderhead.mobile.identitytransfer.OneIdentityTransferUriMatcher
// rest of imports
// This example shows how to exclude links for specific uri(s) which do not have SSL protocols:
// www.google.com and www.uber.com
oneIdentityTransferExcludeList = setOf(
OneIdentityTransferUriMatcher.ExactMatch(URI("http://www.google.com")),
OneIdentityTransferUriMatcher.ExactMatch(URI("http://www.uber.com"))
)
// This example shows how to exclude the sub domains for wikipedia.org but not wikipedia.org directly.
oneIdentityTransferExcludeList = setOf(
OneIdentityTransferUriMatcher.RegexMatch("https://(simple|en)\\.wikipedia\\.org".toRegex())
)
// This example shows how to retrieve the exclude list currently in use
val excludeList = oneIdentityTransferExcludeList
Java
// This example shows how to exclude links for specific uri(s) which do not have SSL protocols:
// www.google.com and www.uber.com
Set<OneIdentityTransferUriMatcher> excludeList = new HashSet<>();
try {
excludeList.add(new OneIdentityTransferUriMatcher.ExactMatch(URI.create("http://www.google.com")));
excludeList.add(new OneIdentityTransferUriMatcher.ExactMatch(URI.create("http://www.uber.com")));
} catch (Exception e) {
e.printStackTrace();
}
One.setIdentityTransferExcludeList(excludeList);
// This example shows how to exclude the sub domains for wikipedia.org but not wikipedia.org directly.
Set<OneIdentityTransferUriMatcher> excludeList = new HashSet<>();
try {
final Regex wikipediaSubDomains = new Regex("https://(simple|en)\\.wikipedia\\.org");
excludeList.add(new OneIdentityTransferUriMatcher.RegexMatch(wikipediaSubDomains));
} catch (Exception e) {
e.printStackTrace();
}
One.setIdentityTransferExcludeList(excludeList);
// This example shows how to retrieve the exclude list currently in use
Set<OneIdentityTransferUriMatcher> excludeList = One.getIdentityTransferExcludeList();
Note:
- If a link is excluded, a
one-tid
is appended to all links other than the excluded link, but is superseded by any included identity transfer links. - Setting the exclude list to an
empty list
clears the existing list.
If the Orchestration Plugin is enabled, the SDK adds a one-tid
as a URL
query parameter to web links opened in WebView
, CustomTabs
and external browsers (via Intent
).
To disable this functionality, call the oneConfigureIdentityTransfer
Kotlin top-level function or the One.setIdentityTransferConfiguration
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.oneConfigureIdentityTransfer
oneConfigureIdentityTransfer {
disableIdentityTransfer = true
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.identitytransfer.OneIdentityTransferConfiguration;
final OneIdentityTransferConfiguration identityTransferConfiguration =
new OneIdentityTransferConfiguration.Builder()
.disableIdentityTransfer(true)
.build();
One.setIdentityTransferConfiguration(identityTransferConfiguration);
Note:
- This also disables the ability to pick up parameters, automatically, from deep links that open the app and prevents the SDK from adding a
one-tid
as aURL
query parameter to web links opened from the app, resulting in the customer's identity not being transferred as they move across channels.
If you have disabled automatic identity transfer, you can still send all URL
parameters received as part of a deep link by calling the java.net.URI.processDeepLink
or android.net.Uri.processDeepLink
Kotlin extension function or the One.processDeepLink
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.processDeepLink
// rest of imports
URI("myapp://MainActivity?customerKey=1").processDeepLink()
Uri.parse("myapp://MainActivity?customerKey=1").processDeepLink()
Java
One.processDeepLink(URI.create("myapp://MainActivity?customerKey=1"));
Note:
- Sends a
PUT
request to Thunderhead ONE or Salesforce Interaction Studio.
If you have disabled automatic identity transfer, you can still create a URL
with a one-tid
parameter to use in the app programmatically, by calling the java.net.URL.generateIdentityTransferUrl()
Kotlin extension function or the One.generateIdentityTransferUrl(URL)
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.generateIdentityTransferUrl
// rest of imports
val urlWithOneTid = URL("http://mysite.com").generateIdentityTransferUrl()
Java
URL url = new URL("http://mysite.com");
URL urlWithOneTid = One.generateIdentityTransferUrl(url);
Once you have the urlWithOneTid
, pass this into the method which handles the opening of the URL
.
Note: The above methods return null
if the SDK is not configured or is in Admin Mode.
If you have disabled automatic identity transfer, you can still create an android.net.Uri
or java.net.URI
with a one-tid
parameter to use in the app programmatically, by calling the java.net.URI.generateIdentityTransferUri()
or android.net.Uri.generateIdentityTransferUri()
Kotlin extension functions or the One.generateIdentityTransferUri(Uri|URI)
Java method as shown below:
Kotlin
import com.thunderhead.mobile.generateIdentityTransferUri
// rest of imports
val androidUriWithOneTid = Uri.parse("http://mysite.com").generateIdentityTransferUri()
val javaUriWithOneTid = URI("http://mysite.com").generateIdentityTransferUri()
Java
Uri uri = Uri.parse("http://mysite.com");
Uri uriWithOneTid = One.generateIdentityTransferUri(uri);
URI javaUri = URI.create("http://mysite.com");
URI javaUriWithOneTid = One.generateIdentityTransferUri(javaUri);
Once you have the uriWithOneTid
, pass this into the method which handles the opening of the Uri
.
Note: The above methods return null
if the SDK is not configured or is in Admin Mode.
If the Orchestration Plugin is enabled, the SDK automatically sends an Interaction request to /one-click
when a URL
is opened in a WebView
, CustomTab
or external browser to facilitate last click attribution.
To disable this functionality, use the code below:
Kotlin
import com.thunderhead.mobile.oneConfigureCodelessInteractionTracking
oneConfigureCodelessInteractionTracking {
disableOutboundLinkTracking = true
}
Java
import com.thunderhead.mobile.One;
import com.thunderhead.mobile.codeless.OneCodelessInteractionTrackingConfiguration;
final OneCodelessInteractionTrackingConfiguration codelessInteractionTrackingConfiguration =
new OneCodelessInteractionTrackingConfiguration.Builder()
.disableOutboundLinkTracking(true)
.build();
One.setCodelessInteractionTrackingConfiguration(codelessInteractionTrackingConfiguration);
If you have disabled automatic outbound link tracking, you can still track a URL
or Uri
, by calling the java.net.URI.sendInteractionForOutboundLink
or android.net.Uri.sendInteractionForOutboundLink
Kotlin extension functions or the One.sendInteractionForOutboundLink
Java method, as shown below:
Kotlin
import com.thunderhead.mobile.sendInteractionForOutboundLink
URI("https://www.yourfullurl.com/").sendInteractionForOutboundLink()
Uri.parse("https://www.yourfullurl.com/").sendInteractionForOutboundLink()
URL("https://www.yourfullurl.com/").sendInteractionForOutboundLink()
Java
// URL example
try {
One.sendInteractionForOutboundLink(new URL("https://www.yourfullurl.com/"));
} catch (MalformedURLException e) {
e.printStackTrace();
}
// URI example
try {
One.sendInteractionForOutboundLink(Uri.parse("https://www.yourfullurl.com/"));
} catch (Exception e) {
e.printStackTrace();
}
Pass the URL
or Uri
, to send an Interaction request to /one-click
using the same logic as is available automatically.
Note:
- Sends a
POST
request to Thunderhead ONE or Salesforce Interaction Studio. - Set up the
/one-click
Interaction request in Thunderhead ONE or Salesforce Interaction Studio, to capture the appropriate attributes and activity.
To send a location object, pass the location object as a parameter to the updateLocation
method, as shown below:
One.processLocation(location);
Use the LocationListener
callback method to call updateLocation
, as shown below:
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
One.getInstance(Activity.this).updateLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras){
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
};
To get the current tid
used by the SDK, call:
Kotlin
oneGetTid()
Java
One.getTid();
Note:
- Returns the
tid
assigned to the current user as aString
. - Retrieving the current
tid
can be useful if you want to monitor the current user in Thunderhead ONE or Salesforce Interaction Studio, or if you need to pass the identity of the current user to another system that sends data to Thunderhead ONE or Salesforce Interaction Studio.
The Thunderhead SDK for Android provides an extensible logging configuration API for debug or reporting purposes. The API can be configured to log any combination of Components (features or technical concepts such as Networking or Databases) to Log Levels (Verbose, Debug, etc). In addition, custom log writers can be added to facilitate reporting if desired (ex. sending errors to Google Console).
By default, the Thunderhead SDK for Android logs ERROR and WARN messages for ANY component. Below are examples of other logging configurations.
Example of configuring logging to VERBOSE Log Level for ANY Components of the Thunderhead SDK.
Kotlin
import com.thunderhead.mobile.logging.OneLogComponent
import com.thunderhead.mobile.logging.OneLogLevel
import com.thunderhead.mobile.oneConfigureLogging
// rest of imports
oneConfigureLogging {
levels = mutableSetOf(OneLogLevel.VERBOSE)
components = mutableSetOf(OneLogComponent.ANY)
}
Java
import com.thunderhead.mobile.logging.OneLogComponent;
import com.thunderhead.mobile.logging.OneLogLevel;
import com.thunderhead.mobile.logging.OneLoggingConfiguration;
// rest of imports
final OneLoggingConfiguration oneLoggingConfiguration = OneLoggingConfiguration.builder()
.log(OneLogLevel.VERBOSE)
.log(OneLogComponent.ANY)
.build();
One.setLoggingConfiguration(oneLoggingConfiguration);
Example of configuring logging to combination of ERROR and WARN levels for just NETWORKING and DATABASE Components of the Thunderhead SDK.
Kotlin
import com.thunderhead.mobile.logging.OneLogComponent
import com.thunderhead.mobile.logging.OneLogLevel
import com.thunderhead.mobile.oneConfigureLogging
// rest of imports
oneConfigureLogging {
levels = OneLogLevel.ERROR and OneLogLevel.WARN
components = OneLogComponent.NETWORKING and OneLogComponent.DATABASE
}
Java
import com.thunderhead.mobile.logging.OneLogComponent;
import com.thunderhead.mobile.logging.OneLogLevel;
import com.thunderhead.mobile.logging.OneLoggingConfiguration;
// rest of imports
final OneLoggingConfiguration oneLoggingConfiguration = OneLoggingConfiguration.builder()
.log(OneLogLevel.ERROR)
.log(OneLogLevel.WARN)
.log(OneLogComponent.NETWORKING)
.log(OneLogComponent.DATABASE)
.build();
One.setLoggingConfiguration(oneLoggingConfiguration);
Example of using a custom logger
Kotlin
import com.thunderhead.mobile.logging.OneLogComponent
import com.thunderhead.mobile.logging.OneLogLevel
import com.thunderhead.mobile.oneConfigureLogging
import com.thunderhead.mobile.logging.OneLogWriter
// rest of imports
oneConfigureLogging {
levels = mutableSetOf(OneLogLevel.VERBOSE)
components = OneLogComponent.NETWORKING and OneLogComponent.DATABASE
logWriters = mutableSetOf(CustomLogger())
}
// custom logger
class CustomLogger : OneLogWriter() {
override fun log(
logLevel: OneLogLevel,
component: OneLogComponent,
message: String,
throwable: Throwable?
) {
Log.d("CustomLogger", "Component: ${component.name}\nMessage: $message", throwable)
}
}
Java
import com.thunderhead.mobile.logging.OneLogComponent;
import com.thunderhead.mobile.logging.OneLogLevel;
import com.thunderhead.mobile.logging.OneLoggingConfiguration;
import com.thunderhead.mobile.logging.OneLogWriter;
// rest of imports
final OneLoggingConfiguration oneLoggingConfiguration = OneLoggingConfiguration.builder()
.log(OneLogLevel.VERBOSE)
.log(OneLogComponent.NETWORKING)
.log(OneLogComponent.DATABASE)
.logTo(new CustomLogger())
.build();
One.setLoggingConfiguration(oneLoggingConfiguration);
// custom logger
static class CustomLogger extends OneLogWriter {
@Override
public void log(
@NotNull OneLogLevel logLevel,
@NotNull OneLogComponent component,
@NotNull String message,
@Nullable Throwable throwable
) {
Log.d("CustomLogger", "Component: " + component.name() + "\nMessage: " + message, throwable);
}
}
The Thunderhead SDK performs initialization processes in an Android Content Provider which is instantiated before the Application is created. This means the log configuration API cannot be invoked before the Thunderhead SDK has finished its initialization process. To turn on logging for the initialization process of the Thunderhead SDK a meta data element must be added to the android manifest. If the metadata element is not set no logging is configured.
Metadata Info:
name
: com.thunderhead.android.InitLogLevel
value
: Comma separated list of com.thunderhead.mobile.logging.OneLogLevel
Example for logging VERBOSE and above logs:
<application>
<!--Other application elements-->
<meta-data
android:name="com.thunderhead.android.InitLogLevel"
android:value="VERBOSE" />
</application>
Recommendation
We recommend including the above metadata only in DEBUG
builds to ensure no unnecessary logging
occurs in release. Therefore, only include this metadata in the DEBUG
variant
AndroidManifest.xml
and NOT in the main AndroidManifest.xml
. To learn more about how manifests
are merged, please see the Android Documentation.
To turn off logging, pass a set to OneLogLevel and a set to OneLogComponent with the values of NONE Example of turning logging off
Kotlin
import com.thunderhead.mobile.logging.OneLogComponent
import com.thunderhead.mobile.logging.OneLogLevel
import com.thunderhead.mobile.oneConfigureLogging
// rest of imports
oneConfigureLogging {
levels = mutableSetOf(OneLogLevel.NONE)
components = mutableSetOf(OneLogComponent.NONE)
}
Java
import com.thunderhead.mobile.logging.OneLogComponent;
import com.thunderhead.mobile.logging.OneLogLevel;
import com.thunderhead.mobile.logging.OneLoggingConfiguration;
// rest of imports
final OneLoggingConfiguration oneLoggingConfiguration = OneLoggingConfiguration.builder()
.log(OneLogLevel.NONE)
.log(OneLogComponent.NONE)
.build();
One.setLoggingConfiguration(oneLoggingConfiguration);
Note:
- The
com.thunderhead.android.InitLogLevel
AndroidManifest.xml
metadata value is only honored for the Thunderhead SDK initialization process. After initialization has finished, the logging configuration reverts to a default configuration mentioned above. If more logging is desired then use the logging configuration APIs to turn on logging as shown above. - When setting a single
OneLogLevel
, the SDK will log any messages of that level and above.- The order from the bottom is: VERBOSE, DEBUG, ERROR, WARN, INFO, ASSERT
- Example: Setting VERBOSE will log all messages.
- Example: Setting INFO will log only INFO and ASSERT messages.
- When setting multiple
OneLogLevel
(s), the SDK will log only messages of those specific levels.- Example: Setting ERROR and WARN will only log message of ERROR and WARN levels and nothing else.
- When setting
OneLogComponent
to onlyANY
all components will be logged in conjunction with the log level. - When setting multiple
OneLogComponent
(s), the SDK will log only messages for those specific components.- Do not set set multiple
Components
(s) along side theANY
component. Choose only the components required or just useANY
but not both.
- Do not set set multiple
Find the current version of the SDK by calling:
Kotlin
val version = oneVersion
Java
One.getVersion();
Programmatically erase the user profile data by calling:
Kotlin
oneClearUserProfile()
Java
One.clearUserProfile();
Note:
- Removes the stored
tid
only from local storage. - For instructions on how completely remove a user's data from Thunderhead ONE or Salesforce Interaction Studio, see our API Documentation.
To completely remove the codeless identity transfer functionality for Android, make the following updates:
- Open the top-level
build.gradle
file and remove the following dependency reference.
classpath 'com.thunderhead.android:orchestration-plugin:7.0.0'
- Open the app-level
build.gradle
file and remove the following references.
apply plugin: 'com.thunderhead.android.orchestration-plugin'
For Salesforce Marketing Cloud Interaction Studio questions, please submit a support ticket via https://help.salesforce.com/home
The Thunderhead team is available 24/7 to answer any questions you have. Just email onesupport@thunderhead.com or visit our docs page for more detailed installation and usage information.