Android SDK

šŸš§

Deprecation Notice

This is a deprecation notice for our mobile SDKs email identification functionality. Identifying users using their user email will no longer be supported as a valid identifier for the Android and iOS SDKs.

Users currently identified by their email will not be affected. However, we recommend identifying all users with a unique user ID. Please note that users will still be associated with their email, which can be used for filtering and audience segmentation.

You can get started with using Taplytics on Android in minutes. Just follow the steps below:

Installation


Android Studio

  • In your moduleā€™s build.gradle, add the url to the SDK.

NOTE: In newer applications, this must also be added to your project's settings.gradle

repositories {               
    maven { url "https://github.com/taplytics/Taplytics-Android-SDK/raw/master/AndroidStudio/" }
}
repositories {
    maven { url "https://github.com/taplytics/Taplytics-Android-SDK/raw/master/AndroidStudio/" }
}
  • In your moduleā€™s build.gradle dependencies (not your project's build.gradle), compile Taplytics and its dependencies.

Note: users that are planning to use the mParticle and Taplytics integration should use RetroFit2 instead of Volley.

dependencies {                                                                  
     //Dependencies for Taplytics
     implementation 'com.taplytics.sdk:taplytics:+@aar'  

     //socket.io connections only made on debug devices.
     //To make live changes on a release build, remove the debugImplementation flag
     debugImplementation ('io.socket:socket.io-client:1.0.1') {
          exclude group: 'org.json', module: 'json'
     }

     //NOTE: You can use either Volley or Retrofit2. Do not use both if you do not have to.

     //Volley
     implementation 'com.android.volley:volley:+'

     //Retrofit2
     implementation 'com.squareup.retrofit2:retrofit:+'

 }
dependencies {                                                                  
     //Dependencies for Taplytics
     implementation 'com.taplytics.sdk:taplytics:+@aar'  

     //socket.io connections only made on debug devices.
     //To make live changes on a release build, remove the debugImplementation flag
     debugImplementation ('io.socket:socket.io-client:1.0.1') {
         exclude group: 'org.json', module: 'json'
     }

     //NOTE: You can use either Volley or Retrofit2. Do not use both if you do not have to.

     //Volley
     implementation 'com.android.volley:volley:+'

     //Retrofit2
     implementation 'com.squareup.retrofit2:retrofit:+'

 }

Import Taplytics as an AAR

If you'd like to manually add Taplytics to your build, you may download the .aar file and add it to your Android project.

Find the latest Android SDK here: https://github.com/taplytics/Taplytics-Android-SDK/tree/master/AndroidStudio/com/taplytics/sdk/taplytics

Import the .aar file to Android Studio and add or ensure the following dependencies are included in your app's build.gradle file.

dependencies {                                                                  
     //Dependencies for Taplytics
     implementation files('libs/taplytics-3.0.0.aar') // replace 3.0.0 with your version

     //socket.io connections only made on debug devices.
     //To make live changes on a release build, remove the debugImplementation flag
     debugImplementation ('io.socket:socket.io-client:1.0.1') {
          exclude group: 'org.json', module: 'json'
     }

     //NOTE: You can use either Volley or Retrofit2. Do not use both if you do not have to.

     //Volley
     implementation 'com.android.volley:volley:+'

     //Retrofit2
     implementation 'com.squareup.retrofit2:retrofit:+'

 }

Click here to read more about the recent socket dependency changes.

  • Override your Applicationā€™s onCreate() method (not your main activity) and call Taplytics.startTaplytics(). If you don't have an Application class, create one. It should look like this:
import com.taplytics.sdk.Taplytics;


public class ExampleApplication extends Application {
     @Override
     public void onCreate() {
         super.onCreate();
         Taplytics.startTaplytics(this, "YOUR TAPLYTICS SDK KEY");
     }
}
import com.taplytics.sdk.Taplytics


class ExampleApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Taplytics.startTaplytics(this, "YOUR TAPLYTICS SDK KEY")
    }
}
  • Now, add the proper permissions, and the Application class to your appā€™s AndroidManifest.xml in the Application tag.
<uses-permission android:name="android.permission.INTERNET" />
 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 <application
     android:name=".ExampleApplication"
 ...
  • To be able to connect to Taplytics on a release build, add the following intent-filter tag to the end of your MAIN activity:

First, get your Taplytics URL Scheme from your Project's Settings:

1780

Then, add it to your manifest in its own intent filter (do not merge with another intent filter).

...
          <intent-filter>
                 <action android:name="android.intent.action.VIEW"/>
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.BROWSABLE"/>
                 <data android:scheme="YOUR URL SCHEME"/>
         </intent-filter>
 </activity>

Install with Segment

The Taplytics SDK can also be installed via Segment. You can find install instructions here.


Install with mParticle

Check the Taplytics integration for the mParticle Android SDK docs here.


Initialization

Initialize the Taplytics SDK by adding the following line of code with your SDK key to your Main Application class. Taplytics can also be started with a few options to help you use it during development. See the start options section for more details.

First, the base method:

Taplytics.startTaplytics(this, "Your SDK Key");
Taplytics.startTaplytics(this, "Your SDK Key");

Or, add a map of options.

HashMap<String, Object> options = new HashMap<>();
options.put("optionName", optionValue);
Taplytics.startTaplytics(this, "Your SDK Key", options);
val options: HashMap<String, Any> = HashMap()
options["optionName"] = optionValue
Taplytics.startTaplytics(this, "Your SDK Key", options)

Important Notes

It is highly recommended to initialize Taplytics in the Applicationā€™s onCreate() for several reasons.

  1. Taplytics uses the information on how your app was launched to support some of our core features. ie. View construction/data for the Visual Editor
  2. Taplytics should also be started as early as possible to allow time for Taplytics to fetch our config and present the correct experiment and feature data before the first activity starts.
  3. Taplytics uses the application start as a signal for when the App has been activated. We use this data to accurately track sessions and activity.

