diff --git a/SensorsABTestSDK/build.gradle b/SensorsABTestSDK/build.gradle index d01d6c6..f61fab1 100644 --- a/SensorsABTestSDK/build.gradle +++ b/SensorsABTestSDK/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'signing' apply plugin: 'maven-publish' -version = "0.1.0" +version = "0.1.1" android { compileSdkVersion 29 @@ -47,7 +47,7 @@ dependencies { testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - compileOnly 'com.sensorsdata.analytics.android:SensorsAnalyticsSDK:5.2.1' + compileOnly 'com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.0.0' } task sourceJar(type: Jar) { diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/ISensorsABTestApi.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/ISensorsABTestApi.java index 6955e1b..3918ea7 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/ISensorsABTestApi.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/ISensorsABTestApi.java @@ -50,6 +50,15 @@ interface ISensorsABTestApi { */ void asyncFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack); + /** + * 始终从网络请求试验结果,可自定义属性和超时时间 + * + * @param experiment 试验参数 + * @param callBack 接口回调 + * @param 默认值类型 + */ + void asyncFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack); + /** * 如果本地有缓存,则返回缓存数据;否则从网络请求最新的试验数据,默认 30s 超时时间 * @@ -70,4 +79,13 @@ interface ISensorsABTestApi { * @param 默认值类型 */ void fastFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack); + + /** + * 如果本地有缓存,则返回缓存数据;否则从网络请求最新的试验数据。可自定义属性和超时时间 + * + * @param experiment 试验参数 + * @param callBack 接口回调 + * @param 默认值类型 + */ + void fastFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack); } diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTest.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTest.java index 9484216..e2d93ed 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTest.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTest.java @@ -21,10 +21,10 @@ import android.text.TextUtils; import android.util.Log; -import com.sensorsdata.abtest.core.SensorsABTestHelper; import com.sensorsdata.abtest.core.SABErrorDispatcher; import com.sensorsdata.abtest.core.SensorsABTestApiRequestHelper; import com.sensorsdata.abtest.core.SensorsABTestCacheManager; +import com.sensorsdata.abtest.core.SensorsABTestHelper; import com.sensorsdata.abtest.entity.SABErrorEnum; import com.sensorsdata.abtest.util.AppInfoUtils; import com.sensorsdata.abtest.util.TaskRunner; @@ -34,13 +34,14 @@ import com.sensorsdata.analytics.android.sdk.TrackTaskManager; import java.lang.reflect.Method; +import java.util.Map; public class SensorsABTest implements ISensorsABTestApi { private static final String TAG = "SAB.SensorsABTest"; private static SensorsABTest sInstance; // 默认请求超时时间 - private static final int TIMEOUT_REQUEST = 30 * 1000; + protected static final int TIMEOUT_REQUEST = 30 * 1000; private SensorsABTestConfigOptions mConfigOptions; private Context mContext; @@ -139,57 +140,56 @@ public T fetchCacheABTest(String paramName, T defaultValue) { @Override public void asyncFetchABTest(String paramName, T defaultValue, OnABTestReceivedData callBack) { - try { - asyncFetchABTest(paramName, defaultValue, TIMEOUT_REQUEST, callBack); - } catch (Exception e) { - SALog.printStackTrace(e); - } + asyncFetchABTestInner(paramName, defaultValue, null, TIMEOUT_REQUEST, callBack); } @Override - public void asyncFetchABTest(final String paramName, final T defaultValue, final int timeoutMillSeconds, final OnABTestReceivedData callBack) { - try { - addTrackEventTask(new Runnable() { - @Override - public void run() { - asyncFetchABTestInner(paramName, defaultValue, timeoutMillSeconds, callBack); - } - }); - } catch (Exception e) { - SALog.printStackTrace(e); - } + public void asyncFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack) { + asyncFetchABTestInner(paramName, defaultValue, null, timeoutMillSeconds, callBack); } - private void asyncFetchABTestInner(final String paramName, final T defaultValue, int timeoutMillSeconds, final OnABTestReceivedData callBack) { - try { - if (timeoutMillSeconds > 0) { - SALog.i(TAG, "timeoutMillSeconds minimum value is 1000ms"); - timeoutMillSeconds = Math.max(1000, timeoutMillSeconds); - } else { - SALog.i(TAG, "timeoutMillSeconds params is not valid: <= 0 and set default value: " + TIMEOUT_REQUEST); - timeoutMillSeconds = TIMEOUT_REQUEST; - } - SALog.i(TAG, "asyncFetchABTest request param name: " + paramName + ",default value: " + defaultValue + ",timeoutMillSeconds: " + timeoutMillSeconds); - final String distinctId = SensorsDataAPI.sharedInstance().getDistinctId(); - final String loginId = SensorsDataAPI.sharedInstance().getLoginId(); - final String anonymousId = SensorsDataAPI.sharedInstance().getAnonymousId(); - new SensorsABTestApiRequestHelper().requestExperimentByParamName(distinctId, loginId, anonymousId, paramName, defaultValue, timeoutMillSeconds, callBack); - } catch (Exception e) { - SALog.printStackTrace(e); + @Override + public void asyncFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack) { + if (experiment == null) { + SALog.i(TAG, "experiment is null, check your param please!"); + return; } + asyncFetchABTestInner(experiment.paramName, experiment.defaultValue, experiment.properties, experiment.timeoutMillSeconds, callBack); } @Override public void fastFetchABTest(String paramName, T defaultValue, OnABTestReceivedData callBack) { + fastFetchABTestInner(paramName, defaultValue, null, TIMEOUT_REQUEST, callBack); + } + + @Override + public void fastFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack) { + fastFetchABTestInner(paramName, defaultValue, null, timeoutMillSeconds, callBack); + } + + @Override + public void fastFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack) { + if (experiment == null) { + SALog.i(TAG, "experiment is null, check your param please!"); + return; + } + fastFetchABTestInner(experiment.paramName, experiment.defaultValue, experiment.properties, experiment.timeoutMillSeconds, callBack); + } + + private void asyncFetchABTestInner(final String paramName, final T defaultValue, final Map properties, final int timeoutMillSeconds, final OnABTestReceivedData callBack) { try { - fastFetchABTest(paramName, defaultValue, TIMEOUT_REQUEST, callBack); + addTrackEventTask(new Runnable() { + @Override + public void run() { + requestExperimentWithParams(paramName, defaultValue, properties, timeoutMillSeconds, callBack); + } + }); } catch (Exception e) { SALog.printStackTrace(e); } } - @Override - public void fastFetchABTest(final String paramName, final T defaultValue, final int timeoutMillSeconds, final OnABTestReceivedData callBack) { + private void fastFetchABTestInner(final String paramName, final T defaultValue, final Map properties, final int timeoutMillSeconds, final OnABTestReceivedData callBack) { try { addTrackEventTask(new Runnable() { @Override @@ -204,7 +204,7 @@ public void run() { } }); } else { - asyncFetchABTestInner(paramName, defaultValue, timeoutMillSeconds, callBack); + requestExperimentWithParams(paramName, defaultValue, properties, timeoutMillSeconds, callBack); } } catch (Exception e) { SALog.printStackTrace(e); @@ -216,6 +216,25 @@ public void run() { } } + private void requestExperimentWithParams(final String paramName, final T defaultValue, Map properties, int timeoutMillSeconds, final OnABTestReceivedData callBack) { + try { + if (timeoutMillSeconds > 0) { + SALog.i(TAG, "timeoutMillSeconds minimum value is 1000ms"); + timeoutMillSeconds = Math.max(1000, timeoutMillSeconds); + } else { + SALog.i(TAG, "timeoutMillSeconds params is not valid: <= 0 and set default value: " + TIMEOUT_REQUEST); + timeoutMillSeconds = TIMEOUT_REQUEST; + } + SALog.i(TAG, "asyncFetchABTest request param name: " + paramName + ",default value: " + defaultValue + ",timeoutMillSeconds: " + timeoutMillSeconds); + final String distinctId = SensorsDataAPI.sharedInstance().getDistinctId(); + final String loginId = SensorsDataAPI.sharedInstance().getLoginId(); + final String anonymousId = SensorsDataAPI.sharedInstance().getAnonymousId(); + new SensorsABTestApiRequestHelper().requestExperimentByParamName(distinctId, loginId, anonymousId, paramName, defaultValue, properties, timeoutMillSeconds, callBack); + } catch (Exception e) { + SALog.printStackTrace(e); + } + } + private static void addTrackEventTask(Runnable runnable) { try { Object obj = TrackTaskManager.getInstance(); diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestEmptyImplementation.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestEmptyImplementation.java index b19ae66..f23e284 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestEmptyImplementation.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestEmptyImplementation.java @@ -47,6 +47,10 @@ public void asyncFetchABTest(String paramName, T defaultValue, OnABTestRecei public void asyncFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack) { } + @Override + public void asyncFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack) { + } + @Override public void fastFetchABTest(String paramName, T defaultValue, OnABTestReceivedData callBack) { } @@ -54,4 +58,8 @@ public void fastFetchABTest(String paramName, T defaultValue, OnABTestReceiv @Override public void fastFetchABTest(String paramName, T defaultValue, int timeoutMillSeconds, OnABTestReceivedData callBack) { } + + @Override + public void fastFetchABTest(SensorsABTestExperiment experiment, OnABTestReceivedData callBack) { + } } diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestExperiment.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestExperiment.java new file mode 100644 index 0000000..e5a1ed6 --- /dev/null +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/SensorsABTestExperiment.java @@ -0,0 +1,98 @@ +/* + * Created by luweibin on 2021/10/21. + * Copyright 2015-2021 Sensors Data Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sensorsdata.abtest; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SensorsABTestExperiment { + protected final String paramName; + protected final T defaultValue; + protected Map properties; + protected int timeoutMillSeconds = SensorsABTest.TIMEOUT_REQUEST; + + private SensorsABTestExperiment(String paramName, T defaultValue) { + this.paramName = paramName; + this.defaultValue = defaultValue; + } + + @Override + public String toString() { + return "SensorsABTestExperiment{" + + "paramName='" + paramName + '\'' + + ", defaultValue=" + defaultValue + + ", properties=" + properties + + ", timeoutMillSeconds=" + timeoutMillSeconds + + '}'; + } + + public static ExperimentBuilder newBuilder(String paramName, T defaultValue) { + return new ExperimentBuilder<>(paramName, defaultValue); + } + + public static class ExperimentBuilder { + private final SensorsABTestExperiment experiment; + + private ExperimentBuilder(String paramName, T defaultValue) { + experiment = new SensorsABTestExperiment<>(paramName, defaultValue); + } + + public ExperimentBuilder addProperty(String propertyKey, CharSequence propertyValue) { + addObjectProperty(propertyKey, propertyValue); + return this; + } + + public ExperimentBuilder addProperty(String propertyKey, boolean propertyValue) { + addObjectProperty(propertyKey, propertyValue); + return this; + } + + public ExperimentBuilder addProperty(String propertyKey, Number propertyValue) { + addObjectProperty(propertyKey, propertyValue); + return this; + } + + public ExperimentBuilder addProperty(String propertyKey, List propertyValue) { + addObjectProperty(propertyKey, propertyValue); + return this; + } + + public ExperimentBuilder addProperty(String propertyKey, Date propertyValue) { + addObjectProperty(propertyKey, propertyValue); + return this; + } + + private void addObjectProperty(String propertyKey, Object propertyValue) { + if (experiment.properties == null) { + experiment.properties = new HashMap<>(); + } + experiment.properties.put(propertyKey, propertyValue); + } + + public ExperimentBuilder setTimeoutMillSeconds(int timeoutMillSeconds) { + experiment.timeoutMillSeconds = timeoutMillSeconds; + return this; + } + + public SensorsABTestExperiment create() { + return experiment; + } + } +} diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestApiRequestHelper.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestApiRequestHelper.java index 628afe0..14fc0d7 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestApiRequestHelper.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestApiRequestHelper.java @@ -29,6 +29,8 @@ import com.sensorsdata.abtest.entity.Experiment; import com.sensorsdata.abtest.entity.ExperimentRequest; import com.sensorsdata.abtest.entity.SABErrorEnum; +import com.sensorsdata.abtest.exception.DataInvalidException; +import com.sensorsdata.abtest.util.SensorsDataHelper; import com.sensorsdata.abtest.util.TaskRunner; import com.sensorsdata.abtest.util.UrlUtil; import com.sensorsdata.analytics.android.sdk.SALog; @@ -51,7 +53,9 @@ public class SensorsABTestApiRequestHelper { private boolean mHasCallback = false; private String mDistinctId; - public void requestExperimentByParamName(final String distinctId, final String loginId, final String anonymousId, final String paramName, final T defaultValue, final int timeoutMillSeconds, final OnABTestReceivedData callBack) { + public void requestExperimentByParamName(final String distinctId, final String loginId, final String anonymousId, + final String paramName, final T defaultValue, Map properties, + final int timeoutMillSeconds, final OnABTestReceivedData callBack) { // callback 为 null if (callBack == null) { SALog.i(TAG, "试验 callback 不正确,试验 callback 不能为空!"); @@ -80,12 +84,27 @@ public void requestExperimentByParamName(final String distinctId, final String l return; } + // 自定义参数校验 + Map propertiesString = null; + if (properties != null && properties.size() > 0) { + try { + propertiesString = SensorsDataHelper.checkPropertiesAndToString(properties); + } catch (DataInvalidException e) { + if (!mHasCallback) { + SABErrorDispatcher.dispatchSABException(SABErrorEnum.ASYNC_REQUEST_PROPERTIES_NOT_VALID, e.getMessage()); + mHasCallback = true; + doCallbackOnMainThread(callBack, defaultValue); + } + return; + } + } + // 启动定时器 final TimeoutRunnable runnable = new TimeoutRunnable(callBack, defaultValue); TaskRunner.getBackHandler().postDelayed(runnable, timeoutMillSeconds); mDistinctId = distinctId; - requestExperimentsAndUpdateCache(new IApiCallback>() { + requestExperimentsAndUpdateCache(propertiesString, paramName, new IApiCallback>() { @Override public void onSuccess(Map experimentMap) { try { @@ -159,11 +178,11 @@ public void onFailure(int errorCode, String message) { }); } - void requestExperiments(final IApiCallback callBack) { - requestExperiments(null, callBack); + void requestExperiments(Map properties, String paramName, final IApiCallback callBack) { + requestExperiments(properties, paramName, null, callBack); } - void requestExperiments(JSONObject object, final IApiCallback callBack) { + void requestExperiments(Map properties, String paramName, JSONObject object, final IApiCallback callBack) { String url = null, key = null; SensorsABTestConfigOptions configOptions = SensorsABTest.shareInstance().getConfigOptions(); if (configOptions != null) { @@ -184,7 +203,7 @@ void requestExperiments(JSONObject object, final IApiCallback callBack) headers.put("project-key", key); new RequestHelper.Builder(HttpMethod.POST, url) .header(headers) - .jsonData(new ExperimentRequest(object).createRequestBody().toString()) + .jsonData(new ExperimentRequest(properties, paramName, object).createRequestBody().toString()) .callback(new HttpCallback.StringCallback() { @Override public void onFailure(final int code, final String errorMessage) { @@ -208,11 +227,11 @@ public void onAfter() { } public void requestExperimentsAndUpdateCache() { - requestExperimentsAndUpdateCache(null); + requestExperimentsAndUpdateCache(null, null, null); } - void requestExperimentsAndUpdateCache(final IApiCallback> callBack) { - requestExperiments(new IApiCallback() { + void requestExperimentsAndUpdateCache(Map properties, String paramName, final IApiCallback> callBack) { + requestExperiments(properties, paramName, new IApiCallback() { @Override public void onSuccess(String s) { try { diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestH5Helper.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestH5Helper.java index aee3146..0e804d5 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestH5Helper.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestH5Helper.java @@ -68,7 +68,7 @@ public void handlerJSMessage() { if (data != null) { request.messageId = data.optString("message_id"); final String distinctId = SensorsDataAPI.sharedInstance().getDistinctId(); - new SensorsABTestApiRequestHelper<>().requestExperiments(data.optJSONObject("request_body"), new IApiCallback() { + new SensorsABTestApiRequestHelper<>().requestExperiments(null, null, data.optJSONObject("request_body"), new IApiCallback() { @Override public void onSuccess(String s) { if (TextUtils.isEmpty(s)) { diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestHelper.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestHelper.java index 974c419..463b15a 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestHelper.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/core/SensorsABTestHelper.java @@ -92,7 +92,7 @@ public void run() { mCountDownTimer = new CountDownTimer(120 * 1000, 30 * 1000) { @Override public void onTick(long l) { - new SensorsABTestApiRequestHelper<>().requestExperimentsAndUpdateCache(new IApiCallback>() { + new SensorsABTestApiRequestHelper<>().requestExperimentsAndUpdateCache(null, null, new IApiCallback>() { @Override public void onSuccess(Map stringExperimentMap) { cancelTimer(); diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/ExperimentRequest.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/ExperimentRequest.java index f8dc786..32b58a0 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/ExperimentRequest.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/ExperimentRequest.java @@ -28,13 +28,18 @@ import org.json.JSONObject; import java.util.Iterator; +import java.util.Map; public class ExperimentRequest { private static final String TAG = "SAB.RequestParams"; private JSONObject mJSONObject; + private Map mProperties; + private String mParamName; - public ExperimentRequest(JSONObject jsonObject) { + public ExperimentRequest(Map properties, String paramName, JSONObject jsonObject) { + this.mProperties = properties; this.mJSONObject = jsonObject; + this.mParamName = paramName; } public JSONObject createRequestBody() { @@ -48,6 +53,10 @@ public JSONObject createRequestBody() { jsonObject.put("platform", "Android"); jsonObject.put("properties", getPresetProperties()); jsonObject.put("abtest_lib_version", BuildConfig.SDK_VERSION); + if (mProperties != null && mProperties.size() > 0 && !TextUtils.isEmpty(mParamName)) { + jsonObject.put("custom_properties", new JSONObject(mProperties)); + jsonObject.put("param_name", mParamName); + } try { if (mJSONObject != null) { Iterator iterator = mJSONObject.keys(); diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/SABErrorEnum.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/SABErrorEnum.java index 91af940..af85102 100644 --- a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/SABErrorEnum.java +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/entity/SABErrorEnum.java @@ -32,7 +32,8 @@ public enum SABErrorEnum { ASYNC_REQUEST_NULL_EXPERIMENT_PARAMETER_NAME("1001", "The experiment param name of async request is empty and return default value: %s"), ASYNC_REQUEST_NETWORK_UNAVAILABLE("1002", "Network is unavailable and return default value: %s"), ASYNC_REQUEST_TIMEOUT("1003", "AsyncRequest is timeout and return default value: %s"), - ASYNC_REQUEST_PARAMS_TYPE_NOT_VALID("1004", "试验结果类型与代码期望类型不一致,试验参数名:%s,当前返回类型为:%s,代码期望类型为:%s"); + ASYNC_REQUEST_PARAMS_TYPE_NOT_VALID("1004", "试验结果类型与代码期望类型不一致,试验参数名:%s,当前返回类型为:%s,代码期望类型为:%s"), + ASYNC_REQUEST_PROPERTIES_NOT_VALID("1005", "请求试验的自定义参数校验不通过: %s"); public String code; public String message; diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/exception/DataInvalidException.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/exception/DataInvalidException.java new file mode 100644 index 0000000..8f79958 --- /dev/null +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/exception/DataInvalidException.java @@ -0,0 +1,24 @@ +/* + * Created by luweibin on 2021/10/26. + * Copyright 2015-2021 Sensors Data Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sensorsdata.abtest.exception; + +public class DataInvalidException extends Exception { + public DataInvalidException(String message) { + super(message); + } +} diff --git a/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/util/SensorsDataHelper.java b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/util/SensorsDataHelper.java new file mode 100644 index 0000000..9324d0d --- /dev/null +++ b/SensorsABTestSDK/src/main/java/com/sensorsdata/abtest/util/SensorsDataHelper.java @@ -0,0 +1,120 @@ +/* + * Created by luweibin on 2021/10/22. + * Copyright 2015-2021 Sensors Data Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.sensorsdata.abtest.util; + +import android.text.TextUtils; +import android.util.Pair; + +import com.sensorsdata.abtest.exception.DataInvalidException; +import com.sensorsdata.analytics.android.sdk.util.TimeUtils; + +import org.json.JSONArray; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +public class SensorsDataHelper { + + private static final Pattern KEY_PATTERN = Pattern.compile( + "^((?!^distinct_id$|^original_id$|^time$|^properties$|^id$|^first_id$|^second_id$|^users$|^events$|^event$|^user_id$|^date$|^datetime$)[a-zA-Z_][a-zA-Z\\d_]{0,100})$", + Pattern.CASE_INSENSITIVE); + private static final int PROPERTY_VALUE_MAX_LENGTH = 8191; + + public static Map checkPropertiesAndToString(Map properties) throws DataInvalidException { + if (properties != null && properties.size() != 0) { + Map stringProperties = new HashMap<>(properties.size()); + for (Map.Entry entry : properties.entrySet()) { + // step1.校验自定义参数名和参数值 + checkProperty(entry); + + // step2.转换自定义参数值为 String 类型 + Pair stringProperty = propertiesToString(entry); + + // step3.将转换的 Key-Value 作为返回值 + stringProperties.put(stringProperty.first, stringProperty.second); + } + return stringProperties; + } + return null; + } + + private static Pair propertiesToString(Map.Entry entry) throws DataInvalidException { + Object value = entry.getValue(); + String stringValue; + if (value instanceof Date) { + stringValue = TimeUtils.formatDate((Date) value); + } else if (value instanceof List) { + JSONArray jsonArray = new JSONArray(); + for (Object item : (List) value) { + jsonArray.put(item.toString()); + } + stringValue = jsonArray.toString(); + } else { + stringValue = value.toString(); + } + if (stringValue.length() > PROPERTY_VALUE_MAX_LENGTH) { + throw new DataInvalidException(createStringValueInvalidMsg(entry.getKey(), stringValue)); + } + return new Pair<>(entry.getKey(), stringValue); + } + + private static void checkProperty(Map.Entry entry) throws DataInvalidException { + String key = entry.getKey(); + Object value = entry.getValue(); + if (TextUtils.isEmpty(key)) { + throw new DataInvalidException(createKeyInvalidMsg(key)); + } + if (key.length() > 100) { + throw new DataInvalidException(createKeyInvalidMsg(key)); + } + if (!(KEY_PATTERN.matcher(key).matches())) { + throw new DataInvalidException(createKeyInvalidMsg(key)); + } + if (value == null) { + throw new DataInvalidException(createValueInvalidMsg(key, null)); + } + if (!(value instanceof CharSequence || value instanceof Number || value instanceof List || + value instanceof Boolean || value instanceof Date)) { + throw new DataInvalidException(createValueInvalidMsg(key, value)); + } + if (value instanceof List) { + for (Object item : (List) value) { + if (!(item instanceof String)) { + throw new DataInvalidException(createValueInvalidMsg(key, value)); + } + } + } + } + + private static String createKeyInvalidMsg(String key) { + return "property name [ " + key + " ] is not valid"; + } + + private static String createValueInvalidMsg(String key, Object value) { + return "property values must be String, Number, List, Boolean or Date. property " + + "[ " + key + " ] of value " + + "[ " + (value == null ? "null" : value.toString()) + " ] is not valid"; + } + + private static String createStringValueInvalidMsg(String key, Object value) { + return "property [ " + key + " ] of value [ " + (value == null ? "null" : value.toString()) + " ] is not valid"; + } +} diff --git a/app/build.gradle b/app/build.gradle index a358b04..644c703 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,5 +54,5 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation project(path: ':SensorsABTestSDK') implementation 'com.tencent.tbs.tbssdk:sdk:43903' - implementation 'com.sensorsdata.analytics.android:SensorsAnalyticsSDK:4.3.6' + implementation 'com.sensorsdata.analytics.android:SensorsAnalyticsSDK:6.0.0' } \ No newline at end of file diff --git a/app/src/main/java/com/sensorsdata/sensorsabtest/MyApplication.java b/app/src/main/java/com/sensorsdata/sensorsabtest/MyApplication.java index bcb1216..39ec1d9 100644 --- a/app/src/main/java/com/sensorsdata/sensorsabtest/MyApplication.java +++ b/app/src/main/java/com/sensorsdata/sensorsabtest/MyApplication.java @@ -39,8 +39,7 @@ public void onCreate() { .enableTrackAppCrash() .enableLog(true) .enableJavaScriptBridge(true) - .enableVisualizedAutoTrack(true) - .enableVisualizedAutoTrackConfirmDialog(true); + .enableVisualizedAutoTrack(true); SensorsDataAPI.startWithConfigOptions(this, configOptions); SensorsABTestConfigOptions abTestConfigOptions = new SensorsABTestConfigOptions("http://10.120.52.81:8222/api/v2/abtest/online/results?project-key=fake"); diff --git a/app/src/main/res/layout/activity_main_layout.xml b/app/src/main/res/layout/activity_main_layout.xml index 5d425a4..6cb3cd5 100644 --- a/app/src/main/res/layout/activity_main_layout.xml +++ b/app/src/main/res/layout/activity_main_layout.xml @@ -3,136 +3,148 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:padding="16dp" - tools:context=".MainActivity"> - - - - - - - - - - - - - - - - - - - - - - - - - - - -