## Example Usage ### Get the Default Singleton Instance ```java CleverTapAPI clevertap = CleverTapAPI.getDefaultInstance(getApplicationContext()); ``` ### Creating Default Instance by specifying custom CleverTap ID * Add the following to `AndroidManifest.xml` file ```xml ``` * Register the ActivityLifecycleCallback with a unique custom CleverTap ID per user ```java ActivityLifecycleCallback.register(this,"uniqueCustomClevertapIDForUser"); ``` * Or, create a default instance with a unique custom CleverTap ID per users ```java CleverTapAPI cleverTapAPI = CleverTapAPI.getDefaultInstance(getApplicationContext(),"uniqueCustomClevertapIDForUser"); ``` ### Record Events ```java clevertap.pushEvent(“Event Name”); ``` * Record an Event with properties ```java HashMap prodViewedAction = new HashMap(); prodViewedAction.put("Product Name", "Casio Chronograph Watch"); prodViewedAction.put("Category", "Mens Accessories"); prodViewedAction.put("Price", 59.99); prodViewedAction.put("Date", new java.util.Date()); clevertap.pushEvent("Product viewed", prodViewedAction); ``` * Record a Charged (purchase made) Event ```java HashMap chargeDetails = new HashMap(); chargeDetails.put("Amount", 300); chargeDetails.put("Payment Mode", "Credit card"); chargeDetails.put("Charged ID", 24052013); HashMap item1 = new HashMap(); item1.put("Product category", "books"); item1.put("Book name", "The Millionaire next door"); item1.put("Quantity", 1); HashMap item2 = new HashMap(); item2.put("Product category", "books"); item2.put("Book name", "Achieving inner zen"); item2.put("Quantity", 1); HashMap item3 = new HashMap(); item3.put("Product category", "books"); item3.put("Book name", "Chuck it, let's do it"); item3.put("Quantity", 5); ArrayList> items = new ArrayList>(); items.add(item1); items.add(item2); items.add(item3); clevertap.pushChargedEvent(chargeDetails, items); ``` ### Record User Profile properties ```java // each of the below fields are optional // if set, these populate demographic information in the Dashboard HashMap profileUpdate = new HashMap(); profileUpdate.put("Name", "Jack Montana"); // String profileUpdate.put("Identity", 61026032); // String or number profileUpdate.put("Email", "jack@gmail.com"); // Email address of the user profileUpdate.put("Phone", "+14155551234"); // Phone (with the country code, starting with +) profileUpdate.put("Gender", "M"); // Can be either M or F profileUpdate.put("Employed", "Y"); // Can be either Y or N profileUpdate.put("Education", "Graduate"); // Can be either Graduate, College or School profileUpdate.put("Married", "Y"); // Can be either Y or N profileUpdate.put("DOB", new Date()); // Date of Birth. Set the Date object to the appropriate value first profileUpdate.put("Age", 28); // Not required if DOB is set profileUpdate.put("Tz", "Asia/Kolkata"); //an abbreviation such as "PST", a full name such as "America/Los_Angeles", //or a custom ID such as "GMT-8:00" profileUpdate.put("Photo", "www.foobar.com/image.jpeg"); // URL to the Image // optional fields. controls whether the user will be sent email, push etc. profileUpdate.put("MSG-email", false); // Disable email notifications profileUpdate.put("MSG-push", true); // Enable push notifications profileUpdate.put("MSG-sms", false); // Disable SMS notifications ArrayList stuff = new ArrayList(); stuff.add("bag"); stuff.add("shoes"); profileUpdate.put("MyStuff", stuff); //ArrayList of Strings String[] otherStuff = {"Jeans","Perfume"}; profileUpdate.put("MyStuff", otherStuff); //String Array clevertap.pushProfile(profileUpdate); ``` ### Handling Multiple Device Users Use `onUserLogin` to maintain multiple distinct user profiles on the same device ```java // each of the below fields are optional // with the exception of one of Identity, Email, FBID or GPID HashMap profileUpdate = new HashMap(); profileUpdate.put("Name", "Jack Montana"); // String profileUpdate.put("Identity", 61026032); // String or number profileUpdate.put("Email", "jack@gmail.com"); // Email address of the user profileUpdate.put("Phone", "+14155551234"); // Phone (with the country code, starting with +) profileUpdate.put("Gender", "M"); // Can be either M or F profileUpdate.put("Employed", "Y"); // Can be either Y or N profileUpdate.put("Education", "Graduate"); // Can be either Graduate, College or School profileUpdate.put("Married", "Y"); // Can be either Y or N profileUpdate.put("DOB", new Date()); // Date of Birth. Set the Date object to the appropriate value first profileUpdate.put("Age", 28); // Not required if DOB is set // optional fields. controls whether the user will be sent email, push etc. profileUpdate.put("MSG-email", false); // Disable email notifications profileUpdate.put("MSG-push", true); // Enable push notifications profileUpdate.put("MSG-sms", false); // Disable SMS notifications ArrayList stuff = new ArrayList(); stuff.add("bag"); stuff.add("shoes"); profileUpdate.put("MyStuff", stuff); //ArrayList of Strings String[] otherStuff = {"Jeans","Perfume"}; profileUpdate.put("MyStuff", otherStuff); //String Array cleverTapAPI.onUserLogin(profileUpdate); ``` ### Integrate Custom Proxy Domain The custom proxy domain feature allows to proxy all events raised from the CleverTap SDK through your required domain, ideal for handling or relaying CleverTap events and Push Impression events with your application server. Following ways can be used to configure custom proxy domain(s): #### Using Manifest file 1. Add your CleverTap Account credentials in the Manifest file against the `CLEVERTAP_ACCOUNT_ID` and `CLEVERTAP_TOKEN` keys. 2. Add the **CLEVERTAP_PROXY_DOMAIN** key with the proxy domain value for handling events through the custom proxy domain. 3. Add the **CLEVERTAP_SPIKY_PROXY_DOMAIN** key with proxy domain value for handling push impression events. ```xml ``` #### Using `changeCredentials` API ```java CleverTapAPI.changeCredentials( "YOUR CLEVERTAP ACCOUNT ID", "YOUR CLEVERTAP ACCOUNT TOKEN", "YOUR PROXY DOMAIN", "YOUR SPIKY PROXY DOMAIN" ); ``` #### Using CleverTap's Additional Instance ```java CleverTapInstanceConfig cleverTapInstanceConfig = CleverTapInstanceConfig.createInstance( applicationContext, "YOUR CLEVERTAP ACCOUNT ID", "YOUR CLEVERTAP ACCOUNT TOKEN" ); cleverTapInstanceConfig.setProxyDomain("YOUR PROXY DOMAIN"); cleverTapInstanceConfig.setSpikyProxyDomain("YOUR SPIKY PROXY DOMAIN"); CleverTapAPI.instanceWithConfig(applicationContext, cleverTapInstanceConfig); ``` ### Using App Inbox #### Adding Inbox Dependencies Add the following dependencies in your app's `build.gradle` ```groovy implementation "androidx.appcompat:appcompat:1.7.0"//MANDATORY for App Inbox implementation "androidx.recyclerview:recyclerview:1.3.2"//MANDATORY for App Inbox implementation "androidx.viewpager:viewpager:1.0.0"//MANDATORY for App Inbox implementation "com.google.android.material:material:1.12.0"//MANDATORY for App Inbox implementation "com.github.bumptech.glide:glide:4.12.0"//MANDATORY for App Inbox //Optional ExoPlayer Libraries for Audio/Video Inbox Messages. Audio/Video messages will be dropped without these dependencies implementation "com.google.android.exoplayer:exoplayer:2.19.1" implementation "com.google.android.exoplayer:exoplayer-hls:2.19.1" implementation "com.google.android.exoplayer:exoplayer-ui:2.19.1" ``` #### Migrating from `Exoplayer` to `AndroidX Media3` Clevertap supports `AndroidX Media3` from `v7.0.0+` to replace the deprecated `ExoPlayer` libraries. For migration change the following dependencies. | Old Dependency | New Dependency | |-----------------------:|:--------------------| | `com.google.android.exoplayer:exoplayer:2.19.1` | `androidx.media3:media3-exoplayer:1.4.0` | | `com.google.android.exoplayer:exoplayer-hls:2.19.1` | `androidx.media3:media3-exoplayer-hls:1.4.0` | | `com.google.android.exoplayer:exoplayer-ui:2.19.1` | `androidx.media3:media3-ui:1.4.0` | #### Initializing the Inbox Initializing the Inbox will provide a callback to two methods `inboxDidInitialize()` AND `inboxMessagesDidUpdate()` ```java import com.clevertap.android.sdk.inbox.CTInboxActivity; import com.clevertap.android.sdk.CTInboxListener; import com.clevertap.android.sdk.CTInboxStyleConfig; import com.clevertap.android.sdk.CleverTapAPI; import com.clevertap.android.sdk.CleverTapInstanceConfig; public class MainActivity extends AppCompatActivity implements CTInboxListener { @Override protected void onCreate(Bundle savedInstanceState) { private CleverTapAPI cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this); if (cleverTapDefaultInstance != null) { //Set the Notification Inbox Listener cleverTapDefaultInstance.setCTNotificationInboxListener(this); //Initialize the inbox and wait for callbacks on overridden methods cleverTapDefaultInstance.initializeInbox(); } } } ``` #### Configure Styling and Showing the Inbox Customize the config object and call the Inbox in the `inboxDidInitialize()` method Call this method on the button click which opens the CleverTap Inbox for your App ```java @Override public void inboxDidInitialize(){ ArrayList tabs = new ArrayList<>(); tabs.add("Promotions"); tabs.add("Offers"); tabs.add("Others");//We support upto 2 tabs only. Additional tabs will be ignored CTInboxStyleConfig styleConfig = new CTInboxStyleConfig(); styleConfig.setFirstTabTitle("First Tab");//By default, name of the first tab is "ALL" styleConfig.setTabs(tabs);//Do not use this if you don't want to use tabs styleConfig.setTabBackgroundColor("#FF0000");//provide Hex code in string ONLY styleConfig.setSelectedTabIndicatorColor("#0000FF"); styleConfig.setSelectedTabColor("#000000"); styleConfig.setUnselectedTabColor("#FFFFFF"); styleConfig.setBackButtonColor("#FF0000"); styleConfig.setNavBarTitleColor("#FF0000"); styleConfig.setNavBarTitle("MY INBOX"); styleConfig.setNavBarColor("#FFFFFF"); styleConfig.setInboxBackgroundColor("#00FF00"); cleverTapDefaultInstance.showAppInbox(styleConfig); //Opens activity tith Tabs //OR cleverTapDefaultInstance.showAppInbox();//Opens Activity with default style config } ``` ### Dismissing App Inbox Use the following method to dismiss the App Inbox Activity as per your business use case - ```java cleverTapDefaultInstance.dismissAppInbox(); ``` ### App Inbox Item and Button Click Callbacks Let's understand the types of buttons first that App Inbox supports: - URL button (fires the deeplink with the associated URL) - Copy to button (Copies the associated text to the clipboard) - KV button (contains the custom kev-value pair for custom handling) The Android SDK v4.6.1 and above supports `onInboxItemClicked` callback on the click of an App Inbox item, such as text or media. From the Android SDK v4.6.8 onwards and below v4.7.0, the `onInboxItemClicked` callback supports the button click besides the item click. The callback returns `CTInboxMessage` object, `itemIndex` and `buttonIndex` parameters. To use this callback, check that your activity implements the `InboxMessageListener` and overrides the following method: ```java @Override public void onInboxItemClicked(CTInboxMessage message, int contentPageIndex, int buttonIndex){ Log.i(TAG, "InboxItemClicked at" + contentPageIndex + " page-index with button-index:" + buttonIndex); //The buttonIndex corresponds to the CTA button clicked (0, 1, or 2). A value of -1 indicates the app inbox body/message clicked. List inboxMessageContentList = message.getInboxMessageContents(); //The contentPageIndex corresponds to the page index of the content, which ranges from 0 to the total number of pages for carousel templates. For non-carousel templates, the value is always 0, as they only have one page of content. CTInboxMessageContent messageContentObject = inboxMessageContentList.get(contentPageIndex); if (buttonIndex != -1) { //button is clicked try { List inboxMessageContentList = message.getInboxMessageContents(); JSONObject buttonObject = (JSONObject) messageContentObject.getLinks().get(buttonIndex); String buttonType = buttonObject.getString("type"); Log.i(TAG, "type of button clicked: " + buttonType); } catch (Throwable t) { t.printStackTrace(); } } else { //Item is clicked Log.i(TAG, "type/template of App Inbox item:" + message.type); } } ``` Android SDK v3.6.1 and above supports an exclusive `onInboxButtonClick` callback on the click of **KV** type of buttons. It returns a Map of Key-Value pairs. To use this, make sure your activity implements the `InboxMessageButtonListener` and override the following method: ```java @Override public void onInboxButtonClick(HashMap hashMap) { Log.i(TAG, "InboxButtonClick with payload:" + payload); } ``` ### Creating your own App Inbox You can choose to create your own App Inbox with the help of the following APIs - ```java //Initialize App Inbox cleverTapDefaultInstance.initializeInbox(); //Get Inbox Message Count cleverTapDefaultInstance.getInboxMessageCount(); //Get Inbox Unread Count cleverTapDefaultInstance.getInboxMessageUnreadCount(); //Get All messages cleverTapDefaultInstance.getAllInboxMessages(); //Get only Unread messages cleverTapDefaultInstance.getUnreadInboxMessages(); //Get message object belonging to the given message id only. Message id should be a String cleverTapDefaultInstance.getInboxMessageForId(messageId); //Delete message from the Inbox. Message id should be a String cleverTapDefaultInstance.deleteInboxMessage(messageId); //Delete message from the Inbox. Message should object of CTInboxMessage cleverTapDefaultInstance.deleteInboxMessage(message); //Mark Message as Read. Message id should be a String cleverTapDefaultInstance.markReadInboxMessage(messageId); //Mark message as Read. Message should object of CTInboxMessage cleverTapDefaultInstance.markReadInboxMessage(message); //Raise Notification Viewed event for Inbox Message. Message id should be a String cleverTapDefaultInstance.pushInboxNotificationViewedEvent(messageId); //Raise Notification Clicked event for Inbox Message. Message id should be a String cleverTapDefaultInstance.pushInboxNotificationClickedEvent(messageId); //Callback on Inbox Message update/delete/read (any activity) @Override public void inboxMessagesDidUpdate() { } ``` ### Additional AndroidManifest.xml Configuration to Support Notifications #### Push Notifications If using FCM, inside the `` tags, register the following services ```xml ``` To set a custom notification icon (only for small icon), add the following meta data entry in your AndroidManifest.xml ```xml ``` To track the push notification events and deeplinks add the following receiver in your AndroidManifest.xml - ```xml ``` CleverTap handles closing the notification with Action buttons. You will have to add an additional CleverTap IntentService to your AndroidManifest.xml and the SDK will do it for you automatically ```xml ``` Starting from `core v5.1.0` we have introduced a new feature that allows developers to define a default notification channel for their app. This feature provides flexibility in handling push notifications. Please note that this is only supported for clevertap core notifications. Support for push templates will be released soon. To specify the default notification channel ID, you can add the following metadata in your app's manifest file: ```xml ``` By including this metadata, you can define a specific notification channel that CleverTap will use if the channel provided in push payload is not registered by your app. This ensures that push notifications are displayed consistently even if the app's notification channels are not set up. In case the SDK does not find the default channel ID specified in the manifest, it will automatically fallback to using a default channel called "Miscellaneous". This ensures that push notifications are still delivered, even if no specific default channel is specified in the manifest. This enhancement provides developers with greater control over the default notification channel used by CleverTap for push notifications, ensuring a seamless and customizable user experience. Starting from `core v5.1.0` below APIs allows you to retrieve a notification bitmap from the specified `bitmapSrcUrl` with a specified timeout and size. In case the bitmap retrieval fails, you can choose to fallback to the app icon by setting the `fallbackToAppIcon` parameter. This API provides more control over the bitmap retrieval process for custom rendering. ```java @Override public void onMessageReceived(RemoteMessage message) { Bundle messageBundle = mParser.toBundle(message); // this method must be called on background thread // context, messageBundle must be non null. // timeout must be in range of 1 - 20000 millis. CleverTapAPI.getNotificationBitmapWithTimeout( context,messageBundle, "https://www.pushicons.com/icon", true, 5000); } ``` Below API extends the functionality of the previous one by additionally allowing you to specify the desired size in bytes for the retrieved bitmap. ```java @Override public void onMessageReceived(RemoteMessage message) { Bundle messageBundle = mParser.toBundle(message); // this method must be called on background thread // context, messageBundle must be non null. // timeout must be in range of 1 - 20000 millis and size must be greater than 0. CleverTapAPI.getNotificationBitmapWithTimeoutAndSize( context,messageBundle, "https://www.pushicons.com/icon", true, 5000,1024); } ``` #### Pull Notifications Starting with v3.4.0, the SDK supports Pull Notifications. Pull Notifications is a capability that allows you to reach users on devices which suppress notifications via GCM/FCM. To allow your app to use CleverTap's Pull Notifications via background ping service, add the following fields in your app's `AndroidManifest.xml` ```xml ``` #### In-App Notifications To support in-app notifications, register the following activity in your AndroidManifest.xml ```xml ``` #### Push primer Android 13 notification runtime permission. Using Half-Interstitial in-app Java ```java JSONObject jsonObject = CTLocalInApp.builder() .setInAppType(CTLocalInApp.InAppType.HALF_INTERSTITIAL) .setTitleText("Get Notified") .setMessageText("Please enable notifications on your device to use Push Notifications.") .followDeviceOrientation(true) .setPositiveBtnText("Allow") .setNegativeBtnText("Cancel") .setBackgroundColor(Constants.WHITE) .setBtnBorderColor(Constants.BLUE) .setTitleTextColor(Constants.BLUE) .setMessageTextColor(Constants.BLACK) .setBtnTextColor(Constants.WHITE) .setImageUrl("https://icons.iconarchive.com/icons/treetog/junior/64/camera-icon.png", "Alt Text") .setBtnBackgroundColor(Constants.BLUE) .build(); cleverTapAPI.promptPushPrimer(jsonObject); ``` Kotlin ```kotlin val jsonObject = CTLocalInApp.builder() .setInAppType(CTLocalInApp.InAppType.HALF_INTERSTITIAL) .setTitleText("Get Notified") .setMessageText("Please enable notifications on your device to use Push Notifications.") .followDeviceOrientation(true) .setPositiveBtnText("Allow") .setNegativeBtnText("Cancel") .setBackgroundColor(Constants.WHITE) .setBtnBorderColor(Constants.BLUE) .setTitleTextColor(Constants.BLUE) .setMessageTextColor(Constants.BLACK) .setBtnTextColor(Constants.WHITE) .setBtnBackgroundColor(Constants.BLUE) .build() cleverTapAPI.promptPushPrimer(jsonObject) ``` Using Alert in-app Java ```java JSONObject jsonObject = CTLocalInApp.builder() .setInAppType(CTLocalInApp.InAppType.ALERT) .setTitleText("Get Notified") .setMessageText("Enable Notification permission") .followDeviceOrientation(true) .setPositiveBtnText("Allow") .setNegativeBtnText("Cancel") .build(); cleverTapAPI.promptPushPrimer(jsonObject); ``` Kotlin ```kotlin val jsonObject = CTLocalInApp.builder() .setInAppType(CTLocalInApp.InAppType.ALERT) .setTitleText("Get Notified") .setMessageText("Enable Notification permission") .followDeviceOrientation(true) .setPositiveBtnText("Allow") .setNegativeBtnText("Cancel") .build() cleverTapAPI.promptPushPrimer(jsonObject) ``` #### Call Android OS runtime notification dialog without using push primer. Takes boolean as a parameter. If true and the permission is denied then we fallback to app’s notification settings, if it’s false then we just throw a verbose log saying permission is denied. Java ```java cleverTapAPI.promptForPushPermission(true); ``` Kotlin ```kotlin cleverTapAPI.promptForPushPermission(true) ``` #### Check the status of notification permission whether it's granted or denied. Returns true if permission is granted, else returns false if permission is denied. Java ```java cleverTapAPI.isPushPermissionGranted(); ``` Kotlin ```kotlin cleverTapAPI.isPushPermissionGranted ``` #### Call Android OS runtime notification dialog for HTML in-app campaigns. Two methods will be available to call hard permission dialog flow from HTML InApp’s as shown below:- promptPushPermission(boolean shouldShowFallbackSettings) - Use to trigger OS notification dialog. dismissInAppNotification() - Use to dismiss the current InApp. Sample code usage ```html ``` #### CTLocalInApp builder methods description Builder Methods | Parameters | Description | Required ---:|:---:|:---:|:--- `setInAppType(InAppType)` | CTLocalInApp.InAppType.HALF_INTERSTITIAL OR CTLocalInApp.InAppType.ALERT | Accepts only HALF_INTERSTITIAL & ALERT type to display the type of InApp | Required `setTitleText(String)` | Text | Sets the title of the local in-app | Required `setMessageText(String)` | Text | Sets the subtitle of the local in-app | Required `followDeviceOrientation(boolean)` | true/false | If true then the local InApp is shown for both portrait and landscape. If it sets false then local InApp only displays for portrait mode | Required `setPositiveBtnText(String)` | Text | Sets the text of the positive button | Required `setNegativeBtnText(String)` | Text | Sets the text of the negative button | Required `setFallbackToSettings(boolean)` | true/false | If true and the permission is denied then we fallback to app’s notification settings, if it’s false then we just throw a verbose log saying permission is denied | Optional `setBackgroundColor(String)` | Accepts Hex color as String | Sets the background color of the local in-app | Optional `setBtnBorderColor(String)` | Accepts Hex color as String | Sets the border color of both positive/negative buttons | Optional `setTitleTextColor(String)` | Accepts Hex color as String | Sets the title color of the local in-app | Optional `setMessageTextColor(String)` | Accepts Hex color as String | Sets the sub-title color of the local in-app | Optional `setBtnTextColor(String)` | Accepts Hex color as String | Sets the color of text for both positive/negative buttons | Optional `setBtnBackgroundColor(String)` | Accepts Hex color as String | Sets the background color for both positive/negative buttons | Optional `setBtnBorderRadius(String)` | Text | Sets the radius for both positive/negative buttons. Default radius is “2” if not set | Optional #### Available Callbacks for Push Primer Based on notification permission grant/deny, we’ll be providing a callback `PushPermissionResponseListener` .Below is a sample implementation to get the permission result ```java public class MainActivity extends AppCompatActivity implements PushPermissionResponseListener { @Override protected void onCreate(Bundle savedInstanceState) { private CleverTapAPI cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this); if (cleverTapDefaultInstance != null) { cleverTapDefaultInstance.ct.setPushPermissionResponseListener(this); } } @Override public void onPushPermissionResponse(boolean accepted) { Log.i(TAG, "onPushPermissionResponse : InApp---> response() called accepted="+accepted); } } ``` From CT-SDK 4.7.0+, new method onShow() is introduced for InAppNotificationListener . Below is the new method added for when the InApp is shown:- ```java @Override public void onShow(CTInAppNotification ctInAppNotification) { } ``` Please note from Android 13+ devices to render notification we'll have to call createNotificationChannel() after the permission is accepted. Java ```java public class MainActivity extends AppCompatActivity implements PushPermissionResponseListener { @Override protected void onCreate(Bundle savedInstanceState) { private CleverTapAPI cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this); if (cleverTapDefaultInstance != null) { cleverTapDefaultInstance.ct.setPushPermissionResponseListener(this); } } @Override public void onPushPermissionResponse(boolean accepted) { if(accepted){ //For Android 13+ we need to create notification channel after notification permission is accepted CleverTapAPI.createNotificationChannel(getApplicationContext(), "BRTesting", "Testing Channel", "Testing Channel for BR", NotificationManager.IMPORTANCE_HIGH, true); } } } ``` Kotlin ```kotlin class HomeScreenActivity : AppCompatActivity(),PushPermissionResponseListener{ override fun onCreate(savedInstanceState: Bundle?) { var cleverTapDefaultInstance = CleverTapAPI.getDefaultInstance(this) cleverTapDefaultInstance?.apply { pushPermissionNotificationResponseListener = this@HomeScreenActivity } } override fun onPushPermissionResponse(accepted: Boolean) { if(accepted){ //For Android 13+ we need to create notification channel after notification permission is accepted CleverTapAPI.createNotificationChannel( this, "BRTesting", "Core", "Core notifications", NotificationManager.IMPORTANCE_MAX, true ) } } } ``` #### Google Ad Id changes for Android 13 Please note if using Google Ad Id for apps targeting Android 13+, will have to declare the below permission ```xml ``` #### Remote Config Variables From CleverTap SDK v5.0.0 onwards, you can use Remote Config Variables in your app. Please refer to the [Remote Config Variables doc](Variables.md) to read more on how to integrate this to your app. #### A/B Experiment Variants From CleverTap SDK v7.7.1 onwards, you can retrieve information about the active A/B experiment variants for the current user. This is useful when you need to access variant data for custom implementations or analytics. The `variants()` method returns a list of active variants, where each variant contains an `id` key mapping to the numeric ID of the variant. **Java** ```java CleverTapAPI clevertap = CleverTapAPI.getDefaultInstance(getApplicationContext()); if (clevertap != null) { List> activeVariants = clevertap.variants(); } ``` **Kotlin** ```kotlin val clevertap = CleverTapAPI.getDefaultInstance(applicationContext) clevertap?.let { val activeVariants: List> = it.variants() } ``` > **Note:** The returned list is unmodifiable. If no variants are active for the current user, an empty list is returned. #### User Privacy & Opt-Out ##### setOptOut(boolean userOptOut) Use this method to opt the user out of all event tracking, or opt them back in. When a user is opted out, no events (including user actions and system events) are sent to CleverTap. **Java** ```java CleverTapAPI clevertap = CleverTapAPI.getDefaultInstance(this); // Opt the user out of tracking clevertap.setOptOut(true); // Opt the user back in clevertap.setOptOut(false); ``` **Kotlin** ```kotlin val clevertap = CleverTapAPI.getDefaultInstance(this) // Opt the user out of tracking clevertap.setOptOut(true) // Opt the user back in clevertap.setOptOut(false) ``` ##### setOptOut(boolean userOptOut, boolean allowSystemEvents) This overload improves GDPR opt-out functionality by allowing you to control whether critical system events (such as app installs, uninstalls, and push notification registration) are still sent to CleverTap, even when a user has opted out of tracking. This helps you respect user privacy choices while still maintaining essential app functionality and compliance with regulations. * If userOptOut is true and allowSystemEvents is true, the user is opted out of tracking, but critical system events will still be sent. * If userOptOut is true and allowSystemEvents is false, the user is fully opted out, and even critical system events will not be sent. * If userOptOut is false, the user is opted back in, and all events (including system events) will be sent. **Java** ```java CleverTapAPI clevertap = CleverTapAPI.getDefaultInstance(this); // Opt the user out, but allow critical system events (GDPR compliant) clevertap.setOptOut(true, true); // Opt the user out, do not allow any events (full opt-out) clevertap.setOptOut(true, false); // Opt the user back in, allow all events clevertap.setOptOut(false, true); ``` **Kotlin** ```kotlin val clevertap = CleverTapAPI.getDefaultInstance(this) // Opt the user out, but allow critical system events (GDPR compliant) clevertap.setOptOut(true, true) // Opt the user out, do not allow any events (full opt-out) clevertap.setOptOut(true, false) // Opt the user back in, allow all events clevertap.setOptOut(false, true) ``` #### Encryption of data stored in the SDK The SDK might contain PII data which could be sensitive information. From CleverTap SDK v5.2.0 onwards, you can enable encryption for PII data wiz. **Email, Identity, Name and Phone** at the app storage level. From CleverTap SDK v7.7.0 onwards, a new **Full_Data** encryption level is introduced that encrypts all data including content and profile information. Currently 3 levels of encryption are supported i.e None(0), Medium(1) and Full_Data(2). If not specified, the encryption level defaults to None(0). **None** - All stored data is in plaintext **Medium** - PII data is encrypted completely (PII keys mentioned above). **Full_Data** (v7.7.0+) - All client data including content and profile information is encrypted. Metadata might still be visible. The only way to set encryption level for default instance is from the `AndroidManifest.xml` * Add the following to `AndroidManifest.xml` file ```xml ``` * Different instances can have different encryption levels. To set an encryption level for an additional instance **Java** ```java CleverTapInstanceConfig config = CleverTapInstanceConfig.createInstance( applicationContext, "CLEVERTAP_ACCOUNT_ID", "CLEVERTAP_ACCOUNT_TOKEN", "CLEVERTAP_ACCOUNT_REGION" ); // For Medium encryption config.setEncryptionLevel(EncryptionLevel.MEDIUM); // For High encryption (v7.7.0+) config.setEncryptionLevel(EncryptionLevel.FULL_DATA); CleverTapAPI clevertap = CleverTapAPI.instanceWithConfig(applicationContext, config); ``` **Kotlin** ```kotlin val clevertap = CleverTapInstanceConfig.createInstance( applicationContext, "CLEVERTAP_ACCOUNT_ID", "CLEVERTAP_ACCOUNT_TOKEN", "CLEVERTAP_ACCOUNT_REGION" ).apply { // For Medium encryption setEncryptionLevel(EncryptionLevel.MEDIUM) // For High encryption (v7.7.0+) setEncryptionLevel(EncryptionLevel.FULL_DATA) }.let { config -> CleverTapAPI.instanceWithConfig(applicationContext, config) } ``` #### Encryption over Network Clevertap can send data over the network in encrypted form instead of plain text. From CleverTap SDK v7.5.0 onwards, you can enable encryption in transit for all eventd data that is sent over the network. To enable encryption in transit, add the following to your `AndroidManifest.xml` * Add the following to `AndroidManifest.xml` file ```xml ``` * Different instances can have different encryption over network settings. To enable encryption for an additional instance ```kotlin // 1. Create a configuration object for your additional instance val clevertapAdditionalInstanceConfig = CleverTapInstanceConfig.createInstance( applicationContext, "YOUR_ADDITIONAL_ACCOUNT_ID", // Replace with your actual Account ID "YOUR_ADDITIONAL_ACCOUNT_TOKEN", // Replace with your actual Account Token "YOUR_ADDITIONAL_ACCOUNT_REGION" ) // 2. Enable encryption in transit for this specific instance clevertapAdditionalInstanceConfig.setEncryptionInTransit(true) // 3. Get the CleverTapAPI instance using the configured settings val clevertapAdditionalInstance = CleverTapAPI.instanceWithConfig(applicationContext, clevertapAdditionalInstanceConfig) ``` ### User event logging APIs Get user event details Java ```java UserEventLog eventLog = clevertap.getUserEventLog("Product Viewed"); if (eventLog != null) { String eventName = eventLog.getEventName(); long firstTime = eventLog.getFirstTs(); long lastTime = eventLog.getLastTs(); int count = eventLog.getCountOfEvents(); String deviceId = eventLog.getDeviceID(); } else { System.out.println("Event not performed"); } ``` Kotlin ```kotlin clevertap.getUserEventLog("Product Viewed")?.let { eventLog -> val eventName = eventLog.eventName val firstTime = eventLog.firstTs val lastTime = eventLog.lastTs val count = eventLog.countOfEvents val deviceId = eventLog.deviceID } ?: println("Event not performed") ``` Get count of event occurrences Java ```java int eventCount = clevertap.getUserEventLogCount("Product Viewed"); ``` Kotlin ```kotlin val eventCount = clevertap.getUserEventLogCount("Product Viewed") ``` Get user's last app visit timestamp Java ```java long lastVisitTs = clevertap.getUserLastVisitTs(); ``` Kotlin ```kotlin val lastVisitTs = clevertap.userLastVisitTs ``` Get total number of app launches by user Java ```java int appLaunchCount = clevertap.getUserAppLaunchCount(); ``` Kotlin ```kotlin val appLaunchCount = cleverTapAPI?.userAppLaunchCount ``` Get full event history for user Java ```java Map eventHistory = clevertap.getUserEventLogHistory(); for (Map.Entry entry : eventHistory.entrySet()) { String eventName = entry.getKey(); UserEventLog log = entry.getValue(); // Process event details } ``` Kotlin ```kotlin val eventHistory = clevertap.userEventLogHistory eventHistory?.forEach { (eventName, log) -> // Process event details } ?: println("Events not performed") ``` ### Push registration tokens to Clevertap Push FCM device token to CleverTap ```kotlin override fun onNewToken(token: String) { super.onNewToken(token) cleverTapAPI.pushFcmRegistrationId(token, true) } ``` Push Huawei registration token to CleverTap ```kotlin override fun onNewToken(token: String?, bundle: Bundle?) { super.onNewToken(token, bundle) if (token != null) { cleverTapAPI.pushRegistrationToken(token, HmsConstants.HPS, true) } } ``` ### Baidu Setup and send channelId to CleverTap Enable baidu Push type in `AndroidManifest.xml` ```xml ``` Push Baidu registration token to CleverTap ```kotlin companion object { private val BAIDU_PUSH_TYPE = PushType( "bps", "bps_token", "com.clevertap.android.bps.BaiduPushProvider", "com.baidu.android.pushservice.PushMessageReceiver" ) } override fun onBind(context:Context, errorCode:Int, appid:String, userId:String, channelId:String, requestId:String) { //.. if (channelId != null) { cleverTapAPI.pushRegistrationToken(channelId, BAIDU_PUSH_TYPE, true) } } ``` Also additionally add this to proguard/any similar tool ```properties -dontwarn com.baidu.** ```