Threading

Taplytics will create its own threadpool and operate off the main thread except when making visual modifications as this must occur on the UI thread.

Caching & SDK Timeouts

Built into each Taplytics client-side SDK is a caching system that saves experiment and feature flag values locally on each session. These values are saved locally for several reasons, but the two primary ones are:

  1. To keep the user experience consistent when Taplyticsā€™ servers are unreachable for any reason
  2. To minimize outbound requests to Taplytics, allowing configuration data to be used throughout a session if successfully loaded and saved

The Taplytics SDK also enables you to configure the request timeout, which specifies the time within which the Taplytics SDK must return a response. By default, the timeout value is set to 4000ms on all client-side SDKs. If the default timeout value is not appropriate for your application, you can adjust the timeout for your specific requirements within the starting parameters.

If a response isnā€™t returned within the time specified, the request ends. The Taplytics SDK will either return the last cached value saved within your device, or if there was no previously cached value, the SDK will use variable default values.

Whichever is used, cached values or defaults, the SDK will use those values for the remainder of the session. Updated values from our SDK will still attempt to download on timeouts in the background and will be cached and ready to use on the next session if any retry is successful.

Example:

HashMap<String, Object> options = new HashMap<>();
options.put("optionName", optionValue);
Taplytics.startTaplytics(Context, "SDK_KEY", options, TimeoutInMillis)
val options: HashMap<String, Any> = HashMap()
options["optionName"] = optionValue
Taplytics.startTaplytics(Context, "SDK_KEY", options, TimeoutInMillis)

Start Options

Customize the startup process and enable extra features that the Taplytics Android SDK provides.

Option NameValuesDefaultExplanation
liveUpdateboolean: true/falseset by build (enabled in debug)Disable live update to remove the border, and activity refreshing in your debug builds to test the functionality of your applications as if they were in release mode. Note that this functionality is always disabled by default in release builds. Setting liveUpdate to true on a release build will override this and force the application into debug mode.
shakeMenuboolean: true/falseset by build (enabled in debug)In your debug builds, disable the quick menu that appears when you shake your device. This menu is never present in release builds.
aggressivebooleanfalseTaplytics has the option to allow for aggressive visual changes. This means that if text or visibility is changed within your app by code outside of Taplytics, Taplytics will force the values to remain what has been set on the dashboard.
sessionMinutesint > 010If you do your own analytics alongside Taplytics, it helps to define your sessions to be the same length to reconcile your data. Set this to be the same timing interval that your app counts sessions.
turnMenuboolean: true/falsefalseIf you are doing visual testing on an emulator, or UI automation, many emulators do not have the ability to shake the device. So, to pop up the Taplytics menu on such devices, set turnMenu to true, and simply rotate the device from portrait/landscape twice in a row within 30 seconds and this menu will show.
disableBordersboolean: true/falseset by build (enabled in debug)This will entirely disable the informational borders Taplytics applies during debug mode testing. Useful to disable for UI testing. Note that this border will NOT show in release mode regardless of setting (except for on previously paired phones).
testExperimentsHashMapnullSee Testing Specific Experiments.
retrofitboolean: true/falseset by build (true if only retrofit present)Taplytics will default to using Volley if it is present. In the event that you have both enabled, you can use this flag to force the library to use retrofit instead.
trackingIdstringnullTo separate all users devices, Taplytics will use device identifiers by default as an identification tool. However, clients are able to provide their own tracking IDs to Taplytics for user devices, such as google advertising IDs. If this option is used, Taplytics will not collect any device identifiers.
loggingboolean: true/falsefalseThis will provide more verbose logging from Taplytics to help with debugging.
userBucketingboolean: true/falsefalseThis will turn on user based bucketing logic for your SDK, creating an anonymous user_id if one is not provided. Otherwise it will take the saved user_id from the device that was saved using setUserAttributes. For more information please view this doc first.

Example:

HashMap<String, Object> options = new HashMap<>();
options.put("shakeMenu", false);
options.put("sessionMinutes", 30);
options.put("logging", true);
Taplytics.startTaplytics(Context, "SDK_KEY", options)
val options: HashMap<String, Any> = HashMap()
options["shakeMenu"] = false
options["sessionMinutes"] = 30
options["logging"] = true
Taplytics.startTaplytics(Context, "SDK_KEY", options)

The Border / Shake Menu

When paired to the Taplytics dashboard on a debug build or through Advanced Pairing, a border will show around your app window. This shows which experiment and variation you are currently viewing.

You can long-press on the top of the border to switch experiments, shake your device and pick from the menu, or select an experiment from the Taplytics website to view the Visual changes made to the app.

When paired to the Taplytics dashboard, the Shake Menu appears whenever you shake the device. Alternatively, you can also use the Taplytics.showMenu() function to display the Shake Menu as well. This is useful if you're testing on environments that can't be shook or rotated.

Note: The border and shake menu will NOT normally appear on release builds.


Advanced Device Pairing

Link a device (even in release mode) to Taplytics.

NOTE: This is used only for deeplink pairing

Retrieve deeplink through Taplytics deeplink intercepted via either email or SMS device pairing. It contains your Taplytics URL scheme and device token. If you wish to intercept the deeplink and then pair the device yourself in your application's code, call this method in your app's LAUNCH activity or MAIN activity, like so:

private void handleDeepLink(Intent intent) {
    String tlDeeplink = intent.getDataString(); //example deep link: 'tl-506f596f://e10651f9ef6b'
    if (tlDeeplink == null) {
            // No deeplink found
            return;
    }
    Taplytics.deviceLink(tlDeeplink);
}
private fun handleDeepLink(intent:Intent) {
  val tlDeeplink = intent.getDataString() //example deep link: 'tl-506f596f://e10651f9ef6b'
  if (tlDeeplink == null)
  {
    return // No deeplink found
  }
  Taplytics.deviceLink(tlDeeplink)
}

