Skip to content

Commit 660c82f

Browse files
authored
Implement wrapper of CleverTap SDK (#512)
* Add CleverTap wrapper * [SDK-1836] Bump huawei, xiaomi, and fcm versions. Remove ct templates dependency. * Move migration config to separate class. Delete persistence of enum and use string. * Update migration state mid-session * Fix mid-session start of CT * Allow partial wrapper initialisation for early access * Add debug logs in wrapper * Fix app launched event, do not start dev socket if ct only, use operation queue for sending requests * Improve user identity mapping * Little fix in factory * Add purchase methods, mappings, LP.start attributes * Merge anonymous user into first login user * Fix and remove unnecessary TODOs * Map param values to CT * Add traffic source info to wrapper * Add CT instance callback, deprecate leanplum-mipush * Fix unit tests * Remove SyncListener usage and replace with custom * Remove setDeviceId from wrapper * Track value each time
1 parent 3dc749a commit 660c82f

39 files changed

+1124
-69
lines changed

AndroidSDKCore/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ dependencies {
4141
// Compile dependencies will be added as dependency in pom file.
4242
api "androidx.legacy:legacy-support-v4:1.0.0"
4343
api "androidx.appcompat:appcompat:${APPCOMPAT_LIBRARY_VERSION}"
44+
45+
api "com.clevertap.android:clevertap-android-sdk:4.6.3"
4446
}
4547

4648
task generateJavadoc(type: Javadoc) {

AndroidSDKCore/src/main/java/com/leanplum/Leanplum.java

Lines changed: 86 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727

2828
import androidx.annotation.NonNull;
2929
import androidx.annotation.Nullable;
30+
import com.clevertap.android.sdk.CleverTapAPI;
3031
import com.leanplum.ActionContext.ContextualValues;
3132
import com.leanplum.actions.internal.ActionDefinition;
3233
import com.leanplum.actions.internal.ActionManagerDefinitionKt;
3334
import com.leanplum.callbacks.ActionCallback;
35+
import com.leanplum.callbacks.CleverTapInstanceCallback;
3436
import com.leanplum.callbacks.ForceContentUpdateCallback;
3537
import com.leanplum.callbacks.RegisterDeviceCallback;
3638
import com.leanplum.callbacks.RegisterDeviceFinishedCallback;
@@ -61,6 +63,7 @@
6163
import com.leanplum.internal.Util.DeviceIdInfo;
6264
import com.leanplum.internal.VarCache;
6365
import com.leanplum.messagetemplates.MessageTemplates;
66+
import com.leanplum.migration.MigrationManager;
6467
import com.leanplum.models.GeofenceEventType;
6568
import com.leanplum.utils.BuildUtil;
6669
import com.leanplum.utils.SharedPreferencesUtil;
@@ -101,7 +104,6 @@ public class Leanplum {
101104
new ArrayList<>();
102105
private static final ArrayList<VariablesChangedCallback> onceNoDownloadsHandlers =
103106
new ArrayList<>();
104-
private static final Object heartbeatLock = new Object();
105107
private static final String LEANPLUM_NOTIFICATION_CHANNEL =
106108
"com.leanplum.LeanplumNotificationChannel";
107109
private static RegisterDeviceCallback registerDeviceHandler;
@@ -195,6 +197,7 @@ public static void enableVerboseLoggingInDevelopmentMode() {
195197
*/
196198
public static void setLogLevel(int level) {
197199
Log.setLogLevel(level);
200+
MigrationManager.getWrapper().setLogLevel(level);
198201
}
199202

200203
/**
@@ -332,7 +335,11 @@ public static void setDeviceId(String deviceId) {
332335
* (Advanced) Sets new device ID. Must be called after Leanplum finished starting.
333336
* This method allows multiple changes of device ID in opposite of
334337
* {@link Leanplum#setDeviceId(String)}, which allows only one.
338+
*
339+
* @deprecated When migration of data to CleverTap is turned on calling this method with different
340+
* device ID would not work any more.
335341
*/
342+
@Deprecated
336343
public static void forceNewDeviceId(String deviceId) {
337344
if (TextUtils.isEmpty(deviceId)) {
338345
Log.i("forceNewDeviceId - Empty deviceId parameter provided.");
@@ -344,6 +351,11 @@ public static void forceNewDeviceId(String deviceId) {
344351
return;
345352
}
346353

354+
if (MigrationManager.getState().useCleverTap()) {
355+
Log.i("Setting new device ID is not allowed when migration to CleverTap is turned on.");
356+
return;
357+
}
358+
347359
if (hasStarted()) {
348360
APIConfig.getInstance().setDeviceId(deviceId);
349361
APIConfig.getInstance().save();
@@ -379,6 +391,7 @@ public static void setLocale(String locale) {
379391
*
380392
* @return String Returns the deviceId in the current Leanplum session.
381393
*/
394+
@Nullable
382395
public static String getDeviceId() {
383396
if (!LeanplumInternal.hasCalledStart()) {
384397
Log.i("Leanplum.start() must be called before calling getDeviceId.");
@@ -394,13 +407,14 @@ public static void setApplicationContext(Context context) {
394407
if (context == null) {
395408
Log.i("setApplicationContext - Null context parameter provided.");
396409
}
397-
398410
Leanplum.context = context;
411+
MigrationManager.updateWrapper(); // init with StaticMethodsWrapper if migrating
399412
}
400413

401414
/**
402415
* Gets the application context.
403416
*/
417+
@Nullable
404418
public static Context getContext() {
405419
if (context == null) {
406420
Log.e("Your application context is not set. "
@@ -607,6 +621,10 @@ static synchronized void start(final Context context, final String userId,
607621

608622
LeanplumInternal.setStartedInBackground(actuallyInBackground);
609623

624+
LeanplumInternal.addStartIssuedHandler(() -> {
625+
MigrationManager.getWrapper().setUserAttributes(attributes);
626+
});
627+
610628
final Map<String, ?> validAttributes = LeanplumInternal.validateAttributes(attributes,
611629
"userAttributes", true);
612630
LeanplumInternal.setCalledStart(true);
@@ -775,30 +793,42 @@ private static void startHelper(
775793

776794
Util.initializePreLeanplumInstall(params);
777795

778-
// Issue start API call.
796+
MigrationManager.fetchState(state -> {
797+
if (state.useLeanplum()) {
798+
issueLeanplumStart(isBackground, params);
799+
} else {
800+
overrideLeanplumStart();
801+
}
802+
return null;
803+
});
804+
}
805+
806+
private static void issueLeanplumStart(boolean isBackground, Map<String, Object> startParams) {
779807
RequestType requestType = isBackground ? RequestType.DEFAULT : RequestType.IMMEDIATE;
780808
Request request = RequestBuilder
781809
.withStartAction()
782-
.andParams(params)
810+
.andParams(startParams)
783811
.andType(requestType)
784812
.create();
785-
request.onResponse(new Request.ResponseCallback() {
786-
@Override
787-
public void response(JSONObject response) {
788-
Log.d("Received start response: %s", response);
789-
handleStartResponse(response);
790-
}
813+
request.onResponse(response -> {
814+
Log.d("Received start response: %s", response);
815+
handleStartResponse(response);
791816
});
792-
request.onError(new Request.ErrorCallback() {
793-
@Override
794-
public void error(Exception e) {
795-
Log.e("Failed to receive start response", e);
796-
handleStartResponse(null);
797-
}
817+
request.onError(e -> {
818+
Log.e("Failed to receive start response", e);
819+
handleStartResponse(null);
798820
});
799821
RequestSender.getInstance().send(request);
822+
LeanplumInternal.triggerStartIssued();
823+
}
800824

825+
private static void overrideLeanplumStart() {
826+
// Override issueStart and hasStarted
801827
LeanplumInternal.triggerStartIssued();
828+
LeanplumInternal.setHasStarted(true);
829+
LeanplumInternal.setStartSuccessful(true);
830+
LeanplumInternal.moveToForeground();
831+
Leanplum.triggerStartResponse(true);
802832
}
803833

804834
private static void handleStartResponse(final JSONObject response) {
@@ -1161,11 +1191,12 @@ public static boolean hasStarted() {
11611191
* Returns the userId in the current Leanplum session. This should only be called after
11621192
* Leanplum.start().
11631193
*/
1194+
@Nullable
11641195
public static String getUserId() {
1165-
if (hasStarted()) {
1196+
if (LeanplumInternal.hasCalledStart()) {
11661197
return APIConfig.getInstance().userId();
11671198
} else {
1168-
Log.e("Leanplum.start() must be called before calling getUserId()");
1199+
Log.i("Leanplum.start() must be called before calling getUserId()");
11691200
}
11701201
return null;
11711202
}
@@ -1466,19 +1497,23 @@ public static void setUserAttributes(final String userId, Map<String, ?> userAtt
14661497
params.put(Constants.Params.NEW_USER_ID, userId);
14671498
}
14681499
if (userAttributes != null) {
1469-
userAttributes = LeanplumInternal.validateAttributes(userAttributes, "userAttributes",
1470-
true);
1471-
params.put(Constants.Params.USER_ATTRIBUTES, JsonConverter.toJson(userAttributes));
1472-
LeanplumInternal.getUserAttributeChanges().add(userAttributes);
1500+
Map<String, ?> validUserAttributes =
1501+
LeanplumInternal.validateAttributes(userAttributes, "userAttributes", true);
1502+
params.put(Constants.Params.USER_ATTRIBUTES, JsonConverter.toJson(validUserAttributes));
1503+
LeanplumInternal.getUserAttributeChanges().add(validUserAttributes);
14731504
}
14741505

14751506
if (LeanplumInternal.issuedStart()) {
1507+
MigrationManager.getWrapper().setUserId(userId);
1508+
MigrationManager.getWrapper().setUserAttributes(userAttributes);
14761509
setUserAttributesInternal(userId, params);
14771510
} else {
14781511
LeanplumInternal.addStartIssuedHandler(new Runnable() {
14791512
@Override
14801513
public void run() {
14811514
try {
1515+
MigrationManager.getWrapper().setUserId(userId);
1516+
MigrationManager.getWrapper().setUserAttributes(userAttributes);
14821517
setUserAttributesInternal(userId, params);
14831518
} catch (Throwable t) {
14841519
Log.exception(t);
@@ -1590,15 +1625,17 @@ public static void setTrafficSourceInfo(Map<String, String> info) {
15901625

15911626
try {
15921627
final HashMap<String, Object> params = new HashMap<>();
1593-
info = LeanplumInternal.validateAttributes(info, "info", false);
1594-
params.put(Constants.Params.TRAFFIC_SOURCE, JsonConverter.toJson(info));
1628+
Map<String, String> validInfo = LeanplumInternal.validateAttributes(info, "info", false);
1629+
params.put(Constants.Params.TRAFFIC_SOURCE, JsonConverter.toJson(validInfo));
15951630
if (LeanplumInternal.issuedStart()) {
1631+
MigrationManager.getWrapper().setTrafficSourceInfo(info);
15961632
setTrafficSourceInfoInternal(params);
15971633
} else {
15981634
LeanplumInternal.addStartIssuedHandler(new Runnable() {
15991635
@Override
16001636
public void run() {
16011637
try {
1638+
MigrationManager.getWrapper().setTrafficSourceInfo(info);
16021639
setTrafficSourceInfoInternal(params);
16031640
} catch (Throwable t) {
16041641
Log.exception(t);
@@ -1635,6 +1672,7 @@ private static void setTrafficSourceInfoInternal(HashMap<String, Object> params)
16351672
*/
16361673
public static void track(final String event, double value, String info,
16371674
Map<String, ?> params) {
1675+
MigrationManager.getWrapper().track(event, value, info, params);
16381676
LeanplumInternal.track(event, value, info, params, null);
16391677
}
16401678

@@ -1660,6 +1698,7 @@ public static void trackPurchase(final String event, double value, String curren
16601698
requestArgs.put(Constants.Params.IAP_CURRENCY_CODE, currencyCode);
16611699
}
16621700

1701+
MigrationManager.getWrapper().trackPurchase(event, value, currencyCode, params);
16631702
LeanplumInternal.track(event, value, null, params, requestArgs);
16641703
} catch (Throwable t) {
16651704
Log.exception(t);
@@ -1729,6 +1768,17 @@ public static void trackGooglePlayPurchase(String eventName, String item, long p
17291768
}
17301769
modifiedParams.put(Constants.Params.IAP_ITEM, item);
17311770

1771+
if (MigrationManager.trackGooglePlayPurchases()) {
1772+
MigrationManager.getWrapper().trackGooglePlayPurchase(
1773+
eventName,
1774+
item,
1775+
priceMicros / 1000000.0,
1776+
currencyCode,
1777+
purchaseData,
1778+
dataSignature,
1779+
params
1780+
);
1781+
}
17321782
LeanplumInternal.track(eventName, priceMicros / 1000000.0, null, modifiedParams, requestArgs);
17331783
}
17341784

@@ -1859,12 +1909,14 @@ public static void advanceTo(final String state, String info, final Map<String,
18591909
}
18601910

18611911
if (LeanplumInternal.issuedStart()) {
1912+
MigrationManager.getWrapper().advanceTo(state, info, params);
18621913
advanceToInternal(state, validatedParams, requestParams);
18631914
} else {
18641915
LeanplumInternal.addStartIssuedHandler(new Runnable() {
18651916
@Override
18661917
public void run() {
18671918
try {
1919+
MigrationManager.getWrapper().advanceTo(state, info, params);
18681920
advanceToInternal(state, validatedParams, requestParams);
18691921
} catch (Throwable t) {
18701922
Log.exception(t);
@@ -2330,4 +2382,14 @@ public static void setPushDeliveryTracking(boolean enable) {
23302382
public static boolean isPushDeliveryTrackingEnabled() {
23312383
return pushDeliveryTrackingEnabled;
23322384
}
2385+
2386+
/**
2387+
* Sets callback object to be notified when CleverTapAPI instance is created.
2388+
* Use this callback to initialise any CleverTap state.
2389+
*
2390+
* @param callback Null value will remove the callback.
2391+
*/
2392+
public static void onCleverTapInstanceInitialized(@Nullable CleverTapInstanceCallback callback) {
2393+
MigrationManager.setCleverTapInstanceCallback(callback);
2394+
}
23332395
}

AndroidSDKCore/src/main/java/com/leanplum/LeanplumActivityHelper.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import android.content.res.Resources;
3030
import android.os.Bundle;
3131

32+
import com.clevertap.android.sdk.ActivityLifecycleCallback;
3233
import com.leanplum.actions.internal.ActionManagerExecutionKt;
3334
import com.leanplum.annotations.Parser;
3435
import com.leanplum.internal.ActionManager;
@@ -37,6 +38,7 @@
3738
import com.leanplum.internal.Log;
3839

3940
import com.leanplum.internal.OperationQueue;
41+
import com.leanplum.migration.MigrationManager;
4042
import com.leanplum.utils.BuildUtil;
4143
import java.util.LinkedList;
4244
import java.util.Queue;
@@ -206,6 +208,8 @@ public static void enableLifecycleCallbacks(final Application app) {
206208
app.registerActivityLifecycleCallbacks(new LeanplumLifecycleCallbacks());
207209
}
208210

211+
MigrationManager.getWrapper().registerLifecycleCallback(app);
212+
209213
registeredCallbacks = true;
210214
// run pending actions if any upon start
211215
LeanplumInternal.addStartIssuedHandler(runPendingActionsRunnable);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.leanplum.callbacks;
2+
3+
import androidx.annotation.NonNull;
4+
import com.clevertap.android.sdk.CleverTapAPI;
5+
6+
public interface CleverTapInstanceCallback {
7+
void onInstance(@NonNull CleverTapAPI cleverTapInstance);
8+
}

AndroidSDKCore/src/main/java/com/leanplum/internal/Constants.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ public static class Params {
158158
public static final String API_HOST = "apiHost";
159159
public static final String API_PATH = "apiPath";
160160
public static final String DEV_SERVER_HOST = "devServerHost";
161+
public static final String MIGRATE_STATE = "migrateState";
162+
public static final String MIGRATE_STATE_HASH = "sha256";
163+
public static final String SDK = "sdk";
164+
public static final String CT_DUPLICATE = "ct";
165+
public static final String CLEVERTAP = "ct";
166+
public static final String CT_ACCOUNT_ID = "accountId";
167+
public static final String CT_TOKEN = "token";
168+
public static final String CT_REGION_CODE = "regionCode";
169+
public static final String CT_ATTRIBUTE_MAPPINGS = "attributeMappings";
170+
public static final String API_EVENTS_STATE = "events";
161171
}
162172

163173
public static class Keys {

AndroidSDKCore/src/main/java/com/leanplum/internal/LeanplumInternal.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import com.leanplum.actions.internal.Priority;
3737
import com.leanplum.callbacks.StartCallback;
3838
import com.leanplum.internal.Request.RequestType;
39+
import com.leanplum.migration.MigrationManager;
3940
import com.leanplum.models.GeofenceEventType;
4041

4142
import java.util.Arrays;
@@ -573,6 +574,9 @@ public void onResponse(boolean success) {
573574
if (!inForeground) {
574575
return;
575576
}
577+
if (!MigrationManager.getState().useLeanplum()) {
578+
return;
579+
}
576580
if (Constants.isDevelopmentModeEnabled && !Constants.isNoop()) {
577581
Socket.connectSocket();
578582
}

AndroidSDKCore/src/main/java/com/leanplum/internal/RequestBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public class RequestBuilder {
5454
public static final String ACTION_GET_INBOX_MESSAGES = "getNewsfeedMessages";
5555
public static final String ACTION_MARK_INBOX_MESSAGE_AS_READ = "markNewsfeedMessageAsRead";
5656
public static final String ACTION_DELETE_INBOX_MESSAGE = "deleteNewsfeedMessage";
57+
public static final String ACTION_GET_MIGRATE_STATE = "getMigrateState";
5758

5859
private String httpMethod;
5960
private String apiAction;
@@ -162,6 +163,10 @@ public static RequestBuilder withDeleteInboxMessageAction() {
162163
return new RequestBuilder(POST, ACTION_DELETE_INBOX_MESSAGE);
163164
}
164165

166+
public static RequestBuilder withGetMigrateState() {
167+
return new RequestBuilder(POST, ACTION_GET_MIGRATE_STATE);
168+
}
169+
165170
public RequestBuilder andParam(String param, Object value) {
166171
if (params == null)
167172
params = new HashMap<>();

0 commit comments

Comments
 (0)