diff --git a/business-core/src/main/java/com/tiktok/TikTokBusinessSdk.java b/business-core/src/main/java/com/tiktok/TikTokBusinessSdk.java
index c748514..b08e2b4 100644
--- a/business-core/src/main/java/com/tiktok/TikTokBusinessSdk.java
+++ b/business-core/src/main/java/com/tiktok/TikTokBusinessSdk.java
@@ -6,13 +6,20 @@
package com.tiktok;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_CRASH;
+
import android.app.Application;
import android.content.Context;
+import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.tiktok.appevents.*;
+import com.tiktok.appevents.base.EventName;
+import com.tiktok.appevents.base.TTBaseEvent;
+import com.tiktok.iap.TTInAppPurchaseWrapper;
+import com.tiktok.util.RegexUtil;
import com.tiktok.util.TTConst;
import com.tiktok.util.TTLogger;
@@ -99,14 +106,18 @@ private TikTokBusinessSdk(@NonNull TTConfig ttConfig) {
logger = new TTLogger(TAG, logLevel);
/* no app id exception */
- if (ttConfig.appId == null) {
- throw new IllegalArgumentException("app id not set");
+ if (TextUtils.isEmpty(ttConfig.appId) || !RegexUtil.validateAppId(ttConfig.appId)) {
+ ttConfig.appId = "";
+ logger.warn("Invalid App Id!");
}
- if (ttConfig.ttAppId == null) {
- logger.warn("ttAppId not set, but its usage is encouraged");
+ if (ttConfig.ttAppId == null || !RegexUtil.validateTTAppId(ttConfig.ttAppId)) {
+ ttConfig.ttAppId = "";
+ ttConfig.ttAppIds = new String[]{""};
+ ttConfig.ttFirstAppId = new BigInteger("0");
+ logger.warn("Invalid TikTok App Id!");
}
-
+ logger.info("appId: %s, TTAppId: %s, autoIapTrack: %s", ttConfig.appId, ttConfig.ttAppId, ttConfig.autoIapTrack);
config = ttConfig;
networkSwitch = new AtomicBoolean(ttConfig.autoStart);
sdkDebugModeSwitch.set(ttConfig.debugModeSwitch);
@@ -136,7 +147,7 @@ public static synchronized void initializeSdk(TTConfig ttConfig) {
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
if (TTCrashHandler.isTTSDKRelatedException(throwable)) {
- TTCrashHandler.handleCrash(TAG, throwable);
+ TTCrashHandler.handleCrash(TAG, throwable, TTSDK_EXCEPTION_CRASH);
}
if (getCrashListener() != null) {
getCrashListener().onCrash(thread, throwable);
@@ -155,6 +166,9 @@ public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwab
// the appEventLogger instance will be the main interface to track events
appEventLogger = new TTAppEventLogger(ttConfig.autoEvent,ttConfig.disabledEvents,
ttConfig.flushTime, ttConfig.disableMetrics, initTimeMS);
+ if (ttConfig.autoIapTrack) {
+ TTInAppPurchaseWrapper.registerIapTrack();
+ }
try {
long endTimeMS = System.currentTimeMillis();
@@ -258,10 +272,27 @@ void onNetworkChange(int toBeSentRequests, int successfulRequest, int failedRequ
* A shortcut method for the situations where the events do not require a property body.
* see more {@link TikTokBusinessSdk#trackEvent(String, JSONObject)}
*/
+ @Deprecated
public static void trackEvent(String event) {
appEventLogger.track(event, null);
}
+ @Deprecated
+ public static void trackEvent(String event, String eventId) {
+ appEventLogger.track(event, null, eventId);
+ }
+
+ public static void trackTTEvent(TTBaseEvent event) {
+ appEventLogger.track(event.eventName, event.properties, event.eventId);
+ }
+
+ public static void trackTTEvent(EventName event) {
+ appEventLogger.track(event.toString(), null);
+ }
+
+ public static void trackTTEvent(EventName event, String eventId) {
+ appEventLogger.track(event.toString(), null, eventId);
+ }
/**
*
@@ -286,8 +317,13 @@ public static void trackEvent(String event) {
*
* @param event event name
*/
+ @Deprecated
public static void trackEvent(String event, @Nullable JSONObject props) {
- appEventLogger.track(event, props);
+ appEventLogger.track(event, props, "");
+ }
+ @Deprecated
+ public static void trackEvent(String event, @Nullable JSONObject props, String eventId) {
+ appEventLogger.track(event, props, eventId);
}
/**
@@ -363,10 +399,18 @@ public static String getAppId() {
/**
* returns api_id
*/
- public static BigInteger getTTAppId() {
+ public static String getTTAppId() {
return config.ttAppId;
}
+ public static String[] getTTAppIds() {
+ return config.ttAppIds;
+ }
+
+ public static BigInteger getFirstTTAppIds() {
+ return config.ttFirstAppId;
+ }
+
/**
* if only appId is provided, the json schema would be like
* {
@@ -416,7 +460,7 @@ public static BigInteger getTTAppId() {
public static boolean onlyAppIdProvided() {
if(config.appId == null){
- throw new IllegalStateException("AppId should be checked before, this path should not be accessed");
+ logger.warn("AppId should be checked before, this path should not be accessed");
}
return config.ttAppId == null;
}
@@ -504,7 +548,10 @@ public static synchronized void identify(String externalId,
@Nullable String phoneNumber,
@Nullable String email) {
long initTimeMS = System.currentTimeMillis();
- appEventLogger.identify(externalId, externalUserName, phoneNumber, email);
+ boolean isReset = appEventLogger.identify(externalId, externalUserName, phoneNumber, email);
+ if (!isReset) {
+ return;
+ }
try {
long endTimeMS = System.currentTimeMillis();
JSONObject meta = TTUtil.getMetaWithTS(initTimeMS)
@@ -566,9 +613,11 @@ public static class TTConfig {
/* application context */
private final Application application;
/* api_id for api calls, App ID in EM */
- private String appId;
+ private String appId = "";
/* tt_app_id for api calls, TikTok App ID from EM */
- private BigInteger ttAppId;
+ private String ttAppId = "";
+ private String[] ttAppIds = {""};
+ private BigInteger ttFirstAppId=new BigInteger("0");
/* flush time interval in seconds, default 15, 0 -> disabled */
private int flushTime = 15;
// /* Access-Token for api calls */
@@ -590,6 +639,8 @@ public static class TTConfig {
/* open LDU mode*/
private boolean lduModeSwitch = false;
+ private boolean autoIapTrack = false;
+
/**
* Read configs from
@@ -614,7 +665,12 @@ public TTConfig setLogLevel(LogLevel ll) {
* set app id
*/
public TTConfig setTTAppId(String ttAppId) {
- this.ttAppId = new BigInteger(ttAppId);
+ this.ttAppId = ttAppId;
+ try {
+ ttAppIds = (ttAppId.replace(" ", "")).split(",");
+ ttFirstAppId = new BigInteger(ttAppIds[0]);
+ } catch (Throwable throwable) {
+ }
return this;
}
@@ -622,7 +678,9 @@ public TTConfig setTTAppId(String ttAppId) {
* set app id
*/
public TTConfig setAppId(String apiId) {
- this.appId = apiId;
+ if (!TextUtils.isEmpty(apiId)) {
+ this.appId = apiId;
+ }
return this;
}
@@ -706,6 +764,18 @@ public TTConfig enableLimitedDataUse() {
lduModeSwitch = true;
return this;
}
+
+ /**
+ * to open the Auto In App Purchase Track
+ */
+ public TTConfig enableAutoIapTrack() {
+ autoIapTrack = true;
+ return this;
+ }
+
+ public boolean isAutoIapTrack() {
+ return autoIapTrack;
+ }
}
/**
@@ -725,6 +795,10 @@ public boolean log() {
}
}
+ public static boolean enableAutoIapTrack() {
+ return config != null && config.isAutoIapTrack();
+ }
+
public interface CrashListener {
void onCrash(Thread thread, Throwable ex);
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTActivityLifecycleCallbacksListener.java b/business-core/src/main/java/com/tiktok/appevents/TTActivityLifecycleCallbacksListener.java
index e75ef77..9bfaa53 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTActivityLifecycleCallbacksListener.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTActivityLifecycleCallbacksListener.java
@@ -6,10 +6,13 @@
package com.tiktok.appevents;
+import static com.tiktok.TikTokBusinessSdk.enableAutoIapTrack;
+
import androidx.annotation.NonNull;
import androidx.lifecycle.LifecycleOwner;
import com.tiktok.TikTokBusinessSdk;
+import com.tiktok.iap.TTInAppPurchaseWrapper;
import com.tiktok.util.TTLogger;
import com.tiktok.util.TTUtil;
import org.json.JSONObject;
@@ -47,6 +50,9 @@ public void onPause(@NonNull LifecycleOwner owner) {
bgStart = System.currentTimeMillis();
appEventLogger.stopScheduler();
isPaused = true;
+ if(enableAutoIapTrack()) {
+ TTInAppPurchaseWrapper.startBillingClient();
+ }
}
@Override
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTAppEvent.java b/business-core/src/main/java/com/tiktok/appevents/TTAppEvent.java
index bf0694f..0569f3c 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTAppEvent.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTAppEvent.java
@@ -10,7 +10,9 @@
import com.tiktok.util.TTLogger;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
public class TTAppEvent implements Serializable {
@@ -20,29 +22,36 @@ public static enum TTAppEventType{
identify
}
- private static final long serialVersionUID = 1L;
-
+ private static final long serialVersionUID = 2L;
+ private List tiktokAppIds = new ArrayList<>();
private TTAppEventType type;
private String eventName;
private Date timeStamp;
private String propertiesJson;
+ private String eventId;
private static AtomicLong counter = new AtomicLong(new Date().getTime() + 0L);
private Long uniqueId;
private TTUserInfo userInfo;
private static String TAG = TTAppEventsQueue.class.getCanonicalName();
private static TTLogger logger = new TTLogger(TAG, TikTokBusinessSdk.getLogLevel());
- TTAppEvent(TTAppEventType type, String eventName, String propertiesJson) {
- this(type, eventName, new Date(), propertiesJson);
+ TTAppEvent(TTAppEventType type, String eventName, String propertiesJson, String eventId, String[] ttAppId) {
+ this(type, eventName, new Date(), propertiesJson, eventId, ttAppId);
}
- TTAppEvent(TTAppEventType type, String eventName, Date timeStamp, String propertiesJson) {
+ TTAppEvent(TTAppEventType type, String eventName, Date timeStamp, String propertiesJson, String eventId, String[] ttAppId) {
this.type = type;
this.eventName = eventName;
this.timeStamp = timeStamp;
this.propertiesJson = propertiesJson;
+ this.eventId = eventId;
this.uniqueId = TTAppEvent.counter.getAndIncrement();
this.userInfo = TTUserInfo.sharedInstance.clone();
+ if (ttAppId != null && ttAppId.length > 0) {
+ for (int i = 0; i < ttAppId.length; i++) {
+ tiktokAppIds.add(ttAppId[i]);
+ }
+ }
}
public TTUserInfo getUserInfo() {
@@ -69,6 +78,14 @@ public void setTimeStamp(Date timeStamp) {
this.timeStamp = timeStamp;
}
+ public String getEventId() {
+ return eventId;
+ }
+
+ public void setEventId(String eventId) {
+ this.eventId = eventId;
+ }
+
public String getPropertiesJson() {
return propertiesJson;
}
@@ -81,13 +98,23 @@ public Long getUniqueId() {
return this.uniqueId;
}
+ public List getTiktokAppIds() {
+ return tiktokAppIds;
+ }
+
+ public void setTiktokAppIds(List tiktokAppIds) {
+ this.tiktokAppIds = tiktokAppIds;
+ }
+
@Override
public String toString() {
return "TTAppEvent{" +
"eventName='" + eventName + '\'' +
", timeStamp=" + timeStamp +
", propertiesJson='" + propertiesJson + '\'' +
+ ", eventId='" + eventId + '\'' +
", uniqueId=" + uniqueId +
+ ", tiktokAppIds=" + tiktokAppIds +
'}';
}
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTAppEventLogger.java b/business-core/src/main/java/com/tiktok/appevents/TTAppEventLogger.java
index a0443ec..b2c99ed 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTAppEventLogger.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTAppEventLogger.java
@@ -6,6 +6,10 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
+import android.text.TextUtils;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
@@ -119,7 +123,7 @@ public void trackPurchase(List purchaseInfos) {
for (TTPurchaseInfo purchaseInfo : purchaseInfos) {
JSONObject property = TTInAppPurchaseManager.getPurchaseProps(purchaseInfo);
if (property != null) {
- track("Purchase", property);
+ track("Purchase", property, purchaseInfo.getEventId());
}
}
});
@@ -171,7 +175,7 @@ void stopScheduler() {
}
}
- public void identify(String externalId,
+ public boolean identify(String externalId,
@Nullable String externalUserName,
@Nullable String phoneNumber,
@Nullable String email) {
@@ -179,15 +183,16 @@ public void identify(String externalId,
if (sharedInstance.isIdentified()) {
logger.warn("SDK is already identified, if you want to switch to another" +
"user account, plz call TiktokBusinessSDK.logout() first and then identify");
- return;
+ return false;
}
sharedInstance.setIdentified();
sharedInstance.setExternalId(externalId);
sharedInstance.setExternalUserName(externalUserName);
sharedInstance.setPhoneNumber(phoneNumber);
sharedInstance.setEmail(email);
- trackEvent(TTAppEvent.TTAppEventType.identify, null, null);
+ trackEvent(TTAppEvent.TTAppEventType.identify, null, null, null);
flushWithReason(TTAppEventLogger.FlushReason.IDENTIFY);
+ return true;
}
public void logout() {
@@ -202,11 +207,14 @@ public void logout() {
* @param props
*/
public void track(String event, @Nullable JSONObject props) {
- trackEvent(TTAppEvent.TTAppEventType.track, event, props);
+ trackEvent(TTAppEvent.TTAppEventType.track, event, props, null);
+ }
+ public void track(String event, @Nullable JSONObject props, String eventId) {
+ trackEvent(TTAppEvent.TTAppEventType.track, event, props, eventId);
}
- private void trackEvent(TTAppEvent.TTAppEventType type, String event, @Nullable JSONObject props) {
- if (!TikTokBusinessSdk.isSystemActivated()) {
+ private void trackEvent(TTAppEvent.TTAppEventType type, String event, @Nullable JSONObject props, String eventId) {
+ if (!TikTokBusinessSdk.isSystemActivated() || TextUtils.isEmpty(TikTokBusinessSdk.getAppId())) {
return;
}
@@ -216,7 +224,7 @@ private void trackEvent(TTAppEvent.TTAppEventType type, String event, @Nullable
logger.debug("track " + event + " : " + finalProps.toString(4));
} catch (JSONException ignored) {}
- TTAppEventsQueue.addEvent(new TTAppEvent(type, event, finalProps.toString()));
+ TTAppEventsQueue.addEvent(new TTAppEvent(type, event, finalProps.toString(), eventId, TikTokBusinessSdk.getTTAppIds()));
if (TTAppEventsQueue.size() > THRESHOLD) {
flush(FlushReason.THRESHOLD);
@@ -285,7 +293,7 @@ void flush(FlushReason reason) {
TTAppEventStorage.persist(null);
}
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
if (flushSize != 0) {
@@ -325,7 +333,7 @@ private void addToQ(Runnable task) {
try {
eventLoop.execute(task);
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
}
@@ -336,7 +344,7 @@ private void addToLater(Runnable task, int seconds) {
try {
eventLoop.schedule(task, seconds, TimeUnit.SECONDS);
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTAppEventStorage.java b/business-core/src/main/java/com/tiktok/appevents/TTAppEventStorage.java
index f825be8..16a584c 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTAppEventStorage.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTAppEventStorage.java
@@ -6,6 +6,8 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
import android.content.Context;
import com.tiktok.TikTokBusinessSdk;
@@ -13,10 +15,9 @@
import com.tiktok.util.TTUtil;
import org.json.JSONObject;
-import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.ObjectInputStream;
+import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -62,7 +63,7 @@ public synchronized static void persist(List failedEvents) {
toBeSaved.addEvents(eventsFromDisk.getAppEvents());
toBeSaved.addEvents(eventsFromMemory);
- //If end up persisting more than 10,000 events, persist the latest 10,000 events by timestamp
+ //If end up persisting more than 500 events, persist the latest 500 events by timestamp
discardOldEvents(toBeSaved, MAX_PERSIST_EVENTS_NUM);
saveToDisk(toBeSaved);
}
@@ -111,7 +112,7 @@ private static boolean saveToDisk(TTAppEventPersist appEventPersist) {
}
success = true;
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
try {
long endTimeMS = System.currentTimeMillis();
@@ -142,9 +143,8 @@ synchronized static TTAppEventPersist readFromDisk() {
TTAppEventPersist appEventPersist = new TTAppEventPersist();
- try (ObjectInputStream ois = new ObjectInputStream(
- new BufferedInputStream(context.openFileInput(EVENT_STORAGE_FILE)))) {
- appEventPersist = (TTAppEventPersist) ois.readObject();
+ try (FileInputStream ois = context.openFileInput(EVENT_STORAGE_FILE)) {
+ appEventPersist = TTSafeReadObjectUtil.safeReadTTAppEventPersist(ois);
logger.debug("disk read data: %s", appEventPersist);
deleteFile(f);
if (TikTokBusinessSdk.diskListener != null) {
@@ -152,7 +152,7 @@ synchronized static TTAppEventPersist readFromDisk() {
}
} catch (Exception e) {
deleteFile(f);
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
try {
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTCrashHandler.java b/business-core/src/main/java/com/tiktok/appevents/TTCrashHandler.java
index c069a02..ec500ea 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTCrashHandler.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTCrashHandler.java
@@ -38,10 +38,10 @@ public class TTCrashHandler {
static TTCrashReport crashReport = new TTCrashReport();
- public static void handleCrash(String originTag, Throwable ex) {
+ public static void handleCrash(String originTag, Throwable ex, int type) {
ttLogger.error(ex, "Error caused by sdk at " + originTag +
"\n" + ex.getMessage() + "\n" + getStackTrace(ex));
- persistException(ex);
+ persistException(ex, type);
}
public static void retryLater(JSONObject monitor) {
@@ -119,11 +119,11 @@ public void addReport(String o, long t, int a) {
}
}
- private static void persistException(Throwable ex) {
+ private static void persistException(Throwable ex, int type) {
JSONObject stat = null;
try {
stat = TTRequestBuilder.getHealthMonitorBase();
- JSONObject monitor = TTUtil.getMonitorException(ex, null);
+ JSONObject monitor = TTUtil.getMonitorException(ex, null, type);
stat.put("monitor", monitor);
crashReport.addReport(stat.toString(), System.currentTimeMillis(), 0);
saveToFile(crashReport);
@@ -161,9 +161,7 @@ private static TTCrashReport readFromFile() {
Context context = TikTokBusinessSdk.getApplicationContext();
try {
FileInputStream fis = context.openFileInput(CRASH_REPORT_FILE);
- ObjectInputStream is = new ObjectInputStream(fis);
- meta = (TTCrashReport) is.readObject();
- is.close();
+ meta = TTSafeReadObjectUtil.safeReadTTCrashHandler(fis);
fis.close();
} catch (Exception ignored) {}
return meta;
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTInAppPurchaseManager.java b/business-core/src/main/java/com/tiktok/appevents/TTInAppPurchaseManager.java
index 167150f..6814ffb 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTInAppPurchaseManager.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTInAppPurchaseManager.java
@@ -6,6 +6,8 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -26,7 +28,7 @@ static JSONObject getPurchaseProps(TTPurchaseInfo purchaseInfo) {
JSONObject skuDetail = purchaseInfo.getSkuDetails();
return getPurchaseProperties(productId, skuDetail);
} catch (JSONException e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
return null;
}
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTPurchaseInfo.java b/business-core/src/main/java/com/tiktok/appevents/TTPurchaseInfo.java
index 559f8e5..cb0373c 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTPurchaseInfo.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTPurchaseInfo.java
@@ -11,6 +11,7 @@
public class TTPurchaseInfo {
private final JSONObject purchase;
private final JSONObject skuDetails;
+ private String eventId;
public static class InvalidTTPurchaseInfoException extends Exception {
@@ -41,6 +42,11 @@ public TTPurchaseInfo(JSONObject purchase, JSONObject skuDetails) throws Invalid
this.skuDetails = skuDetails;
}
+ public TTPurchaseInfo(JSONObject purchase, JSONObject skuDetails, String eventId) throws InvalidTTPurchaseInfoException {
+ this(purchase, skuDetails);
+ this.eventId = eventId;
+ }
+
public JSONObject getPurchase() {
return purchase;
}
@@ -82,4 +88,8 @@ private boolean isValidSkuDetails(JSONObject skuDetails) {
return !skuDetails.isNull("price")
&& !skuDetails.isNull("productId");
}
+
+ public String getEventId() {
+ return eventId;
+ }
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTRequest.java b/business-core/src/main/java/com/tiktok/appevents/TTRequest.java
index 4193da8..4cb2564 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTRequest.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTRequest.java
@@ -6,6 +6,10 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
+import android.text.TextUtils;
+
import com.tiktok.BuildConfig;
import com.tiktok.TikTokBusinessSdk;
import com.tiktok.util.HttpRequestUtil;
@@ -71,10 +75,10 @@ public static JSONObject getBusinessSDKConfig(Map options) {
// for fix bug in lower Android API edition. Maybe there is something wrong with language package, url can not be parsed successfully with some special char
// paramsMap.put("app_name", SystemInfoUtil.getAppName());
paramsMap.put("app_version", SystemInfoUtil.getAppVersionName());
- paramsMap.put("tiktok_app_id", TikTokBusinessSdk.getTTAppId());
+ paramsMap.put("tiktok_app_id", TikTokBusinessSdk.getFirstTTAppIds());
paramsMap.putAll(options);
- String url = "https://business-api.tiktok.com/open_api/business_sdk_config/get/?" + TTUtil.mapToString(paramsMap, "&");
+ String url = TTUtil.mapToString("https://business-api.tiktok.com/open_api/business_sdk_config/get/", paramsMap);
logger.debug(url);
String result = HttpRequestUtil.doGet(url, getHeadParamMap);
logger.debug(result);
@@ -89,7 +93,7 @@ public static JSONObject getBusinessSDKConfig(Map options) {
logger.info("Global config fetched: " + TTUtil.ppStr(config));
} catch (Exception e) {
// might be api returning something wrong
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
}
try {
@@ -162,7 +166,7 @@ public static synchronized List reportAppEvent(JSONObject basePayloa
bodyJson.put("batch", new JSONArray(batch));
} catch (Exception e) {
failedEventsToBeSaved.addAll(currentBatch);
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
continue;
}
@@ -207,7 +211,7 @@ else if (code == TTConst.ApiErrorCodes.PARTIAL_SUCCESS.code) {
}
}
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
failedEventsToBeSaved.addAll(currentBatch);
failedRequests += currentBatch.size();
}
@@ -221,7 +225,7 @@ else if (code == TTConst.ApiErrorCodes.PARTIAL_SUCCESS.code) {
} catch (JSONException e) {
failedRequests += currentBatch.size();
failedEventsToBeSaved.addAll(currentBatch);
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
logger.debug(TTUtil.ppStr(result));
}
@@ -280,6 +284,9 @@ private static JSONObject transferJson(TTAppEvent event) {
if (event.getEventName() != null) {
propertiesJson.put("event", event.getEventName());
}
+ if (!TextUtils.isEmpty(event.getEventId())) {
+ propertiesJson.put("event_id", event.getEventId());
+ }
propertiesJson.put("timestamp", TimeUtil.getISO8601Timestamp(event.getTimeStamp()));
if (TikTokBusinessSdk.isInSdkLDUMode()) {
propertiesJson.put("limited_data_use", true);
@@ -291,7 +298,7 @@ private static JSONObject transferJson(TTAppEvent event) {
propertiesJson.put("context", TTRequestBuilder.getContextForApi(event));
return propertiesJson;
} catch (JSONException e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
return null;
}
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTRequestBuilder.java b/business-core/src/main/java/com/tiktok/appevents/TTRequestBuilder.java
index c6629ea..8d7160f 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTRequestBuilder.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTRequestBuilder.java
@@ -6,6 +6,8 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
import android.content.Context;
import android.os.Build;
@@ -16,6 +18,7 @@
import com.tiktok.util.SystemInfoUtil;
import com.tiktok.util.TTUtil;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -38,14 +41,14 @@ public static JSONObject getBasePayload() {
if (TikTokBusinessSdk.onlyAppIdProvided()) {// to be compatible with the old versions
result.put("app_id", TikTokBusinessSdk.getAppId());
} else {
- result.put("tiktok_app_id", TikTokBusinessSdk.getTTAppId());
+ result.put("tiktok_app_id", TikTokBusinessSdk.getFirstTTAppIds());
}
if (TikTokBusinessSdk.isInSdkDebugMode()) {
- result.put("test_event_code", TikTokBusinessSdk.getTestEventCode());
+ result.put("test_event_code", String.valueOf(TikTokBusinessSdk.getFirstTTAppIds()));
}
result.put("event_source", "APP_EVENTS_SDK");
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
basePayloadCache = new JSONObject();
return basePayloadCache;
}
@@ -56,8 +59,10 @@ public static JSONObject getBasePayload() {
private static JSONObject contextForApiCache = null;
// the context part that does not change
- private static JSONObject getImmutableContextForApi() throws JSONException {
+ private static JSONObject getImmutableContextForApi(TTAppEvent event) throws JSONException {
if (contextForApiCache != null) {
+ freshTTAppID(contextForApiCache, event);
+ freshOsVersion(contextForApiCache, event);
return contextForApiCache;
}
TTIdentifierFactory.AdIdInfo adIdInfo = null;
@@ -75,11 +80,49 @@ private static JSONObject getImmutableContextForApi() throws JSONException {
TikTokBusinessSdk.getAppEventLogger().monitorMetric("did_end", meta, null);
} catch (Exception ignored) {}
contextForApiCache = contextBuilder(adIdInfo);
+ freshTTAppID(contextForApiCache, event);
+ freshOsVersion(contextForApiCache, event);
return contextForApiCache;
}
+ private static void freshTTAppID(JSONObject contextForApiCache, TTAppEvent event) {
+ try {
+ JSONObject app = contextForApiCache.getJSONObject("app");
+ if (event != null && app != null) {
+ JSONArray tiktokAppIds = new JSONArray();
+ if (event.getTiktokAppIds() != null) {
+ for (int i = 0; i < event.getTiktokAppIds().size(); i++) {
+ tiktokAppIds.put(event.getTiktokAppIds().get(i));
+ }
+ app.remove("tiktok_app_id");
+ app.put("tiktok_app_ids", tiktokAppIds);
+ }
+ } else {
+ app.remove("tiktok_app_ids");
+ app.put("tiktok_app_id", TikTokBusinessSdk.getTTAppId());
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+
+ private static void freshOsVersion(JSONObject contextForApiCache, TTAppEvent event) {
+ try {
+ JSONObject device = contextForApiCache.getJSONObject("device");
+ if (event != null && device != null) {
+ device.put("os_version", SystemInfoUtil.getAndroidVersion());
+ device.remove("version");
+ } else {
+ device.put("version", SystemInfoUtil.getAndroidVersion());
+ device.remove("os_version");
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ }
+
public static JSONObject getContextForApi(TTAppEvent event) throws JSONException {
- JSONObject immutablePart = getImmutableContextForApi();
+ JSONObject immutablePart = getImmutableContextForApi(event);
JSONObject finalObj = new JSONObject(immutablePart.toString());
finalObj.put("user", event.getUserInfo().toJsonObject());
return finalObj;
@@ -159,7 +202,6 @@ private static JSONObject contextBuilder(@Nullable TTIdentifierFactory.AdIdInfo
JSONObject device = new JSONObject();
device.put("platform", "Android");
- device.put("version", SystemInfoUtil.getAndroidVersion());
if (adIdInfo != null) {
device.put("gaid", adIdInfo.getAdId());
}
@@ -201,11 +243,11 @@ public static JSONObject getHealthMonitorBase() throws JSONException {
return healthBasePayloadCache;
}
JSONObject finalObj = new JSONObject();
- JSONObject app = new JSONObject(getImmutableContextForApi().getJSONObject("app").toString());
+ JSONObject app = new JSONObject(getImmutableContextForApi(null).getJSONObject("app").toString());
app.put("app_namespace", SystemInfoUtil.getPackageName());
finalObj.put("app", app);
- finalObj.put("library", getImmutableContextForApi().get("library"));
- finalObj.put("device", enrichDeviceBase(getImmutableContextForApi().getJSONObject("device")));
+ finalObj.put("library", getImmutableContextForApi(null).get("library"));
+ finalObj.put("device", enrichDeviceBase(getImmutableContextForApi(null).getJSONObject("device")));
finalObj.put("log_extra", null);
healthBasePayloadCache = finalObj;
return healthBasePayloadCache;
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTSafeReadObjectUtil.java b/business-core/src/main/java/com/tiktok/appevents/TTSafeReadObjectUtil.java
new file mode 100644
index 0000000..236648f
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/TTSafeReadObjectUtil.java
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2024. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+package com.tiktok.appevents;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectStreamClass;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class TTSafeReadObjectUtil {
+ /**
+ * Safely read the object and do some security checks
+ */
+
+ public static TTAppEventPersist safeReadTTAppEventPersist(InputStream in) throws IOException, ClassNotFoundException {
+ List> safeClasses = new ArrayList<>();
+ safeClasses.add(TTAppEventPersist.class);
+ safeClasses.add(ArrayList.class);
+ safeClasses.add(TTAppEvent.class);
+ safeClasses.add(Enum.class);
+ safeClasses.add(String.class);
+ safeClasses.add(Date.class);
+ safeClasses.add(Long.class);
+ safeClasses.add(TTUserInfo.class);
+ safeClasses.add(TTAppEvent.TTAppEventType.class);
+ return safeReadObjects(safeClasses, Long.MAX_VALUE, Long.MAX_VALUE, in);
+ }
+
+ public static TTCrashHandler.TTCrashReport safeReadTTCrashHandler(InputStream in) throws IOException, ClassNotFoundException {
+ List> safeClasses = new ArrayList<>();
+ safeClasses.add(TTCrashHandler.TTCrashReport.class);
+ safeClasses.add(TTCrashHandler.TTCrashReport.Monitor.class);
+ safeClasses.add(String.class);
+ safeClasses.add(Long.class);
+ safeClasses.add(Integer.class);
+ safeClasses.add(ArrayList.class);
+ return safeReadObjects(safeClasses, Long.MAX_VALUE, Long.MAX_VALUE, in);
+ }
+
+ public static T safeReadObjects(List> safeClasses, long maxObjects, long maxBytes, InputStream in) throws IOException, ClassNotFoundException {
+ InputStream lis = new FilterInputStream(in) {
+ private long length = 0;
+
+ public int read() throws IOException {
+ int count = super.read();
+ if (count != -1) {
+ length++;
+ checkLength();
+ }
+ return count;
+ }
+
+ public int read(byte[] b, int off, int readLength) throws IOException {
+ int count = super.read(b, off, readLength);
+ if (count > 0) {
+ length += count;
+ checkLength();
+ }
+ return count;
+ }
+
+ private void checkLength() {
+ if (length > maxBytes) {
+ throw new SecurityException("too many bytes from stream. Limit is " + maxBytes);
+ }
+ }
+ };
+ ObjectInputStream ois = new ObjectInputStream(lis) {
+ private int objCount = 0;
+ boolean enableResolve = enableResolveObject(true);
+
+ protected Object resolveObject(Object obj) throws IOException {
+ if (objCount++ > maxObjects)
+ throw new SecurityException("too many objects from stream. Limit is " + maxObjects);
+ Object object = super.resolveObject(obj);
+ return object;
+ }
+
+ protected Class> resolveClass(ObjectStreamClass osc) throws IOException, ClassNotFoundException {
+ Class> clazz = super.resolveClass(osc);
+ if (clazz.isArray() || clazz.equals(String.class) || Number.class.isAssignableFrom(clazz) || safeClasses.contains(clazz))
+ return clazz;
+ throw new SecurityException("deserialize unauthorized " + clazz);
+ }
+ };
+ T t = (T) ois.readObject();
+ try{
+ in.close();
+ lis.close();
+ ois.close();
+ }catch (Throwable throwable){
+ throwable.printStackTrace();
+ }
+ return t;
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTThreadFactory.java b/business-core/src/main/java/com/tiktok/appevents/TTThreadFactory.java
index bb959a8..f78c9fa 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTThreadFactory.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTThreadFactory.java
@@ -6,6 +6,8 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_CRASH;
+
import androidx.annotation.NonNull;
import com.tiktok.TikTokBusinessSdk;
@@ -20,7 +22,7 @@ public Thread newThread(Runnable r) {
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
- TTCrashHandler.handleCrash(TAG, throwable);
+ TTCrashHandler.handleCrash(TAG, throwable, TTSDK_EXCEPTION_CRASH);
if (TikTokBusinessSdk.getCrashListener() != null) {
TikTokBusinessSdk.getCrashListener().onCrash(thread, throwable);
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/TTUserInfo.java b/business-core/src/main/java/com/tiktok/appevents/TTUserInfo.java
index 19fd70f..ce7b268 100644
--- a/business-core/src/main/java/com/tiktok/appevents/TTUserInfo.java
+++ b/business-core/src/main/java/com/tiktok/appevents/TTUserInfo.java
@@ -6,6 +6,8 @@
package com.tiktok.appevents;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
import android.content.Context;
import com.tiktok.util.TTUtil;
@@ -49,7 +51,7 @@ private String toSha256(String str) {
}
return result.toString();
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
return null;
}
@@ -95,7 +97,7 @@ public JSONObject toJsonObject() {
jsonObject.put("email", email);
}
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_SDK_CATCH);
}
return jsonObject;
}
diff --git a/business-core/src/main/java/com/tiktok/appevents/base/EventName.java b/business-core/src/main/java/com/tiktok/appevents/base/EventName.java
new file mode 100644
index 0000000..69790f4
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/base/EventName.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.base;
+
+public enum EventName {
+ ACHIEVE_LEVEL("AchieveLevel"),
+ ADD_PAYMENT_INFO("AddPaymentInfo"),
+ COMPLETE_TUTORIAL("CompleteTutorial"),
+ CREATE_GROUP("CreateGroup"),
+ CREATE_ROLE("CreateRole"),
+ GENERATE_LEAD("GenerateLead"),
+ IN_APP_AD_CLICK("InAppADClick"),
+ IN_APP_AD_IMPR("InAppAdImpr"),
+ INSTALL_APP("InstallApp"),
+ JOIN_GROUP("JoinGroup"),
+ LAUNCH_APP("LaunchAPP"),
+ LOAN_APPLICATION("LoanApplication"),
+ LOAN_APPROVAL("LoanApproval"),
+ LOAN_DISBURSAL("LoanDisbursal"),
+ LOGIN("Login"),
+ RATE("Rate"),
+ REGISTRATION("Registration"),
+ SEARCH("Search"),
+ SPEND_CREDITS("SpendCredits"),
+ START_TRIAL("StartTrial"),
+ SUBSCRIBE("Subscribe"),
+ UNLOCK_ACHIEVEMENT("UnlockAchievement");
+ private String eventName;
+
+ EventName(String eventName) {
+ this.eventName = eventName;
+ }
+
+ @Override
+ public String toString() {
+ return eventName;
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/base/TTBaseEvent.java b/business-core/src/main/java/com/tiktok/appevents/base/TTBaseEvent.java
new file mode 100644
index 0000000..992d8a6
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/base/TTBaseEvent.java
@@ -0,0 +1,86 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.base;
+
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class TTBaseEvent {
+ public JSONObject properties;
+ public String eventName;
+ public String eventId;
+
+ public TTBaseEvent(String eventName, JSONObject properties, String eventId) {
+ this.eventName = eventName;
+ this.properties = properties;
+ this.eventId = eventId;
+ }
+
+ public static Builder newBuilder(String eventName) {
+ return new Builder(eventName);
+ }
+
+ public static Builder newBuilder(String eventName, String eventId) {
+ return new Builder(eventName, eventId);
+ }
+
+ public static class Builder {
+ public JSONObject properties = new JSONObject();
+ public String eventName;
+ public String eventId;
+ public Builder(String eventName){
+ this.eventName = eventName;
+ }
+ public Builder(String eventName, String eventId){
+ this.eventName = eventName;
+ this.eventId = eventId;
+ }
+ public Builder addProperty(String key, Object value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ public Builder addProperty(String key, String value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ public Builder addProperty(String key, boolean value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ public Builder addProperty(String key, double value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ public Builder addProperty(String key, int value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ public Builder addProperty(String key, long value) {
+ safeAddProperty(key, value);
+ return this;
+ }
+
+ private void safeAddProperty(String key, Object value) {
+ try {
+ properties.put(key, value);
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public TTBaseEvent build() {
+ TTBaseEvent event = new TTBaseEvent(eventName, properties, eventId);
+ return event;
+ }
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToCartEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToCartEvent.java
new file mode 100644
index 0000000..b00c0c7
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToCartEvent.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_CARD;
+
+import org.json.JSONObject;
+
+public class TTAddToCartEvent extends TTContentsEvent {
+
+ TTAddToCartEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder(EVENT_NAME_ADD_TO_CARD, "");
+ }
+
+ public static Builder newBuilder(String eventId) {
+ return new Builder(EVENT_NAME_ADD_TO_CARD, eventId);
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToWishlistEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToWishlistEvent.java
new file mode 100644
index 0000000..7909c25
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTAddToWishlistEvent.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_WISHLIST;
+
+import org.json.JSONObject;
+
+public class TTAddToWishlistEvent extends TTContentsEvent {
+
+ TTAddToWishlistEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder(EVENT_NAME_ADD_TO_WISHLIST, "");
+ }
+
+ public static Builder newBuilder(String eventId) {
+ return new Builder(EVENT_NAME_ADD_TO_WISHLIST, eventId);
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTCheckoutEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTCheckoutEvent.java
new file mode 100644
index 0000000..5352714
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTCheckoutEvent.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_CHECK_OUT;
+
+import org.json.JSONObject;
+
+public class TTCheckoutEvent extends TTContentsEvent {
+
+ TTCheckoutEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder(EVENT_NAME_CHECK_OUT, "");
+ }
+
+ public static Builder newBuilder(String eventId) {
+ return new Builder(EVENT_NAME_CHECK_OUT, eventId);
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTContentParams.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentParams.java
new file mode 100644
index 0000000..40d491f
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentParams.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+
+import android.text.TextUtils;
+
+import org.json.JSONObject;
+
+public class TTContentParams {
+ private float price;
+ private int quantity;
+ private String contentId;
+ private String contentCategory;
+ private String contentName;
+ private String brand;
+ private boolean priceAvailable = false;
+ private boolean quantityAvailable = false;
+
+ public static TTContentParams.Builder newBuilder() {
+ return new TTContentParams.Builder();
+ }
+
+ public static class Builder {
+ private float price = Float.NaN;
+ private int quantity = -1;
+ private String contentId;
+ private String contentCategory;
+ private String contentName;
+ private String brand;
+ private boolean priceAvailable = false;
+ private boolean quantityAvailable = false;
+
+ public Builder setPrice(float price) {
+ this.price = price;
+ priceAvailable = true;
+ return this;
+ }
+
+ public Builder setQuantity(int quantity) {
+ this.quantity = quantity;
+ quantityAvailable = true;
+ return this;
+ }
+
+ public Builder setContentId(String contentId) {
+ this.contentId = contentId;
+ return this;
+ }
+
+ public Builder setContentCategory(String contentCategory) {
+ this.contentCategory = contentCategory;
+ return this;
+ }
+
+ public Builder setContentName(String contentName) {
+ this.contentName = contentName;
+ return this;
+ }
+
+ public Builder setBrand(String brand) {
+ this.brand = brand;
+ return this;
+ }
+
+ public TTContentParams build() {
+ TTContentParams params = new TTContentParams();
+ params.price = price;
+ params.priceAvailable = priceAvailable;
+ params.quantity = quantity;
+ params.quantityAvailable = quantityAvailable;
+ params.contentId = contentId;
+ params.contentCategory = contentCategory;
+ params.contentName = contentName;
+ params.brand = brand;
+ return params;
+ }
+ }
+
+ public JSONObject toJSONObject(){
+ JSONObject jsonObject = null;
+ try{
+ jsonObject = new JSONObject();
+ if(quantityAvailable) {
+ jsonObject.put("quantity", quantity);
+ }
+ if(!TextUtils.isEmpty(contentId)){
+ jsonObject.put("content_id", contentId);
+ }
+ if(!TextUtils.isEmpty(contentCategory)) {
+ jsonObject.put("content_category", contentCategory);
+ }
+ if(!TextUtils.isEmpty(contentName)) {
+ jsonObject.put("content_name", contentName);
+ }
+ if(!TextUtils.isEmpty(brand)) {
+ jsonObject.put("brand", brand);
+ }
+ if (priceAvailable && price != Float.NaN) {
+ jsonObject.put("price", price);
+ }
+ }catch (Throwable e){
+ e.printStackTrace();
+ }
+ return jsonObject;
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEvent.java
new file mode 100644
index 0000000..59b539e
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEvent.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENTS;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_ID;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_TYPE;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CURRENCY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_DESCRIPTION;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_VALUE;
+
+import android.text.TextUtils;
+
+import com.tiktok.appevents.base.TTBaseEvent;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class TTContentsEvent extends TTBaseEvent {
+ TTContentsEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static class Builder extends TTBaseEvent.Builder{
+
+ Builder(String eventName, String eventId) {
+ super(eventName, eventId);
+ }
+
+ public Builder setDescription(String description) {
+ if(!TextUtils.isEmpty(description)){
+ addProperty(EVENT_PROPERTY_DESCRIPTION, description);
+ }
+ return this;
+ }
+
+ public Builder setCurrency(TTContentsEventConstants.Currency currency) {
+ if(currency != null) {
+ addProperty(EVENT_PROPERTY_CURRENCY, currency);
+ }
+ return this;
+ }
+
+ public Builder setValue(double value) {
+ safeAddProperty(EVENT_PROPERTY_VALUE, value);
+ return this;
+ }
+
+ public Builder setContentType(String contentType) {
+ if(!TextUtils.isEmpty(contentType)) {
+ addProperty(EVENT_PROPERTY_CONTENT_TYPE, contentType);
+ }
+ return this;
+ }
+
+ public Builder setContentId(String contentId) {
+ if(!TextUtils.isEmpty(contentId)) {
+ addProperty(EVENT_PROPERTY_CONTENT_ID, contentId);
+ }
+ return this;
+ }
+
+ public Builder setContents(TTContentParams... contents) {
+ if (contents != null) {
+ JSONArray jsonArray = new JSONArray();
+ for (TTContentParams content : contents) {
+ if(content != null) {
+ jsonArray.put(content.toJSONObject());
+ }
+ }
+ addProperty(EVENT_PROPERTY_CONTENTS, jsonArray);
+ }
+ return this;
+ }
+
+ private void safeAddProperty(String key, Object value) {
+ try {
+ properties.put(key, value);
+ } catch (Throwable e) {}
+ }
+
+ public TTContentsEvent build() {
+ TTContentsEvent event = new TTContentsEvent(eventName, properties, eventId);
+ return event;
+ }
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEventConstants.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEventConstants.java
new file mode 100644
index 0000000..b59b86d
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTContentsEventConstants.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+public interface TTContentsEventConstants {
+ interface ContentsEventName {
+ String EVENT_NAME_ADD_TO_CARD = "AddToCart";
+ String EVENT_NAME_ADD_TO_WISHLIST = "AddToWishlist";
+ String EVENT_NAME_CHECK_OUT = "Checkout";
+ String EVENT_NAME_PURCHASE = "Purchase";
+ String EVENT_NAME_VIEW_CONTENT = "ViewContent";
+
+ }
+
+ interface Params {
+ String EVENT_PROPERTY_CONTENT_TYPE = "content_type";
+ String EVENT_PROPERTY_CONTENT_ID = "content_id";
+ String EVENT_PROPERTY_DESCRIPTION = "description";
+ String EVENT_PROPERTY_CURRENCY = "currency";
+ String EVENT_PROPERTY_VALUE = "value";
+ String EVENT_PROPERTY_CONTENTS = "contents";
+ }
+
+ enum Currency {
+ AED, ARS, AUD, BDT, BHD, BIF, BOB, BRL, CAD, CHF, CLP, CNY, COP, CRC, CZK, DKK, DZD, EGP,
+ EUR, GBP, GTQ, HKD, HNL, HUF, IDR, ILS, INR, ISK, JPY, KES, KHR, KRW, KWD, KZT, MAD, MOP,
+ MXN, MYR, NGN, NIO, NOK, NZD, OMR, PEN, PHP, PKR, PLN, PYG, QAR, RON, RUB, SAR, SEK, SGD,
+ THB, TRY, TWD, UAH, USD, VES, VND, ZAR
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTPurchaseEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTPurchaseEvent.java
new file mode 100644
index 0000000..b7e8414
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTPurchaseEvent.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_PURCHASE;
+
+import org.json.JSONObject;
+
+public class TTPurchaseEvent extends TTContentsEvent {
+
+ TTPurchaseEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder(EVENT_NAME_PURCHASE, "");
+ }
+
+ public static Builder newBuilder(String eventId) {
+ return new Builder(EVENT_NAME_PURCHASE, eventId);
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/appevents/contents/TTViewContentEvent.java b/business-core/src/main/java/com/tiktok/appevents/contents/TTViewContentEvent.java
new file mode 100644
index 0000000..f1911aa
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/appevents/contents/TTViewContentEvent.java
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.appevents.contents;
+
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_VIEW_CONTENT;
+
+import org.json.JSONObject;
+
+public class TTViewContentEvent extends TTContentsEvent {
+
+ TTViewContentEvent(String eventName, JSONObject properties, String eventId) {
+ super(eventName, properties, eventId);
+ }
+
+ public static Builder newBuilder() {
+ return new Builder(EVENT_NAME_VIEW_CONTENT, "");
+ }
+
+ public static Builder newBuilder(String eventId) {
+ return new Builder(EVENT_NAME_VIEW_CONTENT, eventId);
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/iap/TTInAppPurchaseWrapper.java b/business-core/src/main/java/com/tiktok/iap/TTInAppPurchaseWrapper.java
new file mode 100644
index 0000000..975393b
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/iap/TTInAppPurchaseWrapper.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.tiktok.iap;
+
+import static com.tiktok.TikTokBusinessSdk.getApplicationContext;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+
+import com.android.billingclient.api.BillingClient;
+import com.android.billingclient.api.BillingClientStateListener;
+import com.android.billingclient.api.BillingResult;
+import com.android.billingclient.api.Purchase;
+import com.android.billingclient.api.PurchasesUpdatedListener;
+import com.android.billingclient.api.SkuDetails;
+import com.android.billingclient.api.SkuDetailsParams;
+import com.tiktok.TikTokBusinessSdk;
+import com.tiktok.appevents.TTPurchaseInfo;
+import com.tiktok.util.TTLogger;
+
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TTInAppPurchaseWrapper {
+
+ private static Context mContext;
+ private static BillingClient mBillingClient;
+ static final String TAG = TTInAppPurchaseWrapper.class.getName();
+
+ private static final TTLogger ttLogger = new TTLogger(TAG, TikTokBusinessSdk.getLogLevel());
+
+ public static void registerIapTrack() {
+ try {
+ if (getApplicationContext() == null) {
+ return;
+ }
+ mContext = getApplicationContext();
+ PurchasesUpdatedListener purchaseUpdateListener = (billingResult, purchases) -> {
+
+ if (billingResult != null && billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
+ && purchases != null) {
+ for (Purchase purchase : purchases) {
+ if (purchase == null) {
+ continue;
+ }
+ List skus = purchase.getSkus();
+ if (skus == null || skus.size() == 0) {
+ continue;
+ }
+ querySkuAndTrack(skus, purchase, true);
+ }
+ } else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
+ ttLogger.info("user canceled");
+ } else {
+ ttLogger.info("otherErr : %s", billingResult.getDebugMessage());
+ }
+ };
+ mBillingClient = BillingClient.newBuilder(mContext)
+ .setListener(purchaseUpdateListener)
+ .enablePendingPurchases()
+ .build();
+ startBillingClient();
+ } catch (Throwable ignored) {
+ ttLogger.error(ignored, "register Iap track error");
+ }
+ }
+
+ public static void startBillingClient() {
+ try {
+ if (mBillingClient == null || mBillingClient.isReady()) {
+ return;
+ }
+ mBillingClient.startConnection(new BillingClientStateListener() {
+ @Override
+ public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
+ if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
+ ttLogger.info("billing setup finished");
+ } else {
+ ttLogger.info("billing setup error %s", billingResult.getDebugMessage());
+ }
+ }
+
+ @Override
+ public void onBillingServiceDisconnected() {
+ ttLogger.info("billing service disconnected");
+ }
+ });
+ } catch (Throwable ignored) {
+ ttLogger.error(ignored, "start billing client connection error");
+ }
+ }
+
+ private static void querySkuAndTrack(List skus, Purchase purchase, boolean isInAppPurchase) {
+ try {
+ List skuList = new ArrayList<>();
+ for (String sku : skus) {
+ if (sku == null || sku.isEmpty()) {
+ continue;
+ }
+ skuList.add(sku);
+ }
+ SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
+ if (isInAppPurchase) {
+ params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
+ } else {
+ params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS);
+ }
+ mBillingClient.querySkuDetailsAsync(params.build(), (billingResult, skuDetailsList) -> {
+ if (billingResult != null && billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK
+ && skuDetailsList != null) {
+ if (skuDetailsList.size() > 0) {
+ List purchaseInfos = new ArrayList<>();
+ try {
+ for (SkuDetails skuDetails : skuDetailsList) {
+ purchaseInfos.add(new TTPurchaseInfo(new JSONObject(purchase.getOriginalJson()),
+ new JSONObject(skuDetails.getOriginalJson())));
+ }
+ TikTokBusinessSdk.trackGooglePlayPurchase(purchaseInfos);
+ } catch (Throwable e) {
+ ttLogger.error(e, "query Sku And Track google play purchase error");
+ }
+ } else {
+ if (isInAppPurchase) {
+ querySkuAndTrack(skus, purchase, false);
+ } else {
+ sendNoSkuIapTrack(skus, purchase);
+ }
+ }
+ } else {
+ sendNoSkuIapTrack(skus, purchase);
+ }
+ });
+ } catch (Throwable ignored) {
+ ttLogger.error(ignored, "query Sku And Track error");
+ }
+ }
+
+ private static void sendNoSkuIapTrack(List skus, Purchase purchase) {
+ try {
+ JSONArray contents = new JSONArray();
+ for (String sku : skus) {
+ if (sku == null || sku.isEmpty()) {
+ continue;
+ }
+ JSONObject item = new JSONObject()
+ .put("quantity", purchase.getQuantity())
+ .put("content_id", sku);
+ contents.put(item);
+ }
+ JSONObject content = new JSONObject().put("contents", contents);
+ TikTokBusinessSdk.trackEvent("Purchase", content);
+ } catch (Throwable ignored) {
+ ttLogger.error(ignored, "Track Purchase error");
+ }
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/util/HttpRequestUtil.java b/business-core/src/main/java/com/tiktok/util/HttpRequestUtil.java
index 709c213..ed4c594 100644
--- a/business-core/src/main/java/com/tiktok/util/HttpRequestUtil.java
+++ b/business-core/src/main/java/com/tiktok/util/HttpRequestUtil.java
@@ -6,6 +6,8 @@
package com.tiktok.util;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_NET_ERROR;
+
import androidx.annotation.Nullable;
import com.tiktok.TikTokBusinessSdk;
import com.tiktok.appevents.TTCrashHandler;
@@ -45,6 +47,8 @@ public void configConnection(HttpURLConnection connection) {
private static final String TAG = HttpRequestUtil.class.getCanonicalName();
+ private static final TTLogger ttLogger = new TTLogger(TAG, TikTokBusinessSdk.getLogLevel());
+
public static String doGet(String url, Map headerParamMap) {
HttpRequestOptions options = new HttpRequestOptions();
options.connectTimeout = 2000;
@@ -75,12 +79,12 @@ public static HttpsURLConnection connect(String url, Map headerP
connection.connect();
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
if (connection != null) {
try {
connection.disconnect();
}catch (Exception exc){
- TTCrashHandler.handleCrash(TAG, exc);
+ TTCrashHandler.handleCrash(TAG, exc, TTSDK_EXCEPTION_NET_ERROR);
}
}
}
@@ -126,13 +130,13 @@ public static String doGet(String url, Map headerParamMap, HttpR
result = streamToString(connection.getInputStream());
}
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
} finally {
if (connection != null) {
try {
connection.disconnect();
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
}
}
}
@@ -198,21 +202,25 @@ public static String doPost(String url, Map headerParamMap, Stri
if (responseCode == HttpURLConnection.HTTP_OK) {
result = streamToString(connection.getInputStream());
}
+ if(TikTokBusinessSdk.isInSdkDebugMode()) {
+ ttLogger.info("doPost request body: %s", jsonStr);
+ ttLogger.info("doPost result: %s", result == null ? String.valueOf(responseCode) : result);
+ }
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
}
}
if (connection != null) {
try {
connection.disconnect();
} catch (Exception e){
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
}
}
}
@@ -239,7 +247,7 @@ private static String streamToString(InputStream is) {
}
return sb.toString().trim();
} catch (Exception e) {
- TTCrashHandler.handleCrash(TAG, e);
+ TTCrashHandler.handleCrash(TAG, e, TTSDK_EXCEPTION_NET_ERROR);
}
return null;
}
diff --git a/business-core/src/main/java/com/tiktok/util/RegexUtil.java b/business-core/src/main/java/com/tiktok/util/RegexUtil.java
new file mode 100644
index 0000000..d86fd1a
--- /dev/null
+++ b/business-core/src/main/java/com/tiktok/util/RegexUtil.java
@@ -0,0 +1,20 @@
+package com.tiktok.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class RegexUtil {
+ public static boolean validateAppId(String appId) {
+ String appIdRegex = "^[a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)+$";
+ Pattern pattern = Pattern.compile(appIdRegex);
+ Matcher matcher = pattern.matcher(appId);
+ return matcher.matches();
+ }
+
+ public static boolean validateTTAppId(String ttAppId) {
+ String ttAppIdRegex = "^(\\d+,)*\\d+$";
+ Pattern pattern = Pattern.compile(ttAppIdRegex);
+ Matcher matcher = pattern.matcher(ttAppId);
+ return matcher.matches();
+ }
+}
diff --git a/business-core/src/main/java/com/tiktok/util/SystemInfoUtil.java b/business-core/src/main/java/com/tiktok/util/SystemInfoUtil.java
index ff50411..dcd4beb 100644
--- a/business-core/src/main/java/com/tiktok/util/SystemInfoUtil.java
+++ b/business-core/src/main/java/com/tiktok/util/SystemInfoUtil.java
@@ -6,6 +6,8 @@
package com.tiktok.util;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
+
import android.app.Application;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -112,7 +114,7 @@ public static void initUserAgent() {
if (userAgent == null) userAgent = "";
long endTimeMS = System.currentTimeMillis();
try {
- JSONObject meta = TTUtil.getMetaException(ex, endTimeMS)
+ JSONObject meta = TTUtil.getMetaException(ex, endTimeMS, TTSDK_EXCEPTION_SDK_CATCH)
.put("latency", endTimeMS-initTimeMS);
TikTokBusinessSdk.getAppEventLogger().monitorMetric("ua_end", meta, null);
} catch (Exception ignored) {}
diff --git a/business-core/src/main/java/com/tiktok/util/TTConst.java b/business-core/src/main/java/com/tiktok/util/TTConst.java
index c1ca429..d634ee5 100644
--- a/business-core/src/main/java/com/tiktok/util/TTConst.java
+++ b/business-core/src/main/java/com/tiktok/util/TTConst.java
@@ -15,6 +15,9 @@ public class TTConst {
public static final String TTSDK_APP_2DR_TIME = "com.tiktok.sdk.2drTime";
public static final String TTSDK_PREFIX = "com.tiktok";
+ public static final int TTSDK_EXCEPTION_NET_ERROR = 1;
+ public static final int TTSDK_EXCEPTION_SDK_CATCH = 2;
+ public static final int TTSDK_EXCEPTION_CRASH = 3;
public static enum ApiErrorCodes {
PARTIAL_SUCCESS(20001),
diff --git a/business-core/src/main/java/com/tiktok/util/TTUtil.java b/business-core/src/main/java/com/tiktok/util/TTUtil.java
index 768072f..dafc2b0 100644
--- a/business-core/src/main/java/com/tiktok/util/TTUtil.java
+++ b/business-core/src/main/java/com/tiktok/util/TTUtil.java
@@ -7,6 +7,7 @@
package com.tiktok.util;
import android.content.Context;
+import android.net.Uri;
import android.os.Looper;
import androidx.annotation.NonNull;
@@ -22,6 +23,7 @@
import java.util.UUID;
import static com.tiktok.util.TTConst.TTSDK_APP_ANONYMOUS_ID;
+import static com.tiktok.util.TTConst.TTSDK_EXCEPTION_SDK_CATCH;
public class TTUtil {
private static final String TAG = TTUtil.class.getName();
@@ -35,7 +37,7 @@ public class TTUtil {
*/
public static void checkThread(String tag) {
if (Looper.getMainLooper() == Looper.myLooper()) {
- TTCrashHandler.handleCrash(tag, new IllegalStateException("Current method should be called in a non-main thread"));
+ TTCrashHandler.handleCrash(tag, new IllegalStateException("Current method should be called in a non-main thread"), TTSDK_EXCEPTION_SDK_CATCH);
}
}
@@ -76,31 +78,15 @@ public static String getOrGenAnoId(Context context, boolean forceGenerate) {
return anoId;
}
- public static String escapeHTML(String s) {
- StringBuilder out = new StringBuilder(Math.max(16, s.length()));
- for (int i = 0; i < s.length(); i++) {
- char c = s.charAt(i);
- if (c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') {
- out.append("");
- out.append((int) c);
- out.append(';');
- } else {
- out.append(c);
- }
- }
- return out.toString();
- }
-
- public static String mapToString(Map map, String separator) {
+ public static String mapToString(String url, Map map) {
if (map.isEmpty()) {
- return "";
+ return url;
}
- StringBuffer buf = new StringBuffer();
+ Uri.Builder build = Uri.parse(url).buildUpon();
for (Map.Entry entry : map.entrySet()) {
- buf.append(entry.getKey() + "=" + escapeHTML(entry.getValue().toString()) + "&");
+ build.appendQueryParameter(entry.getKey(), entry.getValue().toString());
}
- String finalStr = buf.toString();
- return finalStr.substring(0, finalStr.length() - 1);
+ return build.toString();
}
public static JSONObject getMetaWithTS(@Nullable Long ts) {
@@ -113,18 +99,18 @@ public static JSONObject getMetaWithTS(@Nullable Long ts) {
return new JSONObject();
}
- public static JSONObject getMonitorException(@Nullable Throwable ex, @Nullable Long ts) {
+ public static JSONObject getMonitorException(@Nullable Throwable ex, @Nullable Long ts, int type) {
JSONObject monitor = new JSONObject();
try {
monitor.put("type", "exception");
monitor.put("name", "exception");
- monitor.put("meta", getMetaException(ex, ts));
+ monitor.put("meta", getMetaException(ex, ts, type));
monitor.put("extra", null);
} catch (Exception ignored) {}
return monitor;
}
- public static JSONObject getMetaException(@Nullable Throwable ex, @Nullable Long ts) {
+ public static JSONObject getMetaException(@Nullable Throwable ex, @Nullable Long ts, int type) {
JSONObject meta = getMetaWithTS(ts);
try {
if (ex != null) {
@@ -137,6 +123,7 @@ public static JSONObject getMetaException(@Nullable Throwable ex, @Nullable Long
" " + rootCause.getStackTrace()[0].getLineNumber();
meta.put("ex_args", argMsg);
meta.put("ex_msg", rootCause.getMessage());
+ meta.put("ex_type", type);
final int stackLimit = 15;
String[] st = new String[stackLimit];
for(int i = 0; i < stackLimit; i++) {
diff --git a/business-core/src/test/java/com/tiktok/appevents/TTAppEventLoggerTest.java b/business-core/src/test/java/com/tiktok/appevents/TTAppEventLoggerTest.java
index 0fa5ae4..ba8b9bb 100644
--- a/business-core/src/test/java/com/tiktok/appevents/TTAppEventLoggerTest.java
+++ b/business-core/src/test/java/com/tiktok/appevents/TTAppEventLoggerTest.java
@@ -108,9 +108,9 @@ public void globalSwitchOnButNetworkOnButAccessTokenNull() {
appEventLogger.flush(TTAppEventLogger.FlushReason.FORCE_FLUSH);
}
- TTAppEvent fromDisk1 = new TTAppEvent(TTAppEvent.TTAppEventType.track, "InternalTest", "{}");
- TTAppEvent fromDisk2 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}");
- TTAppEvent fromMemory3 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}");
+ TTAppEvent fromDisk1 = new TTAppEvent(TTAppEvent.TTAppEventType.track, "InternalTest", "{}", null, null);
+ TTAppEvent fromDisk2 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null);
+ TTAppEvent fromMemory3 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null);
private TTAppEventLogger flushCommon() {
PowerMockito.mockStatic(TikTokBusinessSdk.class);
@@ -164,7 +164,7 @@ public void flushNormally() {
public void flushFailed() {
TTAppEventLogger appEventLogger = flushCommon();
List failed = new LinkedList<>();
- failed.add(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}"));
+ failed.add(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null));
// when failed to flush, persist to disk
when(TTRequest.reportAppEvent(any(), any())).thenReturn(failed);
diff --git a/business-core/src/test/java/com/tiktok/appevents/TTAppEventsQueueTest.java b/business-core/src/test/java/com/tiktok/appevents/TTAppEventsQueueTest.java
index 16de4f4..e0d542b 100644
--- a/business-core/src/test/java/com/tiktok/appevents/TTAppEventsQueueTest.java
+++ b/business-core/src/test/java/com/tiktok/appevents/TTAppEventsQueueTest.java
@@ -28,8 +28,8 @@ public class TTAppEventsQueueTest extends BaseTest {
@Test
public void simpleCase() {
- TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}"));
- TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}"));
+ TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null));
+ TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null));
assertEquals(2, TTAppEventsQueue.size());
TTAppEventsQueue.clearAll();
@@ -58,15 +58,15 @@ public void thresholdLeft(int threshold, int left) {
}
});
- TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}"));
+ TTAppEventsQueue.addEvent(new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null));
TikTokBusinessSdk.destroy();
TTAppEventsQueue.clearAll();
}
@Test
public void testExport() {
- TTAppEvent e1 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}");
- TTAppEvent e2 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}");
+ TTAppEvent e1 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null);
+ TTAppEvent e2 = new TTAppEvent(TTAppEvent.TTAppEventType.track,"InternalTest", "{}", null, null);
TTAppEventsQueue.addEvent(e1);
TTAppEventsQueue.addEvent(e2);
diff --git a/gradle.properties b/gradle.properties
index 51862c9..1719fdb 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,7 @@ android.enableJetifier=true
GROUP=com.tiktok
-VERSION_NAME=1.2.11
+VERSION_NAME=1.3.1
mavenGroupId = com.tiktok
mavenArtifactId = tiktok-business-android-sdk
diff --git a/jitpack.yml b/jitpack.yml
new file mode 100644
index 0000000..46c8529
--- /dev/null
+++ b/jitpack.yml
@@ -0,0 +1,2 @@
+jdk:
+ - openjdk11
\ No newline at end of file
diff --git a/samples/TestApp/build.gradle b/samples/TestApp/build.gradle
index 8478846..9ccbbaf 100644
--- a/samples/TestApp/build.gradle
+++ b/samples/TestApp/build.gradle
@@ -7,21 +7,14 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 31
+ compileSdkVersion 33
buildToolsVersion "30.0.2"
-
defaultConfig {
- applicationId "com.example"
+ applicationId "com.tiktok.iabtest"
minSdkVersion 17
- targetSdkVersion 31
- versionCode 1
- versionName "0.0.1-alpha"
-
- javaCompileOptions {
- annotationProcessorOptions {
- arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
- }
- }
+ targetSdkVersion 33
+ versionCode 14
+ versionName "1.14"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
@@ -32,11 +25,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
}
dependencies {
@@ -51,7 +39,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-process:2.4.0'
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
- implementation 'com.android.billingclient:billing:3.0.3'
+ implementation 'com.android.billingclient:billing:6.0.0'
implementation 'androidx.room:room-runtime:2.4.3'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
diff --git a/samples/TestApp/src/main/AndroidManifest.xml b/samples/TestApp/src/main/AndroidManifest.xml
index e3720a2..80c9e03 100644
--- a/samples/TestApp/src/main/AndroidManifest.xml
+++ b/samples/TestApp/src/main/AndroidManifest.xml
@@ -11,6 +11,10 @@
+
+
+
+
android.util.Log.i("TikTokBusinessSdk", "setOnCrashListener" + thread.getName(), ex));
-
- // testing delay tracking, implementing a 6 sec delay manually
- // ideally has to be after accepting tracking permission
- new Handler(Looper.getMainLooper()).postDelayed(TikTokBusinessSdk::startTrack, 10000);
- }
}
@Override
@@ -105,7 +78,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
for (String prop : Objects.requireNonNull(TestEvents.TTEventProperties.get(event))) {
props.put(prop, "");
}
- eventLogRepo.save(new EventLog(event, props.toString()));
+ eventLogRepo.save(new EventLog(event, props.toString()), false);
} catch (JSONException e) {
e.printStackTrace();
}
@@ -122,7 +95,7 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
}
while (count < MAX && logs.size() > 0) {
for (EventLog log : Objects.requireNonNull(logs)) {
- eventLogRepo.save(new EventLog(log.eventType, log.properties));
+ eventLogRepo.save(new EventLog(log.eventType, log.properties), false);
count++;
if (count >= MAX) break;
}
diff --git a/samples/TestApp/src/main/java/com/example/persistence/EventLogRepo.java b/samples/TestApp/src/main/java/com/example/persistence/EventLogRepo.java
index de8c0a9..8a07abc 100644
--- a/samples/TestApp/src/main/java/com/example/persistence/EventLogRepo.java
+++ b/samples/TestApp/src/main/java/com/example/persistence/EventLogRepo.java
@@ -6,14 +6,38 @@
package com.example.persistence;
+import static com.example.testdata.TestEvents.TTBaseEvents;
+import static com.example.testdata.TestEvents.TTContentsEvent;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_CARD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_WISHLIST;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_CHECK_OUT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_PURCHASE;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_VIEW_CONTENT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENTS;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_ID;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_TYPE;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CURRENCY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_DESCRIPTION;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_VALUE;
+
import android.app.Application;
import android.os.AsyncTask;
import androidx.lifecycle.LiveData;
import com.example.model.EventLog;
+import com.example.testdata.TestEvents;
import com.tiktok.TikTokBusinessSdk;
+import com.tiktok.appevents.base.TTBaseEvent;
+import com.tiktok.appevents.contents.TTAddToCartEvent;
+import com.tiktok.appevents.contents.TTAddToWishlistEvent;
+import com.tiktok.appevents.contents.TTCheckoutEvent;
+import com.tiktok.appevents.contents.TTContentParams;
+import com.tiktok.appevents.contents.TTContentsEvent;
+import com.tiktok.appevents.contents.TTPurchaseEvent;
+import com.tiktok.appevents.contents.TTViewContentEvent;
+import org.json.JSONArray;
import org.json.JSONObject;
import java.util.Iterator;
@@ -51,16 +75,77 @@ public List getLogs() throws ExecutionException, InterruptedException
return new getAllAsyncTask(eventLogDao).execute().get();
}
- public void save(final EventLog eventLog) {
+ public void save(final EventLog eventLog, boolean oldApi) {
try {
+ if(oldApi){
+ JSONObject props = new JSONObject(eventLog.properties);
+ Iterator iterator = props.keys();
+ JSONObject obj = new JSONObject();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ obj.put(key, props.get(key));
+ }
+ TikTokBusinessSdk.trackEvent(eventLog.eventType, obj);
+ PersistenceManager.databaseWriteExecutor.execute(() -> eventLogDao.save(eventLog));
+ return;
+ }
JSONObject props = new JSONObject(eventLog.properties);
- Iterator iterator = props.keys();
- JSONObject obj = new JSONObject();
- while (iterator.hasNext()) {
- String key = (String) iterator.next();
- obj.put(key, props.get(key));
+ if(TTBaseEvents.get(eventLog.eventType) != null){
+ TikTokBusinessSdk.trackTTEvent(TTBaseEvents.get(eventLog.eventType));
+ }else if(TTContentsEvent.contains(eventLog.eventType)){
+ TTContentsEvent.Builder info = null;
+ switch (eventLog.eventType){
+ case EVENT_NAME_ADD_TO_CARD:
+ info = TTAddToCartEvent.newBuilder();
+ break;
+ case EVENT_NAME_ADD_TO_WISHLIST:
+ info = TTAddToWishlistEvent.newBuilder();
+ break;
+ case EVENT_NAME_CHECK_OUT:
+ info = TTCheckoutEvent.newBuilder();
+ break;
+ case EVENT_NAME_PURCHASE:
+ info = TTPurchaseEvent.newBuilder();
+ break;
+ case EVENT_NAME_VIEW_CONTENT:
+ info = TTViewContentEvent.newBuilder();
+ break;
+ }
+ if(info != null){
+ TTContentParams[] params = null;
+ if(props.has(EVENT_PROPERTY_CONTENTS)){
+ JSONArray jsonArray = props.optJSONArray(EVENT_PROPERTY_CONTENTS);
+ params = new TTContentParams[jsonArray.length()];
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject jsonObject = jsonArray.getJSONObject(i);
+ params[i] = TTContentParams.newBuilder()
+ .setContentId(jsonObject.optString("content_id"))
+ .setContentCategory(jsonObject.optString("content_category"))
+ .setBrand(jsonObject.optString("brand"))
+ .setPrice((float) jsonObject.optDouble("price"))
+ .setQuantity(jsonObject.optInt("quantity"))
+ .setContentName(jsonObject.optString("content_name")).build();
+ }
+ }
+ info.setDescription(props.optString(EVENT_PROPERTY_DESCRIPTION))
+ .setCurrency(TestEvents.TTCurrency.get(props.optString(EVENT_PROPERTY_CURRENCY)))
+ .setValue(props.optDouble(EVENT_PROPERTY_VALUE))
+ .setContentId(props.optString(EVENT_PROPERTY_CONTENT_ID))
+ .setContentType(props.optString(EVENT_PROPERTY_CONTENT_TYPE));
+ if(params != null){
+ info.setContents(params);
+ }
+ TikTokBusinessSdk.trackTTEvent(info.build());
+ }
+ }else {
+ Iterator iterator = props.keys();
+ TTBaseEvent.Builder ttBaseEvent = TTBaseEvent.newBuilder(eventLog.eventType);
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ ttBaseEvent.addProperty(key, props.get(key));
+ }
+ TikTokBusinessSdk.trackTTEvent(ttBaseEvent.build());
}
- TikTokBusinessSdk.trackEvent(eventLog.eventType, obj);
PersistenceManager.databaseWriteExecutor.execute(() -> eventLogDao.save(eventLog));
} catch (Exception ignored) {}
}
diff --git a/samples/TestApp/src/main/java/com/example/testdata/TestEvents.java b/samples/TestApp/src/main/java/com/example/testdata/TestEvents.java
index d793855..2e7a68b 100644
--- a/samples/TestApp/src/main/java/com/example/testdata/TestEvents.java
+++ b/samples/TestApp/src/main/java/com/example/testdata/TestEvents.java
@@ -6,8 +6,108 @@
package com.example.testdata;
+import static com.tiktok.appevents.base.EventName.ACHIEVE_LEVEL;
+import static com.tiktok.appevents.base.EventName.ADD_PAYMENT_INFO;
+import static com.tiktok.appevents.base.EventName.COMPLETE_TUTORIAL;
+import static com.tiktok.appevents.base.EventName.CREATE_GROUP;
+import static com.tiktok.appevents.base.EventName.CREATE_ROLE;
+import static com.tiktok.appevents.base.EventName.GENERATE_LEAD;
+import static com.tiktok.appevents.base.EventName.INSTALL_APP;
+import static com.tiktok.appevents.base.EventName.IN_APP_AD_CLICK;
+import static com.tiktok.appevents.base.EventName.IN_APP_AD_IMPR;
+import static com.tiktok.appevents.base.EventName.JOIN_GROUP;
+import static com.tiktok.appevents.base.EventName.LAUNCH_APP;
+import static com.tiktok.appevents.base.EventName.LOAN_APPLICATION;
+import static com.tiktok.appevents.base.EventName.LOAN_APPROVAL;
+import static com.tiktok.appevents.base.EventName.LOAN_DISBURSAL;
+import static com.tiktok.appevents.base.EventName.LOGIN;
+import static com.tiktok.appevents.base.EventName.RATE;
+import static com.tiktok.appevents.base.EventName.REGISTRATION;
+import static com.tiktok.appevents.base.EventName.SEARCH;
+import static com.tiktok.appevents.base.EventName.SPEND_CREDITS;
+import static com.tiktok.appevents.base.EventName.START_TRIAL;
+import static com.tiktok.appevents.base.EventName.SUBSCRIBE;
+import static com.tiktok.appevents.base.EventName.UNLOCK_ACHIEVEMENT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_CARD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_ADD_TO_WISHLIST;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_CHECK_OUT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_PURCHASE;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.ContentsEventName.EVENT_NAME_VIEW_CONTENT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.AED;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.ARS;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.AUD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.BDT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.BHD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.BIF;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.BOB;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.BRL;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CAD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CHF;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CLP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CNY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.COP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CRC;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.CZK;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.DKK;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.DZD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.EGP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.EUR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.GBP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.GTQ;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.HKD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.HNL;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.HUF;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.IDR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.ILS;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.INR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.ISK;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.JPY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.KES;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.KHR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.KRW;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.KWD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.KZT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.MAD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.MOP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.MXN;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.MYR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.NGN;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.NIO;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.NOK;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.NZD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.OMR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.PEN;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.PHP;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.PKR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.PLN;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.PYG;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.QAR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.RON;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.RUB;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.SAR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.SEK;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.SGD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.THB;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.TRY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.TWD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.UAH;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.USD;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.VES;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.VND;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Currency.ZAR;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_ID;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENT_TYPE;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CURRENCY;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_DESCRIPTION;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_VALUE;
+
+import com.tiktok.appevents.base.EventName;
+import com.tiktok.appevents.contents.TTContentsEventConstants;
+
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
public class TestEvents {
@@ -19,176 +119,151 @@ public static String[] getAllEvents() {
return events.toArray(new String[events.size()]);
}
- public static HashMap TTEventProperties = new HashMap<>();
+ public static HashMap TTEventProperties = new LinkedHashMap<>();
+ public static HashMap TTBaseEvents = new HashMap<>();
+ public static HashSet TTContentsEvent = new HashSet<>();
+ public static String[] TTContentParams;
+
+ public static HashMap TTCurrency = new HashMap<>();
static {
- TTEventProperties.put("LaunchAPP", new String[]{});
- TTEventProperties.put("InstallApp", new String[]{});
- TTEventProperties.put("2Dretention", new String[]{});
- TTEventProperties.put("AddPaymentInfo", new String[]{
- "success",
- });
- TTEventProperties.put("AddToCart", new String[]{
- "content_type",
- "sku_id",
- "description",
- "currency",
- "value",
- });
- TTEventProperties.put("AddToWishlist", new String[]{
- "page_type",
- "content_id",
- "description",
- "currency",
- "value",
- });
- TTEventProperties.put("Checkout", new String[]{
- "description",
- "sku_id",
- "num_items",
- "payment_available",
- "currency",
- "value",
- "game_item_type",
- "game_item_id",
- "room_type",
- "currency",
- "value",
- "location",
- "checkin_date",
- "checkout_date",
- "number_of_rooms",
- "number_of_nights",
- });
- TTEventProperties.put("CompleteTutorial", new String[]{});
- TTEventProperties.put("ViewContent", new String[]{
- "page_type",
- "sku_id",
- "description",
- "currency",
- "value",
- "search_string",
- "room_type",
- "location",
- "checkin_date",
- "checkout_date",
- "number_of_rooms",
- "number_of_nights",
- "outbound_origination_city",
- "outbound_destination_city",
- "return_origination_city",
- "return_destination_city",
- "class",
- "number_of_passenger",
- });
- TTEventProperties.put("CreateGroup", new String[]{
- "group_name",
- "group_logo",
- "group_description",
- "group_type",
- "group_id",
- });
- TTEventProperties.put("CreateRole", new String[]{
- "role_type",
- });
- TTEventProperties.put("GenerateLead", new String[]{});
- TTEventProperties.put("InAppADClick", new String[]{
- "ad_type",
- });
- TTEventProperties.put("InAppADImpr", new String[]{
- "ad_type",
- });
- TTEventProperties.put("JoinGroup", new String[]{
- "level_number",
+ TTEventProperties.put(ACHIEVE_LEVEL.toString(), new String[]{});
+ TTEventProperties.put(ADD_PAYMENT_INFO.toString(), new String[]{});
+ TTEventProperties.put(EVENT_NAME_ADD_TO_CARD, new String[]{
+ EVENT_PROPERTY_CONTENT_TYPE,EVENT_PROPERTY_CONTENT_ID,EVENT_PROPERTY_DESCRIPTION,
+ EVENT_PROPERTY_CURRENCY,EVENT_PROPERTY_VALUE
});
- TTEventProperties.put("AchieveLevel", new String[]{
- "level_number",
- "score",
+ TTEventProperties.put(EVENT_NAME_ADD_TO_WISHLIST, new String[]{
+ EVENT_PROPERTY_CONTENT_TYPE,EVENT_PROPERTY_CONTENT_ID,EVENT_PROPERTY_DESCRIPTION,
+ EVENT_PROPERTY_CURRENCY,EVENT_PROPERTY_VALUE
});
- TTEventProperties.put("LoanApplication", new String[]{
- "loan_type",
- "application_id",
+ TTEventProperties.put(EVENT_NAME_CHECK_OUT, new String[]{
+ EVENT_PROPERTY_CONTENT_TYPE,EVENT_PROPERTY_CONTENT_ID,EVENT_PROPERTY_DESCRIPTION,
+ EVENT_PROPERTY_CURRENCY,EVENT_PROPERTY_VALUE
});
- TTEventProperties.put("LoanApproval", new String[]{
- "value",
+ TTEventProperties.put(COMPLETE_TUTORIAL.toString(), new String[]{});
+ TTEventProperties.put(CREATE_GROUP.toString(), new String[]{});
+ TTEventProperties.put(CREATE_ROLE.toString(), new String[]{});
+ TTEventProperties.put(GENERATE_LEAD.toString(), new String[]{});
+ TTEventProperties.put(IN_APP_AD_CLICK.toString(), new String[]{});
+ TTEventProperties.put(IN_APP_AD_IMPR.toString(), new String[]{});
+ TTEventProperties.put(INSTALL_APP.toString(), new String[]{});
+ TTEventProperties.put(JOIN_GROUP.toString(), new String[]{});
+ TTEventProperties.put(LAUNCH_APP.toString(), new String[]{});
+ TTEventProperties.put(LOAN_APPLICATION.toString(), new String[]{});
+ TTEventProperties.put(LOAN_APPROVAL.toString(), new String[]{});
+ TTEventProperties.put(LOAN_DISBURSAL.toString(), new String[]{});
+ TTEventProperties.put(LOGIN.toString(), new String[]{});
+ TTEventProperties.put(EVENT_NAME_PURCHASE, new String[]{
+ EVENT_PROPERTY_CONTENT_TYPE,EVENT_PROPERTY_CONTENT_ID,EVENT_PROPERTY_DESCRIPTION,
+ EVENT_PROPERTY_CURRENCY,EVENT_PROPERTY_VALUE
});
- TTEventProperties.put("LoanDisbursal", new String[]{
- "value",
- });
- // TODO remove LOGIN event? If not, determine the ordering.
- TTEventProperties.put("Login", new String[]{});
- TTEventProperties.put("Purchase", new String[]{
- "page_type",
- "sku_id",
- "description",
- "num_items",
- "coupon_used",
- "currency",
- "value",
- "group_type",
- "game_item_id",
- "room_type",
- "location",
- "checkin_date",
- "checkout_date",
- "number_of_rooms",
- "number_of_nights",
- "outbound_origination_city",
- "outbound_destination_city",
- "return_origination_city",
- "return_destination_city",
- "class",
- "number_of_passenger",
- "service_type",
- "service_id",
- });
- TTEventProperties.put("Rate", new String[]{
- "page_type",
- "sku_id",
- "content",
- "rating_value",
- "max_rating_value",
- "rate",
- });
- TTEventProperties.put("Registration", new String[]{
- "registration_method",
- });
- TTEventProperties.put("Search", new String[]{
- "search_string",
- "checkin_date",
- "checkout_date",
- "number_of_rooms",
- "number_of_nights",
- "origination_city",
- "destination_city",
- "departure_date",
- "return_date",
- "class",
- "number_of_passenger",
- });
- TTEventProperties.put("SpendCredits", new String[]{
- "game_item_type",
- "game_item_id",
- "level_number",
- });
- TTEventProperties.put("StartTrial", new String[]{
- "order_id",
- "currency",
- });
- TTEventProperties.put("Subscribe", new String[]{
- "order_id",
- "currency",
- });
- TTEventProperties.put("Share", new String[]{
- "content_type",
- "content_id",
- "shared_destination",
- });
- TTEventProperties.put("Contact", new String[]{});
- TTEventProperties.put("UnlockAchievement", new String[]{
- "description",
- "achievement_type",
+ TTEventProperties.put(RATE.toString(), new String[]{});
+ TTEventProperties.put(REGISTRATION.toString(), new String[]{});
+ TTEventProperties.put(SEARCH.toString(), new String[]{});
+ TTEventProperties.put(SPEND_CREDITS.toString(), new String[]{});
+ TTEventProperties.put(START_TRIAL.toString(), new String[]{});
+ TTEventProperties.put(SUBSCRIBE.toString(), new String[]{});
+ TTEventProperties.put(UNLOCK_ACHIEVEMENT.toString(), new String[]{});
+ TTEventProperties.put(EVENT_NAME_VIEW_CONTENT, new String[]{
+ EVENT_PROPERTY_CONTENT_TYPE,EVENT_PROPERTY_CONTENT_ID,EVENT_PROPERTY_DESCRIPTION,
+ EVENT_PROPERTY_CURRENCY,EVENT_PROPERTY_VALUE
});
+ TTEventProperties.put("Test", new String[]{});
+
+ TTBaseEvents.put(ACHIEVE_LEVEL.toString(), ACHIEVE_LEVEL);
+ TTBaseEvents.put(ADD_PAYMENT_INFO.toString(), ADD_PAYMENT_INFO);
+ TTBaseEvents.put(COMPLETE_TUTORIAL.toString(), COMPLETE_TUTORIAL);
+ TTBaseEvents.put(CREATE_GROUP.toString(), CREATE_GROUP);
+ TTBaseEvents.put(CREATE_ROLE.toString(), CREATE_ROLE);
+ TTBaseEvents.put(GENERATE_LEAD.toString(), GENERATE_LEAD);
+ TTBaseEvents.put(IN_APP_AD_CLICK.toString(), IN_APP_AD_CLICK);
+ TTBaseEvents.put(IN_APP_AD_IMPR.toString(), IN_APP_AD_IMPR);
+ TTBaseEvents.put(INSTALL_APP.toString(), INSTALL_APP);
+ TTBaseEvents.put(JOIN_GROUP.toString(), JOIN_GROUP);
+ TTBaseEvents.put(LAUNCH_APP.toString(), LAUNCH_APP);
+ TTBaseEvents.put(LOAN_APPLICATION.toString(), LOAN_APPLICATION);
+ TTBaseEvents.put(LOAN_APPROVAL.toString(), LOAN_APPROVAL);
+ TTBaseEvents.put(LOAN_DISBURSAL.toString(), LOAN_DISBURSAL);
+ TTBaseEvents.put(LOGIN.toString(), LOGIN);
+ TTBaseEvents.put(RATE.toString(), RATE);
+ TTBaseEvents.put(REGISTRATION.toString(), REGISTRATION);
+ TTBaseEvents.put(SEARCH.toString(), SEARCH);
+ TTBaseEvents.put(SPEND_CREDITS.toString(), SPEND_CREDITS);
+ TTBaseEvents.put(START_TRIAL.toString(), START_TRIAL);
+ TTBaseEvents.put(SUBSCRIBE.toString(), SUBSCRIBE);
+ TTBaseEvents.put(UNLOCK_ACHIEVEMENT.toString(), UNLOCK_ACHIEVEMENT);
+
+ TTContentsEvent.add(EVENT_NAME_ADD_TO_CARD);
+ TTContentsEvent.add(EVENT_NAME_ADD_TO_WISHLIST);
+ TTContentsEvent.add(EVENT_NAME_CHECK_OUT);
+ TTContentsEvent.add(EVENT_NAME_PURCHASE);
+ TTContentsEvent.add(EVENT_NAME_VIEW_CONTENT);
+ TTContentParams = new String[]{"price", "quantity", "content_id", "content_category",
+ "content_name", "brand"};
+
+ TTCurrency.put(AED.toString(), AED);
+ TTCurrency.put(ARS.toString(),ARS);
+ TTCurrency.put(AUD.toString(),AUD);
+ TTCurrency.put(BDT.toString(),BDT);
+ TTCurrency.put(BHD.toString(),BHD);
+ TTCurrency.put(BIF.toString(),BIF);
+ TTCurrency.put(BOB.toString(),BOB);
+ TTCurrency.put(BRL.toString(),BRL);
+ TTCurrency.put(CAD.toString(),CAD);
+ TTCurrency.put(CHF.toString(),CHF);
+ TTCurrency.put(CLP.toString(),CLP);
+ TTCurrency.put(CNY.toString(),CNY);
+ TTCurrency.put(EGP.toString(),EGP);
+ TTCurrency.put(IDR.toString(),IDR);
+ TTCurrency.put(ILS.toString(),ILS);
+ TTCurrency.put(OMR.toString(),OMR);
+ TTCurrency.put(VND.toString(),VND);
+ TTCurrency.put(INR.toString(),INR);
+ TTCurrency.put(PEN.toString(),PEN);
+ TTCurrency.put(ZAR.toString(),ZAR);
+ TTCurrency.put(ISK.toString(),ISK);
+ TTCurrency.put(JPY.toString(),JPY);
+ TTCurrency.put(KES.toString(),KES);
+ TTCurrency.put(KHR.toString(),KHR);
+ TTCurrency.put(KRW.toString(),KRW);
+ TTCurrency.put(KWD.toString(),KWD);
+ TTCurrency.put(KZT.toString(),KZT);
+ TTCurrency.put(MAD.toString(),MAD);
+ TTCurrency.put(MOP.toString(),MOP);
+ TTCurrency.put(EUR.toString(),EUR);
+ TTCurrency.put(PHP.toString(),PHP);
+ TTCurrency.put(PKR.toString(),PKR);
+ TTCurrency.put(PLN.toString(),PLN);
+ TTCurrency.put(QAR.toString(),QAR);
+ TTCurrency.put(PYG.toString(),PYG);
+ TTCurrency.put(RON.toString(),RON);
+ TTCurrency.put(MXN.toString(),MXN);
+ TTCurrency.put(RUB.toString(),RUB);
+ TTCurrency.put(SAR.toString(),SAR);
+ TTCurrency.put(SEK.toString(),SEK);
+ TTCurrency.put(SGD.toString(),SGD);
+ TTCurrency.put(THB.toString(),THB);
+ TTCurrency.put(COP.toString(),COP);
+ TTCurrency.put(GBP.toString(),GBP);
+ TTCurrency.put(MYR.toString(),MYR);
+ TTCurrency.put(TRY.toString(),TRY);
+ TTCurrency.put(CRC.toString(),CRC);
+ TTCurrency.put(GTQ.toString(),GTQ);
+ TTCurrency.put(NGN.toString(),NGN);
+ TTCurrency.put(TWD.toString(),TWD);
+ TTCurrency.put(CZK.toString(),CZK);
+ TTCurrency.put(HKD.toString(),HKD);
+ TTCurrency.put(NIO.toString(),NIO);
+ TTCurrency.put(UAH.toString(),UAH);
+ TTCurrency.put(DKK.toString(),DKK);
+ TTCurrency.put(HNL.toString(),HNL);
+ TTCurrency.put(NOK.toString(),NOK);
+ TTCurrency.put(USD.toString(),USD);
+ TTCurrency.put(DZD.toString(),DZD);
+ TTCurrency.put(HUF.toString(),HUF);
+ TTCurrency.put(NZD.toString(),NZD);
+ TTCurrency.put(VES.toString(),VES);
}
}
diff --git a/samples/TestApp/src/main/java/com/example/ui/eventlog/EventLogViewModel.java b/samples/TestApp/src/main/java/com/example/ui/eventlog/EventLogViewModel.java
index 8b37e92..731669f 100644
--- a/samples/TestApp/src/main/java/com/example/ui/eventlog/EventLogViewModel.java
+++ b/samples/TestApp/src/main/java/com/example/ui/eventlog/EventLogViewModel.java
@@ -27,7 +27,7 @@ public EventLogViewModel(Application application) {
public LiveData> getAllEventLogs() { return eventLogRepo.getAllEventLogs(); }
- public void save(EventLog eventLog) {
- eventLogRepo.save(eventLog);
+ public void save(EventLog eventLog, boolean oldApi) {
+ eventLogRepo.save(eventLog, oldApi);
}
}
\ No newline at end of file
diff --git a/samples/TestApp/src/main/java/com/example/ui/events/EventFragment.java b/samples/TestApp/src/main/java/com/example/ui/events/EventFragment.java
index dcb15cb..a106f41 100644
--- a/samples/TestApp/src/main/java/com/example/ui/events/EventFragment.java
+++ b/samples/TestApp/src/main/java/com/example/ui/events/EventFragment.java
@@ -6,13 +6,20 @@
package com.example.ui.events;
+import static com.example.testdata.TestEvents.TTBaseEvents;
+import static com.example.ui.events.PropEditActivity.SOURCE;
+import static com.example.ui.events.PropEditActivity.SOURCE_CONTENT;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENTS;
+
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
@@ -35,6 +42,8 @@
import java.util.Iterator;
import java.util.Objects;
+import java.util.Random;
+import java.util.Set;
public class EventFragment extends Fragment {
@@ -70,6 +79,36 @@ public View onCreateView(@NonNull LayoutInflater inflater,
View root = inflater.inflate(R.layout.fragment_events, container, false);
final TextView propsTV = root.findViewById(R.id.propsPrettyViewer);
+ final Button propertyBtn = root.findViewById(R.id.addContents);
+ final Button crash = root.findViewById(R.id.crash);
+ final Button trackEvent = root.findViewById(R.id.track_event);
+ final EditText editText = root.findViewById(R.id.number_of_events);
+ final Button sendRandomEvents = root.findViewById(R.id.send_random_event);
+ propertyBtn.setVisibility(View.GONE);
+ sendRandomEvents.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int number = 100;
+ try{
+ number = Integer.parseInt(String.valueOf(editText.getText()));
+ }catch (Throwable throwable){
+ throwable.printStackTrace();
+ }
+ Setset = TTBaseEvents.keySet();
+ String[]key = set.toArray(new String[0]);
+ for(int i =0;i {
try {
assert s != null;
@@ -91,6 +130,9 @@ public View onCreateView(@NonNull LayoutInflater inflater,
Iterator keys = eventViewModel.getPropIterator();
while (keys.hasNext()) {
String key = keys.next();
+ if(EVENT_PROPERTY_CONTENTS.equals(key)){
+ continue;
+ }
try {
bundlePros.putString(key, eventViewModel.getProp(key));
} catch (JSONException e) {
@@ -100,6 +142,11 @@ public View onCreateView(@NonNull LayoutInflater inflater,
intent.putExtras(bundlePros);
startActivityForResult(intent, 2);
});
+ propertyBtn.setOnClickListener(view -> {
+ Intent intent = new Intent(requireContext(), PropEditActivity.class);
+ intent.putExtra(SOURCE, SOURCE_CONTENT);
+ startActivityForResult(intent, 2);
+ });
ImageButton savedEventsBtn = root.findViewById(R.id.savedEventsBtn);
savedEventsBtn.setOnClickListener(v -> {
@@ -109,11 +156,23 @@ public View onCreateView(@NonNull LayoutInflater inflater,
builder.setItems(events, (dialog, selected) -> {
eventViewModel.resetProps();
eventViewModel.setEvent(events[selected]);
- for (String property : Objects.requireNonNull(TestEvents.TTEventProperties.get(events[selected]))) {
- try {
- eventViewModel.addProp(property, "");
- } catch (JSONException e) {
- e.printStackTrace();
+ if(TTBaseEvents.containsKey(events[selected])){
+ propsTV.setVisibility(View.GONE);
+ propertyBtn.setVisibility(View.GONE);
+ } else {
+ for (String property : Objects.requireNonNull(TestEvents.TTEventProperties.get(events[selected]))) {
+ try {
+ eventViewModel.addProp(property, "");
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ if(TestEvents.TTContentsEvent.contains(events[selected])){
+ propsTV.setVisibility(View.VISIBLE);
+ propertyBtn.setVisibility(View.VISIBLE);
+ } else {
+ propsTV.setVisibility(View.VISIBLE);
+ propertyBtn.setVisibility(View.GONE);
}
}
});
@@ -128,11 +187,22 @@ public View onCreateView(@NonNull LayoutInflater inflater,
eventLogViewModel.save(new EventLog(
eventName,
Objects.requireNonNull(eventViewModel.getLiveProperties().getValue()).toString()
- ));
+ ), false);
Toast.makeText(requireContext(), eventName + " event tracked, plz check log", Toast.LENGTH_SHORT).show();
}
});
+ trackEvent.setOnClickListener(view -> {
+ String eventName = eventTV.getText().toString();
+ eventViewModel.setEvent(eventName);
+ if (!eventName.equals("")) {
+ eventLogViewModel.save(new EventLog(
+ eventName,
+ Objects.requireNonNull(eventViewModel.getLiveProperties().getValue()).toString()
+ ), true);
+ Toast.makeText(requireContext(), eventName + " event tracked, plz check log", Toast.LENGTH_SHORT).show();
+ }
+ });
Button flushBtn = root.findViewById(R.id.flush);
flushBtn.setOnClickListener(v -> {
TikTokBusinessSdk.flush();
@@ -144,20 +214,44 @@ public View onCreateView(@NonNull LayoutInflater inflater,
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
+ if(data == null || data.getExtras() == null){
+ return;
+ }
+ Bundle bundle = data.getExtras();
if (resultCode == 2) {
- assert data != null;
- Bundle bundle = data.getExtras();
- eventViewModel.resetProps();
- if (bundle != null) {
- for (String key : bundle.keySet()) {
- if (bundle.get(key) != null) {
- try {
- eventViewModel.addProp(key, bundle.get(key));
- } catch (JSONException e) {
- e.printStackTrace();
+ try {
+ if (!TextUtils.isEmpty(bundle.getString("event_prop"))) {
+ eventViewModel.setProps(new JSONObject(bundle.getString("event_prop")));
+ } else {
+ eventViewModel.resetProps();
+ for (String key : bundle.keySet()) {
+ if (bundle.get(key) != null) {
+ try {
+ eventViewModel.addProp(key, bundle.get(key));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
}
}
}
+ }catch (Throwable throwable){
+ throwable.printStackTrace();
+ }
+ } else if (resultCode == 3) {
+ JSONObject jsonObject = new JSONObject();
+ for (String key : bundle.keySet()) {
+ if (bundle.get(key) != null) {
+ try {
+ jsonObject.put(key, bundle.get(key));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ try {
+ eventViewModel.addContents(jsonObject);
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
}
}
}
diff --git a/samples/TestApp/src/main/java/com/example/ui/events/EventViewModel.java b/samples/TestApp/src/main/java/com/example/ui/events/EventViewModel.java
index b1d314f..78437f0 100644
--- a/samples/TestApp/src/main/java/com/example/ui/events/EventViewModel.java
+++ b/samples/TestApp/src/main/java/com/example/ui/events/EventViewModel.java
@@ -6,6 +6,8 @@
package com.example.ui.events;
+import static com.tiktok.appevents.contents.TTContentsEventConstants.Params.EVENT_PROPERTY_CONTENTS;
+
import android.app.Application;
import android.os.Build;
@@ -14,6 +16,7 @@
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -44,6 +47,10 @@ void resetProps() {
properties.setValue(new JSONObject());
}
+ void setProps(JSONObject js) {
+ properties.setValue(js);
+ }
+
void addProp(String key, Object value) throws JSONException {
JSONObject newProp = properties.getValue();
assert newProp != null;
@@ -51,9 +58,21 @@ void addProp(String key, Object value) throws JSONException {
properties.setValue(newProp);
}
+ void addContents(Object value) throws JSONException {
+ JSONObject newProp = properties.getValue();
+ if(newProp.optJSONArray(EVENT_PROPERTY_CONTENTS) != null){
+ newProp.getJSONArray(EVENT_PROPERTY_CONTENTS).put(value);
+ } else {
+ JSONArray jsonArray = new JSONArray();
+ jsonArray.put(value);
+ newProp.put(EVENT_PROPERTY_CONTENTS, jsonArray);
+ }
+ properties.setValue(newProp);
+ }
+
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
String getProp(String key) throws JSONException {
- return (String) Objects.requireNonNull(properties.getValue()).get(key);
+ return String.valueOf(Objects.requireNonNull(properties.getValue()).get(key));
}
LiveData getLiveProperties() {
diff --git a/samples/TestApp/src/main/java/com/example/ui/events/PropEditActivity.java b/samples/TestApp/src/main/java/com/example/ui/events/PropEditActivity.java
index 7d2eaa1..9cb7778 100644
--- a/samples/TestApp/src/main/java/com/example/ui/events/PropEditActivity.java
+++ b/samples/TestApp/src/main/java/com/example/ui/events/PropEditActivity.java
@@ -6,6 +6,8 @@
package com.example.ui.events;
+import static com.example.testdata.TestEvents.TTContentParams;
+
import androidx.annotation.NonNull;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
@@ -15,6 +17,7 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
+import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -35,9 +38,13 @@
public class PropEditActivity extends AppCompatActivity {
private ListView myList;
+ private EditText eventProp;
+ public static final String SOURCE_CONTENT = "content";
+ public static final String SOURCE = "source";
private ArrayList props;
private PropListAdapter adapter;
private Integer nextPropID = 0;
+ private int resultCode = 2;
@SuppressLint("CutPasteId")
@Override
@@ -52,14 +59,24 @@ protected void onCreate(Bundle savedInstanceState) {
myList = findViewById(R.id.list);
ImageButton fabImageButton = findViewById(R.id.fab_image_button);
+ eventProp = findViewById(R.id.event_prop);
props = new ArrayList<>();
- Bundle bundle = getIntent().getExtras();
- if (bundle != null) {
- for (String key : bundle.keySet()) {
- if (bundle.get(key) != null) {
- nextPropID++;
- props.add(new Property(nextPropID.toString(), key, (String) bundle.get(key)));
+ if(SOURCE_CONTENT.equals(getIntent().getStringExtra(SOURCE))){
+ for (String key : TTContentParams) {
+ nextPropID++;
+ props.add(new Property(nextPropID.toString(), key, ""));
+ }
+ resultCode = 3;
+ fabImageButton.setVisibility(View.GONE);
+ }else {
+ Bundle bundle = getIntent().getExtras();
+ if (bundle != null) {
+ for (String key : bundle.keySet()) {
+ if (bundle.get(key) != null) {
+ nextPropID++;
+ props.add(new Property(nextPropID.toString(), key, (String) bundle.get(key)));
+ }
}
}
}
@@ -131,15 +148,37 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
Intent intent= new Intent();
Bundle bundlePros = new Bundle();
for (Property p : props) {
- bundlePros.putString(p.key, p.value);
+ if(!TextUtils.isEmpty(p.value)){
+ convertValue(bundlePros, p.key, p.value);
+ }
+ }
+ if (!TextUtils.isEmpty(eventProp.getText())) {
+ bundlePros.putString("event_prop", String.valueOf(eventProp.getText()));
+ resultCode = 2;
}
intent.putExtras(bundlePros);
- setResult(2, intent);
+ setResult(resultCode, intent);
finish();
}
return super.onOptionsItemSelected(item);
}
+ private void convertValue(Bundle bundlePros, String key, String value) {
+ switch (key){
+ case "value":
+ bundlePros.putFloat(key, Float.parseFloat(value));
+ break;
+ case "price":
+ bundlePros.putFloat(key, Float.parseFloat(value));
+ break;
+ case "quantity":
+ bundlePros.putInt(key, Integer.parseInt(value));
+ break;
+ default:
+ bundlePros.putString(key, value);
+ }
+ }
+
public void onDeleteButtonClick(View view) {
View v = (View) view.getParent();
TextView itemID = (TextView) v.findViewById(R.id.item_id);
diff --git a/samples/TestApp/src/main/java/com/example/ui/home/HomeFragment.java b/samples/TestApp/src/main/java/com/example/ui/home/HomeFragment.java
index af8feaa..11550e2 100644
--- a/samples/TestApp/src/main/java/com/example/ui/home/HomeFragment.java
+++ b/samples/TestApp/src/main/java/com/example/ui/home/HomeFragment.java
@@ -14,7 +14,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
@@ -22,10 +21,6 @@
import com.android.billingclient.api.*;
import com.example.R;
-import com.tiktok.TikTokBusinessSdk;
-import com.tiktok.appevents.TTPurchaseInfo;
-
-import org.json.JSONObject;
import java.util.*;
@@ -37,17 +32,23 @@ public class HomeFragment extends Fragment {
private BillingClient billingClient;
private SkuDetails skuDetails = null;
private Purchase purchase = null;
+ private SkuDetails subsSkuDetails = null;
+ private Purchase subscribe = null;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
View root = inflater.inflate(R.layout.fragment_home, container, false);
final TextView textView = root.findViewById(R.id.text_home);
+ final TextView subsTextView = root.findViewById(R.id.text_subs);
+ final TextView textLog = root.findViewById(R.id.text_log);
homeViewModel.getText().observe(getViewLifecycleOwner(), s -> textView.setText(s));
+ homeViewModel.getSubscribeText().observe(getViewLifecycleOwner(), s -> subsTextView.setText(s));
+ homeViewModel.getLogText().observe(getViewLifecycleOwner(), s -> textLog.setText(s));
textView.setOnClickListener(v -> {
if (purchase != null) {
- consumePurchase();
+ consumePurchase(true);
} else if (skuDetails != null) {
Activity activity = requireActivity();
BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
@@ -55,9 +56,25 @@ public View onCreateView(@NonNull LayoutInflater inflater,
.build();
int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();
- homeViewModel.setText("BillingResponseCode: " + responseCode);
+ homeViewModel.setLogText("BillingResponseCode: " + responseCode);
} else {
- queryPurchase();
+ queryPurchase(true);
+ }
+ });
+
+ subsTextView.setOnClickListener(v -> {
+ if (subscribe != null) {
+ consumePurchase(false);
+ } else if (subsSkuDetails != null) {
+ Activity activity = requireActivity();
+ BillingFlowParams billingFlowParams = BillingFlowParams.newBuilder()
+ .setSkuDetails(subsSkuDetails)
+ .build();
+
+ int responseCode = billingClient.launchBillingFlow(activity, billingFlowParams).getResponseCode();
+ homeViewModel.setLogText("BillingResponseCode: " + responseCode);
+ } else {
+ queryPurchase(false);
}
});
@@ -67,23 +84,23 @@ public View onCreateView(@NonNull LayoutInflater inflater,
&& purchases != null) {
/** tiktok.monitor track purchase */
- List purchaseInfos = new ArrayList<>();
-
- try {
- for (Purchase purchase : purchases) {
- purchaseInfos.add(new TTPurchaseInfo(new JSONObject(purchase.getOriginalJson()), new JSONObject(skuDetails.getOriginalJson())));
- }
- TikTokBusinessSdk.trackGooglePlayPurchase(purchaseInfos);
- } catch (Exception e) {
- Toast.makeText(HomeFragment.this.getActivity(), "Failed to track purchase: " + e.getMessage(), Toast.LENGTH_SHORT).show();
- }
-
- purchase = purchases.get(0);
- homeViewModel.setText("purchase success, sku: " + purchase.getSku() + ". click to consume");
+// List purchaseInfos = new ArrayList<>();
+//
+// try {
+// for (Purchase purchase : purchases) {
+// purchaseInfos.add(new TTPurchaseInfo(new JSONObject(purchase.getOriginalJson()), new JSONObject(skuDetails.getOriginalJson())));
+// }
+// TikTokBusinessSdk.trackGooglePlayPurchase(purchaseInfos);
+// } catch (Exception e) {
+// Toast.makeText(HomeFragment.this.getActivity(), "Failed to track purchase: " + e.getMessage(), Toast.LENGTH_SHORT).show();
+// }
+//
+// purchase = purchases.get(0);
+// homeViewModel.setText("purchase success, sku: " + purchase.getSkus().get(0) + ". click to consume");
} else if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.USER_CANCELED) {
- homeViewModel.setText("USER_CANCELED");
+ homeViewModel.setLogText("USER_CANCELED");
} else {
- homeViewModel.setText("otherErr : " + billingResult.getResponseCode());
+ homeViewModel.setLogText("otherErr : " + billingResult.getResponseCode());
}
};
@@ -97,50 +114,77 @@ public View onCreateView(@NonNull LayoutInflater inflater,
return root;
}
- private void newPurchase() {
+ private void newPurchase(boolean isInApp) {
List skuList = new ArrayList<>();
- skuList.add("android.test.purchased");
+ if(isInApp){
+ skuList.add("test_iap_item_1");
+ }else {
+ skuList.add("test_sub_item_2");
+ }
SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
- params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
+ params.setSkusList(skuList).setType(isInApp?BillingClient.SkuType.INAPP:BillingClient.SkuType.SUBS);
billingClient.querySkuDetailsAsync(params.build(), (billingResult1, skuDetailsList) -> {
if (billingResult1.getResponseCode() == BillingClient.BillingResponseCode.OK
&& skuDetailsList != null) {
if (skuDetailsList.size() > 0) {
- skuDetails = skuDetailsList.get(0);
- homeViewModel.setText("launchBillingFlow: " + skuDetails.getSku());
+ if(isInApp){
+ skuDetails = skuDetailsList.get(0);
+ }else {
+ subsSkuDetails = skuDetailsList.get(0);
+ }
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ homeViewModel.setLogText("launchBillingFlow: " + skuDetailsList.get(0).getSku());
+ }
+ });
}
}
});
}
- private void consumePurchase() {
+ private void consumePurchase(boolean isInApp) {
ConsumeParams consumeParams = ConsumeParams.newBuilder()
- .setPurchaseToken(purchase.getPurchaseToken())
+ .setPurchaseToken(isInApp ? purchase.getPurchaseToken() : subscribe.getPurchaseToken())
.build();
ConsumeResponseListener listener = (billingResult, purchaseToken) -> {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
- purchase = null;
- skuDetails = null;
- homeViewModel.setText("Consume success. click to start sku fetch");
+ if(isInApp){
+ purchase = null;
+ skuDetails = null;
+ }else {
+ subscribe = null;
+ subsSkuDetails = null;
+ }
+ homeViewModel.setLogText("Consume success. click to start sku fetch");
}
};
billingClient.consumeAsync(consumeParams, listener);
}
- private void queryPurchase() {
- Purchase.PurchasesResult purchasesResult = billingClient.queryPurchases(BillingClient.SkuType.INAPP);
- if (purchasesResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
- if (Objects.requireNonNull(purchasesResult.getPurchasesList()).size() > 0) {
- // purchase exist
- purchase = purchasesResult.getPurchasesList().get(0);
- homeViewModel.setText("purchase exist, sku: " + purchase.getSku() + ". click to consume");
- } else {
- // new purchase
- newPurchase();
+ private void queryPurchase(boolean isInApp) {
+ billingClient.queryPurchasesAsync(isInApp?BillingClient.SkuType.INAPP:BillingClient.SkuType.SUBS, new PurchasesResponseListener() {
+ @Override
+ public void onQueryPurchasesResponse(@NonNull BillingResult billingResult, @NonNull List list) {
+ if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
+ if (Objects.requireNonNull(list.size() > 0)) {
+ // purchase exist
+ if(isInApp){
+ purchase = list.get(0);
+ } else {
+ subscribe = list.get(0);
+ }
+ homeViewModel.setLogText("purchase exist, sku: " + purchase.getSkus().get(0) + ". click to consume");
+ } else {
+ // new purchase
+ newPurchase(isInApp);
+ }
+ }
}
- }
+ });
+
}
private void startBilling() {
@@ -148,17 +192,17 @@ private void startBilling() {
@Override
public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
- homeViewModel.setText("onBillingSetupFinished");
+ homeViewModel.setLogText("onBillingSetupFinished");
// query existing purchases
- queryPurchase();
+ queryPurchase(true);
} else {
- homeViewModel.setText("Failed to set up billing");
+ homeViewModel.setLogText("Failed to set up billing");
}
}
@Override
public void onBillingServiceDisconnected() {
- homeViewModel.setText("onBillingServiceDisconnected");
+ homeViewModel.setLogText("onBillingServiceDisconnected");
new Handler(Looper.getMainLooper()).postDelayed(() -> startBilling(), 5000);
}
});
diff --git a/samples/TestApp/src/main/java/com/example/ui/home/HomeViewModel.java b/samples/TestApp/src/main/java/com/example/ui/home/HomeViewModel.java
index 8a06911..94e5a52 100644
--- a/samples/TestApp/src/main/java/com/example/ui/home/HomeViewModel.java
+++ b/samples/TestApp/src/main/java/com/example/ui/home/HomeViewModel.java
@@ -19,12 +19,18 @@
public class HomeViewModel extends AndroidViewModel {
private final MutableLiveData mText;
+ private final MutableLiveData mSubsText;
+ private final MutableLiveData mLogText;
SharedPreferences sharedPreferences;
public HomeViewModel(Application application) {
super(application);
mText = new MutableLiveData<>();
mText.setValue("Purchase");
+ mSubsText = new MutableLiveData<>();
+ mSubsText.setValue("Subscribe");
+ mLogText = new MutableLiveData<>();
+ mLogText.setValue("init");
sharedPreferences = getApplication().getSharedPreferences("TT_IDENTIFY", Context.MODE_PRIVATE);
}
@@ -43,6 +49,22 @@ public LiveData getText() {
return mText;
}
+ public LiveData getSubscribeText() {
+ return mSubsText;
+ }
+
+ public void setSubscribeText(String txt) {
+ mSubsText.setValue(txt);
+ }
+
+ public LiveData getLogText() {
+ return mLogText;
+ }
+
+ public void setLogText(String txt) {
+ mLogText.setValue(txt);
+ }
+
public void setNewCache(String externalId,
@Nullable String externalUserName,
@Nullable String phoneNumber,
diff --git a/samples/TestApp/src/main/java/com/example/ui/init/InitFragment.java b/samples/TestApp/src/main/java/com/example/ui/init/InitFragment.java
new file mode 100644
index 0000000..239ed54
--- /dev/null
+++ b/samples/TestApp/src/main/java/com/example/ui/init/InitFragment.java
@@ -0,0 +1,187 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.example.ui.init;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Switch;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.example.R;
+import com.example.ui.home.HomeViewModel;
+import com.tiktok.TikTokBusinessSdk;
+
+public class InitFragment extends Fragment {
+
+ private InitViewModel initViewModel;
+ private EditText appId;
+ private EditText ttAppId;
+ private Button init;
+ private Button startTrack;
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ ViewGroup container, Bundle savedInstanceState) {
+
+ initViewModel = new ViewModelProvider(this).get(InitViewModel.class);
+ View root = inflater.inflate(R.layout.fragment_init, container, false);
+ appId = root.findViewById(R.id.app_id);
+ ttAppId = root.findViewById(R.id.tt_app_id);
+ init = root.findViewById(R.id.init);
+ startTrack = root.findViewById(R.id.startTrack);
+ ((Switch)(root.findViewById(R.id.autostart_status))).setChecked(InitViewModel.autoStart);
+ ((Switch)(root.findViewById(R.id.auto_events_status))).setChecked(InitViewModel.autoEvent);
+ ((Switch)(root.findViewById(R.id.install_logging_status))).setChecked(InitViewModel.loggingStatus);
+ ((Switch)(root.findViewById(R.id.launch_logging_status))).setChecked(InitViewModel.launchStatus);
+ ((Switch)(root.findViewById(R.id.retention_logging_status))).setChecked(InitViewModel.retentionStatus);
+ ((Switch)(root.findViewById(R.id.id_collection_status))).setChecked(InitViewModel.advertiserIDCollectionEnable);
+ ((Switch)(root.findViewById(R.id.monitor_status))).setChecked(InitViewModel.Metrics);
+ ((Switch)(root.findViewById(R.id.debug_status))).setChecked(InitViewModel.debugModeSwitch);
+ ((Switch)(root.findViewById(R.id.limited_status))).setChecked(InitViewModel.lduModeSwitch);
+ ((Switch)(root.findViewById(R.id.iap_status))).setChecked(InitViewModel.autoIapTrack);
+ init.setOnClickListener(v -> {
+ HomeViewModel homeViewModel = new ViewModelProvider(this).get(HomeViewModel.class);
+
+ if (savedInstanceState == null) {
+ // !!!!!!!!!!!!!!!!!!!!!!!!!
+ // in order for this app to be runnable, plz create a resource file containing the relevant string resources
+ // Tiktok sdk init start
+ TikTokBusinessSdk.LogLevel logLevel = TikTokBusinessSdk.LogLevel.DEBUG;
+ TikTokBusinessSdk.TTConfig ttConfig = new TikTokBusinessSdk.TTConfig(getActivity().getApplicationContext());
+ try{
+ int t = Integer.parseInt(String.valueOf(((EditText)(root.findViewById(R.id.level_label_et))).getText()));
+ switch (t){
+ case 0:
+ logLevel = TikTokBusinessSdk.LogLevel.NONE;
+ break;
+ case 1:
+ logLevel = TikTokBusinessSdk.LogLevel.INFO;
+ break;
+ case 2:
+ logLevel = TikTokBusinessSdk.LogLevel.WARN;
+ break;
+ case 3:
+ logLevel = TikTokBusinessSdk.LogLevel.DEBUG;
+ break;
+
+ }
+ }catch (Throwable throwable){
+ throwable.printStackTrace();
+ }finally {
+ ttConfig.setLogLevel(logLevel);
+ }
+ try{
+ int t = Integer.parseInt(String.valueOf(((EditText)(root.findViewById(R.id.flush_time_et))).getText()));
+ ttConfig.setFlushTimeInterval(t);
+ }catch (Throwable throwable){
+ throwable.printStackTrace();
+ }
+ if(!((Switch)(root.findViewById(R.id.autostart_status))).isChecked()){
+ ttConfig.disableAutoStart();
+ InitViewModel.autoStart = false;
+ }else {
+ InitViewModel.autoStart = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.auto_events_status))).isChecked()){
+ ttConfig.disableAutoEvents();
+ InitViewModel.autoEvent = false;
+ }else {
+ InitViewModel.autoEvent = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.install_logging_status))).isChecked()){
+ ttConfig.disableInstallLogging();
+ InitViewModel.loggingStatus = false;
+ }else {
+ InitViewModel.loggingStatus = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.launch_logging_status))).isChecked()){
+ ttConfig.disableLaunchLogging();
+ InitViewModel.launchStatus = false;
+ }else {
+ InitViewModel.launchStatus = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.retention_logging_status))).isChecked()){
+ ttConfig.disableRetentionLogging();
+ InitViewModel.retentionStatus = false;
+ }else {
+ InitViewModel.retentionStatus = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.id_collection_status))).isChecked()){
+ ttConfig.disableAdvertiserIDCollection();
+ InitViewModel.advertiserIDCollectionEnable = false;
+ }else {
+ InitViewModel.advertiserIDCollectionEnable = true;
+ }
+ if(!((Switch)(root.findViewById(R.id.monitor_status))).isChecked()){
+ ttConfig.disableMonitor();
+ InitViewModel.Metrics = false;
+ }else {
+ InitViewModel.Metrics = true;
+ }
+ if(((Switch)(root.findViewById(R.id.debug_status))).isChecked()){
+ ttConfig.openDebugMode();
+ InitViewModel.debugModeSwitch = true;
+ } else {
+ InitViewModel.debugModeSwitch = false;
+ }
+ if(((Switch)(root.findViewById(R.id.limited_status))).isChecked()){
+ ttConfig.enableLimitedDataUse();
+ InitViewModel.lduModeSwitch = true;
+ } else {
+ InitViewModel.lduModeSwitch = false;
+ }
+ if(((Switch)(root.findViewById(R.id.iap_status))).isChecked()){
+ ttConfig.enableAutoIapTrack();
+ InitViewModel.autoIapTrack = true;
+ }else {
+ InitViewModel.autoIapTrack = false;
+ }
+ if(appId.getText().toString() == null || appId.getText().toString().isEmpty()){
+ ttConfig.setAppId("com.tiktok.iabtest");
+ } else {
+ ttConfig.setAppId(appId.getText().toString());
+ }
+ if(ttAppId.getText().toString() == null || ttAppId.getText().toString().isEmpty()){
+ ttConfig.setTTAppId("123456");
+ } else {
+ ttConfig.setTTAppId(ttAppId.getText().toString());
+ }
+ TikTokBusinessSdk.initializeSdk(ttConfig);
+
+ // check if user info is cached & init
+ // homeViewModel.checkInitTTAM();
+
+ TikTokBusinessSdk.setOnCrashListener((thread, ex) -> android.util.Log.i("TikTokBusinessSdk", "setOnCrashListener" + thread.getName(), ex));
+
+ // testing delay tracking, implementing a 6 sec delay manually
+ // ideally has to be after accepting tracking permission
+ if(InitViewModel.autoStart) {
+ new Handler(Looper.getMainLooper()).postDelayed(TikTokBusinessSdk::startTrack, 10000);
+ }
+ }
+ });
+ startTrack.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ TikTokBusinessSdk.startTrack();
+ }
+ });
+ return root;
+ }
+
+}
\ No newline at end of file
diff --git a/samples/TestApp/src/main/java/com/example/ui/init/InitViewModel.java b/samples/TestApp/src/main/java/com/example/ui/init/InitViewModel.java
new file mode 100644
index 0000000..d16a432
--- /dev/null
+++ b/samples/TestApp/src/main/java/com/example/ui/init/InitViewModel.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2023. Tiktok Inc.
+ *
+ * This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree.
+ ******************************************************************************/
+
+package com.example.ui.init;
+
+import android.app.Application;
+import androidx.lifecycle.AndroidViewModel;
+
+public class InitViewModel extends AndroidViewModel {
+
+ public static boolean autoEvent = true;
+ public static boolean advertiserIDCollectionEnable = true;
+ public static boolean autoStart = true;
+ public static boolean Metrics = true;
+ public static boolean debugModeSwitch = true;
+ public static boolean lduModeSwitch = false;
+ public static boolean autoIapTrack = true;
+ public static boolean loggingStatus = true;
+ public static boolean launchStatus = true;
+ public static boolean retentionStatus = true;
+
+ public InitViewModel(Application application) {
+ super(application);
+ }
+
+
+}
\ No newline at end of file
diff --git a/samples/TestApp/src/main/res/layout/activity_prop_editor.xml b/samples/TestApp/src/main/res/layout/activity_prop_editor.xml
index bc0eaf0..d461d7e 100644
--- a/samples/TestApp/src/main/res/layout/activity_prop_editor.xml
+++ b/samples/TestApp/src/main/res/layout/activity_prop_editor.xml
@@ -7,10 +7,14 @@
-
+
+
diff --git a/samples/TestApp/src/main/res/layout/fragment_events.xml b/samples/TestApp/src/main/res/layout/fragment_events.xml
index 0ba1654..5740636 100644
--- a/samples/TestApp/src/main/res/layout/fragment_events.xml
+++ b/samples/TestApp/src/main/res/layout/fragment_events.xml
@@ -96,8 +96,57 @@
android:layout_height="match_parent"
android:gravity="bottom"
tools:ignore="MissingConstraints">
+
+
+
+
+
+
+
+
+
+ android:text="@string/track_tt_event" />