Additionally, if you are using singleTop activities, you MUST handle the incoming intents in your activity like so:

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    // this handles the intents in the case where your main activity has already been created
    handleDeepLink(intent);
}
override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    // this handles the intents in the case where your main activity has already been created
    handleDeepLink(intent)
}

Do not forget to get your Taplytics URL Scheme from your Project's Settings:

1780

Then, add it to your manifest in its own intent filter:

...
             <intent-filter>
                    <action android:name="android.intent.action.VIEW"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <category android:name="android.intent.category.BROWSABLE"/>
                    <data android:scheme="YOUR URL SCHEME"/>
            </intent-filter>
    </activity>

NOTE: The socketIO dependency must be present in the release build (ie not set to debugImplementation) to pair with a release build.


Setting User Attributes

It's possible to send custom user attributes to Taplytics using a JSONObject of user info.

The possible fields are:

ParameterType
emailString
user_idString
firstnameString
lastnameString
nameString
ageNumber
genderString

You can also add anything else you would like to this JSONObject and it will also be passed to Taplytics.

An example with custom data:

JSONObject attributes = new JSONObject();
attributes.put("email", "[email protected]");
attributes.put("name", "John Doe");
attributes.put("age", 25);
attributes.put("gender", "male");
attributes.put("avatarUrl", "https://someurl.com/someavatar.png");

attributes.put("someCustomAttribute", 50);
attributes.put("paidSubscriber", true);
attributes.put("subscriptionPlan", "yearly");

Taplytics.setUserAttributes(attributes);
val attributes = JSONObject()
attributes.put("email", "[email protected]")
attributes.put("name", "John Doe")
attributes.put("age", 25)
attributes.put("gender", "male")
attributes.put("avatarUrl", "https://someurl.com/someavatar.png")

attributes.put("someCustomAttribute", 50)
attributes.put("paidSubscriber", true)
attributes.put("subscriptionPlan", "yearly")

Taplytics.setUserAttributes(attributes)

You can also attach a callback to notify you when user attributes has finished calling:

Taplytics.setUserAttributes(attributes, new TaplyticsSetUserAttributesListener() {
    @Override
    public void finishedSettingUserAttributes() {
        // Finished setting user attributes
    }
});
Taplytics.setUserAttributes(attributes) {
  // Finished setting user attributes
}

User Attributes on First Launch

User Attributes set before startTaplytics is called will be used for experiment segmentation on the first session of your app. Any attributes that are set after startTaplytics is called will not be used for experiment segmentation until the next session of your app.

// These custom data values will be used for segmentation on the first session of the app.

JSONObject attributes = new JSONObject();
attributes.put("example", 1);
Taplytics.setUserAttributes(attributes);

Taplytics.startTaplytics(this, SDK_KEY)

// These custom data values will only take effect on the second session of the app.

JSONObject attributes = new JSONObject();
attributes.put("example", 0);
Taplytics.setUserAttributes(attributes);
// These custom data values will be used for segmentation on the first session of the app.

val attributes = JSONObject()
attributes.put("example", 1);
Taplytics.setUserAttributes(attributes)

Taplytics.startTaplytics(this, SDK_KEY)

// These custom data values will only take effect on the second session of the app.

val attributes = JSONObject()
attributes.put("example", 0);
Taplytics.setUserAttributes(attributes)

Retrieving Session Info

Taplytics also offers a method to retrieve select information of what you know about a session at a given time. This method returns the user's Taplytics identifier (appUser_id) and current session id (session_id)

Taplytics.getSessionInfo(new SessionInfoRetrievedListener() {
    @Override
    public void sessionInfoRetrieved(HashMap sessionInfo) {
        //Use your Hashmap of Session Info
    }
    @Override
    public void onError(HashMap sessionInfo) {
        // Handle Error
    }
});
Taplytics.getSessionInfo(object : SessionInfoRetrievedListener {
    override fun sessionInfoRetrieved(sessionInfo: HashMap<*, *>?) {
        //Use your Hashmap of Session Info
    }
    override fun onError(sessionInfo: HashMap<*, *>?) {
        // Handle Error
    }
})

Resetting User Attributes or Logging out a User

Once a user logs out of your app, their User Attributes are no longer valid. You can reset their data by calling resetAppUser. Make sure you do not set any new user attributes until you receive the callback.

Taplytics.resetAppUser(new TaplyticsResetUserListener() {
	@Override
	public void finishedResettingUser() {
		//Finished User Reset
	}
});
Taplytics.resetAppUser {
  //Finished User Reset
}

User Opt-In / Opt-Out

Using the User Opt-In / Opt-Out APIs allows you to simplify the process to get user consent for analytics tracking and experimentation. Calling optOutUserTracking will disable all Taplytics analytics tracking and experiments, and calling optInUserTracking will re-enable all Taplytics analytics tracking and experiments. You can retrieve the current status using: hasUserOptedOutTracking.

// Opt In
Taplytics.optInUserTracking(this);

// Opt Out
Taplytics.optOutUserTracking(this);

// Check if user has opted out
Taplytics.hasUserOptedOutTracking(this, new TaplyticsHasUserOptedOutListener() {
    @Override
    public void hasUserOptedOutTracking(boolean hasOptedOut) {
        // use hasOptedOut:
        // true: user has opted out
        // false: user has opted in
    }
});
// Opt In
Taplytics.optInUserTracking(this)

// Opt Out
Taplytics.optOutUserTracking(this)

// Check if user has opted out
Taplytics.hasUserOptedOutTracking(this) { hasOptedOut ->
    // use hasOptedOut:
    // true: user has opted out
    // false: user has opted in
}

Device Identifiers

You may notice upon submitting your App to the Play Store that a warning for Sending Device Identifiers is issued and originated from Taplytics. This is only a warning and is used to notify users that the device identifier is being used by Taplytics. No action is needed at this time and Taplytics will be phasing out the device identifier in the future.

Taplytics uses the unique device identifiers to ensure that we can track a user's device consistently on the Taplytics dashboard. Additionally, Taplytics uses this ID as the main source of bucketing a user. This is a standard use case for device identifiers as outlined [here] (https://developer.android.com/training/articles/user-data-ids#common-use-cases).


Tracking Events


Automatic Events

Some events are automatically tracked by Taplytics and will appear on your dashboard. These events are:

  • App Start
  • Activity and/or Fragment load
  • Activity and/or Fragment destroy
  • Activity pause
  • App background
  • IP location tracking
  • Viewpager changes

App terminate is also tracked, but this is only true when your MAIN activity is at the bottom of your activity stack, and the user exits the app from that activity.

No changes are needed in your code for this event tracking to occur.


Receiving External Analytics

Taplytics has support for external events that come from many of your favourite analytics tools. To learn more about our integrations and what's available, visit our 3rd Party Integrations page.

Many of our integrations work out of the box and will automatically logs events to both Taplytics and your Analytics sources.


Custom Events

To log your own events, simply call:

Taplytics.logEvent("Your Event Name");
Taplytics.logEvent("Your Event Name")

You can also log events with numerical values:

Number num = 0;
Taplytics.logEvent("Your Event Name", num);
val num: Number = 0
Taplytics.logEvent("Your Event Name", num)

And with custom object data:

Number num = 0;
JSONObject customInfo = new JSONObject();
customInfo.put("some title", someValue)
Taplytics.logEvent("Your Event Name", num, customInfo);
val num = 0
val customInfo = JSONObject()
customInfo.put("some title", someValue)
Taplytics.logEvent("Your Event Name", num, customInfo)

Revenue Logging

It's also possible to log revenue.

Revenue logging is the same as event logging, only call logRevenue:

Number someRevenue = 10000000;  
Taplytics.logRevenue("Revenue Name", someRevenue);
val someRevenue = 10000000
Taplytics.logRevenue("Revenue Name", someRevenue)

And similarly, with custom object data:

Number someRevenue = 10000000;
JSONObject customInfo = new JSONObject();
customInfo.put("some rag", someValue)

Taplytics.logRevenue("Revenue Name", someRevenue, customInfo);
val someRevenue = 10000000
val customInfo = JSONObject()
customInfo.put("some rag", someValue)

Taplytics.logRevenue("Revenue Name", someRevenue, customInfo)

Disabling Automatic Tracking

You can disable automatic tracking for any of the options below by adding them to the disable array and including the array into your start options.

To disable IP Location tracking, head over to the Settings page on the Taplytics dashboard and select the checkbox to disable IP tracking.

ENUMDescription
delayloadOption to delay the loading of your main activity while Taplytics gets initial view changes ready
shakemenuA debug menu, exposing some useful items like the push token retrieval and the ability to test experiments and feature flags
eventsAll Event tracking
viewtrackingView related tracking
analyticsAnalytics event tracking
externalExternal Analytics event tracking
googleGoogle Analytics event tracking
mixpanelMixpanel Analytics event tracking
flurryFlurry Analytics event tracking
adobeAdobe Analytics event tracking
localyticsLocalytics Analytics event tracking
amplitudeAmplitude Analytics event tracking
segmentSegment Analytics event tracking
recyclerviewsRecyclerviews tracking for Visual Editor
listviewsListviews tracking for Visual Editor

For Example:

ArrayList<String> disabledFunctionality = new ArrayList<String>()
disabledFunctionality.add("google");
disabledFunctionality.add("shakemenu");
startOptions.put("disable", disabledFunctionality);
val disabledFunctionality = listOf("google", "shakemenu")
startOptions["disable"] = disabledFunctionality)

Experiments

Creating experiments is easy using Taplytics. You can either use our visual editor or create code-based experiments. You can find documentation on how to do this below.


Visual Editing

You don't have to do anything else! Once you've installed the SDK and initialized Taplytics, you can use the Taplytics dashboard to make all your visual changes. See the docs on visual editing here.

šŸ“˜

Visual Editing Support on Android

Visual Editing for native Android apps is supported on all versions of Android other than Android X. Due to changes in how Android creates visual elements in Android X visual editing is not supported at this time.


Dynamic Variables & Code Blocks

To see and modify these variables or blocks on the dashboard, the app must be launched and this code containing the variable or block must be navigated to at least once.

The code below is used to send the information of the variable or block to Taplytics, so it will appear on the dashboard.


Dynamic Variables

Taplytics variables are values in your app that are controlled by experiments. Changing the values can update the content or functionality of your app. Variables are reusable between experiments and operate in one of two modes: synchronous or asynchronous.


Synchronous

Synchronous variables are guaranteed to have the same value for the entire session and will have that value immediately after construction.

Due to the synchronous nature of the variable, if it is used before the experiments have been loaded, its value will be the default value rather than the value set for that experiment. This could taint the results of the experiment. In order to prevent this you can ensure that the experiments are loaded before using the variable. This can be done with either the delayLoad functionality, the TaplyticsExperimentsLoadedListener parameter in your startTaplytics call, or the getRunningExperimentsAndVariations call.

Synchronous variables take two parameters in its constructor:

  1. Variable name (String)
  2. Default Value

The type of the variable is defined in the first diamond brackets, and can be a String, Number, Boolean or JSON.

For example, using a variable of type String:

TaplyticsVar<String> stringVar = new TaplyticsVar<String>("some name", "default value");
val stringVar = TaplyticsVar("some name", "default value")

Then when you wish to get the value for the variable, simply call get() on the Taplytics variable:

String value = stringVar.get();
val value = stringVar.get()

Asynchronous

Asynchronous variables take care of insuring that the experiments have been loaded before returning a value. This removes any danger of tainting the results of your experiment with bad data. What comes with the insurance of using the correct value is the possibility that the value will not be set immediately. If the variable is constructed before the experiments are loaded, you won't have the correct value until the experiments have finished loading. If the experiments fail to load, then you will be given the default value, as specified in the variables constructor. For the best results with asynchronous varaibles make sure that they are constructed after the StartTaplytics call in your app's lifecycle.

Asynchronous variables take three parameters in its constructor:

  1. Variable name (String)
  2. Default Value
  3. TaplyticsVarListener

Just as for synchronous variables the type of the variable is defined in the first diamond brackets, and can be a String, Number, Boolean or JSON.

For example, using a variable of type Number:

TaplyticsVar<Number> codeVar = new TaplyticsVar<>("name", 5, new TaplyticsVarListener() {
    @Override
    public void variableUpdated(Object value) {
        // Do something with the value
    }
});
val codeVar: TaplyticsVar<Int> =
  TaplyticsVar<Int>("name", 5, TaplyticsVarListener {
    // Do something with the value
  })

When the variable's value has been updated, the listener will be called with that updated value. You can specify what you want to do with the variable inside the variableUpdated method.

Note: Default values for dynamic variables cannot be NULL. NULL values may cause default to trigger in all scenarios


Testing Dynamic Variables

When testing dynamic variables in live update mode you can change the values on the fly via the Taplytics interface and you can switch variations with the shake menu on the device.

Important Note: When testing synchronous dynamic variables you must call the constructor again to see the new value, as there are no callbacks which occur when the variable is updated with a new value.

This can be achieved by using an experiments updated listener. Here is an example for updating a TextView:

Taplytics.setTaplyticsExperimentsUpdatedListener(new TaplyticsExperimentsUpdatedListener() {
    @Override
    public void onExperimentUpdate() {
        final TaplyticsVar<String> stringVar = new TaplyticsVar<String>("stringVar", "defaultValue");
        updateText(stringVar.get());
    }
});
Taplytics.setTaplyticsExperimentsUpdatedListener {
  val stringVar = TaplyticsVar("stringVar", "defaultValue")
  updateText(stringVar.get())
}

List Current Variables

If you would like to see which variables are currently available within the session, there exists a getAllVariables method which will return an Array of VariableInfo object.

šŸš§

Warning - Debug Use Only

This method is for debugging purposes only. Please do not use this method as a means to retrieve variable values for use.

Example:

Taplytics.getAllVariables(new TaplyticsAllVarsListener() {
    @Override
    public void currentVars(Array<VariableInfo> variables) {
        // TODO: Do something with the array.
    }
});
Taplytics.getAllVariables {
  // TODO: Do something with the array.
}

The VariableInfo object contains the following properties:

class VariableInfo {
    name: String;
    value: object;
    variableType: String;
    _id: String;
}

NOTE: This function runs asynchronously, as it waits for the updated properties to load from Taplytics servers.


Code Blocks

Similar to Dynamic Variables, Taplytics has an option for 'Code Blocks'. Code blocks are linked to Experiments through the Taplytics website very much the same way that Dynamic Variables are, and will be executed based on the configuration of the experiment through the Taplytics website. A Code Block is a callback that can be enabled or disabled depending on the variation. If enabled, the code within the callback will be executed. If disabled, the variation will not get the callback.

A Code Block can be used alongside as many other Code Blocks as you would like to determine a combination that yields the best results. Perhaps there are three different Code Blocks on one activity. This means there could be 8 different combinations of Code Blocks being enabled / disabled on that activity if you'd like.

For example:

Taplytics.runCodeBlock("name", new CodeBlockListener() {
    @Override
    public void run() {
        // Put your code here!
    }
});
Taplytics.runCodeBlock("name") {
  // Put your code here!
}

By default, a code block will not run unless enabled on the Taplytics Dashboard. It must be enabled for a Variation before it will run.


Testing Specific Experiments

To test/QA specific experiment and variation combinations, add a map to the Taplytics start options containing the experiments and variations you wish to test. The keys should be the experiment names, and values of variation names (or baseline).

For example:

HashMap<String, Object> startOptions = new HashMap<>();
HashMap testExperiments = new HashMap();
testExperiments.put("Big Experiment", "Variation 2");
startOptions.put("testExperiments", testExperiments);

Taplytics.startTaplytics(this, SDK_KEY, startOptions);
val startOptions: HashMap<String, Any> = HashMap()
val testExperiments = HashMap<Any, Any>()
testExperiments["Big Experiment"] = "Variation 2"
startOptions["testExperiments"] = testExperiments

Taplytics.startTaplytics(this, SDK_KEY, startOptions)

List Running Experiments

If you would like to see which variations and experiments are running on a given device, there exists a getRunningExperimentsAndVariations(TaplyticsRunningExperimentsListener listener) function which provides a callback with a map of the current experiments and their running variation. An example:

Taplytics.getRunningExperimentsAndVariations(new TaplyticsRunningExperimentsListener() {
    @Override
    public void runningExperimentsAndVariation(Map<String, String> experimentsAndVariations) {
        // TODO: Do something with the map.
    }
});
Taplytics.getRunningExperimentsAndVariations {
  // TODO: Do something with the map.
}

NOTE: This function runs asynchronously, as it waits for the updated properties to load from Taplytics' servers before returning the running experiments.

If you want to see when the experiments have been loaded by Taplytics, you can add a TaplyticsExperimentLoadedListener to your startTaplytics call. For example:

Taplytics.startTaplytics(this, "YOUR SDK KEY", null, new TaplyticsExperimentsLoadedListener() {
	@Override
	public void loaded() {
		//TODO: Do something now that experiments are loaded
	}
});
Taplytics.startTaplytics(
  this,
  "YOUR SDK KEY",
  null
) {
  //TODO: Do something now that experiments are loaded
}

Feature Flags

Taplytics feature flags operate in synchronous mode. Synchronous feature flags are guaranteed to have the same value for the entire session and will have that value immediately after construction.

Due to the synchronous nature of feature flags, if it is used before the feature flags have been loaded from Taplytics servers (for example on the first launch of your app), it will default to as if the feature flag is not present. In order to prevent this you can ensure that the feature flag is loaded before using the feature flag. This can be done with either the delayLoad functionality, the TaplyticsExperimentsLoadedListener parameter in your startTaplytics call, or the getRunningExperimentsAndVariations call:

if (Taplytics.featureFlagEnabled("featureFlagKey")) {
    //Put feature code here, or launch feature from here
}
if (Taplytics.featureFlagEnabled("featureFlagKey")) {
    //Put feature code here, or launch feature from here
}

Setting a Default value

In the event that Taplytics was unable to load, Taplytics will by default return FALSE when using featureFlagEnabled. To prevent this behavour and instead default a feature to being ON in cases of no network, pass in the desired default behavior.

WARNING: Setting the Feature Flag default to true means that users will always receive the Feature Flag, even when it has been disabled.

if (Taplytics.featureFlagEnabled("featureFlagKey", true)) {
    //Put feature code here, or launch feature from here
}
if (Taplytics.featureFlagEnabled("featureFlagKey", true)) {
    //Put feature code here, or launch feature from here
}

Running Feature Flags

If you would like to see which feature flags are running on a given device, there exists a getRunningFeatureFlags(TaplyticsRunningFeatureFlagsListener listener) function which provides a callback with a map of the current feature flags. An example:

Taplytics.getRunningFeatureFlags(new TaplyticsRunningFeatureFlagsListener() {
    @Override
    public void runningFeatureFlags(Map<String, String> featureFlags) {
        // TODO: Do something with the map.
    }
});
Taplytics.getRunningFeatureFlags {
    // TODO: Do something with the map.
}

Dialogs

Taplytics supports editing elements on dialogFragments (not dialogs). To do this properly, you must use a fragmentTransaction to add the fragment to the backstack. The tag used here should be the same as the tag used to show the fragment, like so:

FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.show(someDialog);
fragmentTransaction.addToBackStack("fragment_some_dialog");
someDialog.show(fragmentTransaction, "fragment_some_dialog");
val fragmentTransaction: FragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.show(someDialog)
fragmentTransaction.addToBackStack("fragment_some_dialog")
someDialog.show(fragmentTransaction, "fragment_some_dialog")

Taplytics tracks the appearance/disappearance of the dialog via the backstack manager, which is why it needs to be sent there. The tag is necessary to confirm that the visual edits are being applied to the correct fragment.

This only works with dialogFragments as normal Dialogs do not have any unique identifying tags.

NOTE: dialogFragments exist on an entirely different view hierarchy than traditional view elements. They exist within their own window and have an entirely different viewRoot than the rest of your application. This makes changes on dialogs very difficult, and this feature is not 100% guaranteed to work for all dialogs.


Delay Load

Taplytics has the option to delay the loading of your main activity while Taplytics gets initial view changes ready. Keep in mind that this initial load will only take some time the very first time, after that, these changes will be saved to disk and will likely not need a delay.

There are two methods to do this, use both at the start of your java onCreate() after java setContentView():


Delay Load With Image

In this instance, Taplytics takes care of the loading for you. Taplytics creates a splash screen with the provided image. The image will fade automatically after the given time, or when Taplytics has successfully loaded visual changes on the provided activity.

Method: Taplytics.delayLoad(Activity activity, Drawable image, int maxTime) and Taplytics.delayLoad(Activity activity, Drawable image, int maxTime, int minTime)

  • Activity: the activity (typically main activity) that will be covered in a splash image.
  • Image: A Drawable image that will be the splash screen.
  • maxTime: Regardless of the results of Taplytics, the image will fade after this time. Milliseconds.
  • minTime: Sometimes Taplytics loads things really fast, and this might make the image show only for a short amount of time. To keep this from happening, there is an optional minimum time option. Regardless of Taplytics loading experiments, the delayLoad won't finish until after this minimum time. Milliseconds.

Example:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        Taplytics.delayLoad(this, getResources().getDrawable(R.drawable.image5), 2000);
        ...
override fun onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.main_layout);

  Taplytics.delayLoad(this, getResources().getDrawable(R.drawable.image5), 2000);
  ...

With a 1 second minimum time

Taplytics.delayLoad(this, getResources().getDrawable(R.drawable.image5), 2000, 1000);
...
Taplytics.delayLoad(this, getResources().getDrawable(R.drawable.image5), 2000, 1000);
...

Delay Load with Callbacks

In this instance, Taplytics provides callbacks when the delay load should begin, and when the delay load ends. The callback will also return after the provided timeout time has been reached. This provides you the ability to show a splashscreen that is more than just a simple image.

Method: Taplytics.delayLoad(int maxTime, TaplyticsDelayLoadListener listener) and Taplytics.delayLoad(int maxTime, int minTime, TaplyticsDelayLoadListener listener)

  • maxTime: Regardless of the results of Taplytics, this callback will be triggered if this time is reached.
  • minTime: Sometimes Taplytics loads things really fast, and this might make the behavior of the callback undesirable. To keep this from happening, there is an optional minimum time option. Regardless of Taplytics loading experiments, the delayLoad won't finish until after this minimum time.
  • Listener: This listener will provide the necessary callbacks.

Example:

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);

        Taplytics.delayLoad(2000, new TaplyticsDelayLoadListener() {
                @Override
                public void startDelay() {
                        //Start delaying!
                }

                @Override
                public void delayComplete() {
                        //Loading completed, or the given time has been reached. Insert your code here.
                }
        });
        ...
protected fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
    Taplytics.delayLoad(2000, object : TaplyticsDelayLoadListener {
        override fun startDelay() {
            //Start delaying!
        }

        override fun delayComplete() {
            //Loading completed, or the given time has been reached. Insert your code here.
        }
    })
    ...
}

With a 1 second minimum time:

Taplytics.delayLoad(2000, 1000, ...
Taplytics.delayLoad(2000, 1000, ...


Sessions

By default, Taplytics defines a session as when a user is using the app with less than 10 minutes of inactivity. If the app has been backgrounded for 10 minutes, the next time the user opens the app it will be considered a new session. Similarly, if the app is entirely force closed, the next time the app is opened, it will be considered a new session.


StartNewSession

To manually force a new user session (ex: A user has logged in / out), there exists Taplytics.startNewSession

If there is an internet connection, a new session will be created, and new experiments/variations will be fetched from Taplytics if they exist.

It can be used as follows:

Taplytics.startNewSession(new TaplyticsNewSessionListener() {
  @Override
  public void onNewSession() {
  	// New session here! Only returns if successful.
  }

  public void onError() {
  	// No new session here! Only returns if unsuccessful.
  }
});
Taplytics.startNewSession(object : TaplyticsNewSessionListener {
  override fun onNewSession() {
      // New session here! Only returns if successful.
  }

  override fun onError() {
      // No new session here! Only returns if unsuccessful.
  }
})

Session Listener

To keep track of when Taplytics defines a new session, use a TaplyticsNewSessionListener as follows.

Taplytics.setTaplyticsNewSessionListener(new TaplyticsNewSessionListener() {
	@Override
	public void onNewSession() {
		//We are in a new session
	}

	public void onError() {
		//We are not in a new session
	}
});
Taplytics.setTaplyticsNewSessionListener(object : TaplyticsNewSessionListener {
	override fun onNewSession() {
		//We are in a new session
	}

	override fun onError() {
		//We are not in a new session
	}
})

Note that this is NOT called the first time Taplytics loads, only on subsequent sessions


Push Notifications

Setting up Push Notifications using Taplytics is simple. Follow the steps below to get started.

Note: Google has changed the way push notifications work from using GCM to FCM, migrate ASAP!


1. Setup

Set up your Firebase certificate on Taplytics by following these docs.

Android Studio

Follow these instructions to add firebase to your project. Then, add the following to your dependencies in your build.gradle:

implementation 'com.google.firebase:firebase-messaging:17.+'
implementation 'com.google.firebase:firebase-core:16.0.8'

On firebase-messaging versions above 22.0.0, include the firebase-iid dependency.

implementation 'com.google.firebase:firebase-messaging:22.0.0'
implementation 'com.google.firebase:firebase-iid:21.1.0'

Add the following to the end of your build.gradle file if you haven't already:

apply plugin: 'com.google.gms.google-services'

Android Manifest

If you wish to use Push Notifications on Taplytics, you must add the following to your AndroidManifest.xml file under the application tag:

<service
    android:name="com.taplytics.sdk.fcm.TLFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Other notes

  • Keep in mind Firebase is supported only in API level 14 and up
  • Minimum version of firebase-messaging support is 17.0.0
  • Minimum version of firebase-core support is 16.0.8
  • Minimum version of Taplytics Android SDK is 2.1.0

In order to set the notification icon you must add a meta-tag to your manifest specifying the drawable you want to use as the icon:

<meta-data android:name="com.taplytics.sdk.notification_icon"
            android:resource="@drawable/notification_icon"/>

If this isn't set the application's icon will be used instead.


2. Receiving Push Notifications

To send your users Push Notifications, we'll need you to upload your Google Cloud Messaging credentials. Please follow this guide to do so.

Activity Routing

By default, when a notification sent by Taplytics is clicked, it will open up the main activity of the application. However, you may want to route your users to a different Activity. This can be done on the Taplytics Push page.

Simply add a custom data value to the push with the key tl_activity and with the full (including package name) class name of your activity. For example:

image

Push Title

By default, the title of a push notification will be the application name.

Currently, the best way to change the title of a push notification is to add a tl_title custom key. For Example:

image

Getting the Push Token

Sometimes, it can be useful to have the actual token generated by GCM, to target pushes at specific users.

To get this token, use the following method:

Taplytics.setTaplyticsPushTokenListener(new TaplyticsPushTokenListener() {
	@Override
	public void pushTokenReceived(String token) {
		//Do something with the push token here.
	}
});
Taplytics.setTaplyticsPushTokenListener {
  //Do something with the push token here.
}

3. Rich Push Notifications

Implementing rich push notification support can help improve user engagement with your push notifications with image content attached. We currently support JPEG and PNG images sent from the Taplytics dashboard or API.

Android will automatically crop all images to be a 2:1 aspect ratio, scaling if necessary.

The max image size that can be uploaded is 10mb. Note that images are not downscaled and if an image is sent, the full file size of the crop will be used.

Here is an example of a push notification with an image:

image


4. Custom Data and Tracking Push Interactions

Taplytics has changed as of version 1.9 and push notifications are easier than ever:

To retrieve custom data set in the Taplytics dashboard, as well as to track push interactions (receive, open, dismiss), simply extend the TLBroadcastReceiver and override the function that you need. Then, replace the TLGcmBroadcastReceiver in your manifest with that one!

Below is an example receiver that explains exactly how this is done. You can put this class directly in your app and start tracking push notifications right away. By default, taplytics will open the LAUNCH activity of your app, but this can be changed by not calling the super (see example below).

Note that Taplytics automatically tracks the following, however if you would like to do so for internal reasons, this is how.

/**
 * Example receiver to take action with push notifications.
 *
 * Make sure to add this to your manifest (see the docs)
 *
 * Overriding any of these is entirely optional.
 *
 * By default, taplytics will open the launch activity of your
 * app when a push notification is clicked.
 *
 */
public class MyBroadcastReceiver extends TLGcmBroadcastReceiver {

    @Override
    public void pushOpened(Context context, Intent intent) {

        //A user clicked on the notification! Do whatever you want here!

        /* If you call through to the super,
        Taplytics will launch your app's LAUNCH activity.
        This is optional. */
        super.pushOpened(context, intent);
    }

    @Override
    public void pushDismissed(Context context, Intent intent) {
        //The push has been dismissed :(

    }

    @Override
    public void pushReceived(Context context, Intent intent) {
        //The push was received, but not opened yet!

        /*
        If you add the custom data of tl_silent = true to the push notification,
        there will be no push notification presented to the user. However, this will
        still be triggered, meaning you can use this to remotely trigger something
        within the application!
         */
    }
}
/**
 * Example receiver to take action with push notifications.
 *
 * Make sure to add this to your manifest (see the docs)
 *
 * Overriding any of these is entirely optional.
 *
 * By default, taplytics will open the launch activity of your
 * app when a push notification is clicked.
 *
 */
class MyBroadcastReceiver : TLGcmBroadcastReceiver() {

  override fun pushOpened(context: Context?, intent: Intent?) {
    //A user clicked on the notification! Do whatever you want here!
    /* If you call through to the super,
    Taplytics will launch your app's LAUNCH activity.
    This is optional. */
    super.pushOpened(context, intent)
  }

  override fun pushDismissed(context: Context?, intent: Intent?) {
    //The push has been dismissed :(
  }

  override fun pushReceived(context: Context?, intent: Intent?) {
    //The push was received, but not opened yet!
    /*
      If you add the custom data of tl_silent = true to the push notification,
      there will be no push notification presented to the user. However, this will
      still be triggered, meaning you can use this to remotely trigger something
      within the application!
    */
  }
}

And then in your manifest:

<receiver
    android:name=".MyBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>

    <intent-filter>
         <action android:name="taplytics.push.OPEN" />
         <action android:name="taplytics.push.DISMISS" />
    </intent-filter>
</receiver>

If you are handling push notifications with custom payloads, the custom data key/values will be added to the custom_keys object as seen below in an example push payload:

{
  "data": {
    "message": "Test Push",
    "tl_id": "",
    "custom_keys": {
      "custom_data_key": "custom_data_value"
    },
    "image_url": ""
  }
}

5. Special Push Options (title, priority, icon)

The dashboard allows for custom data to be entered into your push notifications. However there are some options that can be added to the custom data for special functionality.

NameValuesExplanation
tl_titleStringThis changes the TITLE of the push notification. By default, it is your application's name. But with this option you can change the title to be anything.
tl_priorityintegerSet the priority of the push notification. For more info see the section 'Correctly set and manage notification priority' here. The value set must be the integer that is associated with the priorities, which can be found here.
tl_image_iconbooleanWill not show a preview image as the notification icon when set to false. Defaults to true.
tl_large_iconbooleanWill show the app icon in the notification when set to true. Defaults to false.

6. Manual Token Registration (Optional)

If you already have a system to receive Firebase push tokens, you can use TLFirebaseMessagingServiceLite instead of TLFirebaseMessagingService. Instead of overriding onNewToken and saving the token to our system automatically, all TLFirebaseMessagingServiceLite does is process the push notification.

Include the start option manualPushToken and set it to true to bypass saving the token automatically.

options.put("manualPushToken", true);
Taplytics.startTaplytics(this, "YOUR_SDK_KEY", options);
options["manualPushToken"] = true
Taplytics.startTaplytics(this, "YOUR_SDK_KEY", options)

You then must use Taplytics.savePushToken("PUSH_TOKEN") to enable push notifications through Taplytics.


7. Tracking Self Built Notifications

You may be using Taplytics simply to send push notifications. If you already have a system to build notifications, then extending the Taplytics BroadcastReceiver will cause you to see duplicates.

To avoid this problem, first, do not call super.onReceive() where super would be the TLGCMBroadcastReceiver.

Now, Taplytics will not have any push notification tracking if you do this.

To mitigate this, you must use the Taplytics functions provided. In each function, you must pass in the tl_id in the notification attempt.

Push Open

Taplytics.trackPushOpen("tl_id",customKeys);

Where tl_id is retrieved from the notification intent. CustomKeys is the metadata passed into the notification. It is optional/nullable

Push Dismissed

Taplytics.trackPushDismissed("tl_id",customKeys);

Where tl_id is retrieved from the notification intent. CustomKeys is the metadata passed into the notification. It is optional/nullable

Push Received

Taplytics.trackPushReceived("tl_id",customKeys);

Where tl_id is retrieved from the notification intent. CustomKeys is the metadata passed into the notification. It is optional/nullable


8. Troubleshooting

Using the shake menu, you can copy the token to your clipboard, force to save the token to Taplytics, or renew the token.

Taplytics.showMenu(); // Shows the shake menu, exposing some useful items like the push token

image


Sockets

Usage

Sockets are used in the Taplytics SDK to establish a two way connection to Taplytics' servers. This connection is used for editing experiments, both for visual experiments and dynamic variables. However it is only used in the development versions of the app, or devices which have been explicitly paired with Taplytics using a pairing email or text message.


Inclusion in release builds - as of 1.7.0

Due to experiment editing for the most part being done on internal dev builds sockets are not necessary on release versions of the app. In order to account for this Taplytics does not require the socket dependency for release builds. So instead of using the standard compile directive when adding the socket dependency the debugImplementation directive can be used instead.

Please note that this will not allow pairing of live versions of the app using text message or email pairing.

debugImplementation ('io.socket:socket.io-client:1.0.1') {
        // excluding org.json which is provided by Android
        exclude group: 'org.json', module: 'json'
}

I'm getting a popup saying update my sockets!

There was a recent change (1.7.0) in the package name of the socket library used by Taplytics. Given that the library has continued to improve over time Taplytics makes use of this new library. Unfortunately this means that the dependency that was being used before is no longer valid.

There is a quick change that will fix this issue, simple change:

implementation("com.github.nkzawa:socket.io-client:+") {
            exclude group: 'org.json'
}
implementation("com.github.nkzawa:engine.io-client:+") {
            exclude group: 'org.json'
}

to:

debugImplementation('io.socket:socket.io-client:1.0.1') {
        // excluding org.json which is provided by Android
        exclude group: 'org.json', module: 'json'
}

as mentioned above you can use debugImplementation instead of implementation if you don't want to include socket-io in the release version of your app.