diff --git a/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessage.java b/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessage.java deleted file mode 100644 index 5ff009dec..000000000 --- a/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessage.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.mparticle.internal.messages; - -import android.location.Location; - -import androidx.annotation.Nullable; - -import com.mparticle.internal.Constants; -import com.mparticle.internal.InternalSession; -import com.mparticle.internal.MPUtility; - -import org.json.JSONException; -import org.json.JSONObject; - -public class BaseMPMessage extends JSONObject { - private long mpId; - - protected BaseMPMessage() { - } - - protected BaseMPMessage(BaseMPMessageBuilder builder, InternalSession session, @Nullable Location location, long mpId) throws JSONException { - super(builder, builder.getKeys()); - this.mpId = mpId; - if (!has(Constants.MessageKey.TIMESTAMP)) { - put(Constants.MessageKey.TIMESTAMP, session.mLastEventTime); - } - if (Constants.MessageType.SESSION_START.equals(builder.getMessageType())) { - put(Constants.MessageKey.ID, session.mSessionID); - } else { - put(Constants.MessageKey.SESSION_ID, session.mSessionID); - - if (session.mSessionStartTime > 0) { - put(Constants.MessageKey.SESSION_START_TIMESTAMP, session.mSessionStartTime); - } - } - - - if (!(Constants.MessageType.ERROR.equals(builder.getMessageType()) && - !(Constants.MessageType.OPT_OUT.equals(builder.getMessageType())))) { - if (location != null) { - JSONObject locJSON = new JSONObject(); - locJSON.put(Constants.MessageKey.LATITUDE, location.getLatitude()); - locJSON.put(Constants.MessageKey.LONGITUDE, location.getLongitude()); - locJSON.put(Constants.MessageKey.ACCURACY, location.getAccuracy()); - put(Constants.MessageKey.LOCATION, locJSON); - } - } - } - - public JSONObject getAttributes() { - return optJSONObject(Constants.MessageKey.ATTRIBUTES); - } - - public long getTimestamp() { - try { - return getLong(Constants.MessageKey.TIMESTAMP); - } catch (JSONException jse) { - - } - return 0; - } - - public String getSessionId() { - if (Constants.MessageType.SESSION_START.equals(getMessageType())) { - return optString(Constants.MessageKey.ID, Constants.NO_SESSION_ID); - } else { - return optString(Constants.MessageKey.SESSION_ID, Constants.NO_SESSION_ID); - } - } - - public String getMessageType() { - return optString(Constants.MessageKey.TYPE); - } - - public int getTypeNameHash() { - return MPUtility.mpHash(getMessageType() + getName()); - } - - public String getName() { - return optString(Constants.MessageKey.NAME); - } - - public long getMpId() { - return mpId; - } - - public static class Builder extends BaseMPMessageBuilder { - - public Builder(String messageType) { - super(messageType); - } - } -} diff --git a/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessageBuilder.java b/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessageBuilder.java deleted file mode 100644 index a919b1d28..000000000 --- a/android-core/src/main/java/com/mparticle/internal/messages/BaseMPMessageBuilder.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.mparticle.internal.messages; - - -import android.location.Location; - -import androidx.annotation.Nullable; - -import com.mparticle.internal.Constants; -import com.mparticle.internal.InternalSession; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -public class BaseMPMessageBuilder extends JSONObject { - private Double mLength = null; - - public BaseMPMessageBuilder(String messageType) { - try { - put(Constants.MessageKey.TYPE, messageType); - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public BaseMPMessageBuilder timestamp(long timestamp) { - try { - put(Constants.MessageKey.TIMESTAMP, timestamp); - } catch (JSONException e) { - e.printStackTrace(); - } - return this; - } - - public Long getTimestamp() { - long timestamp = optLong(Constants.MessageKey.TIMESTAMP, -1); - if (timestamp != -1) { - return timestamp; - } else { - return null; - } - } - - public BaseMPMessageBuilder name(String name) { - try { - put(Constants.MessageKey.NAME, name); - } catch (JSONException e) { - e.printStackTrace(); - } - return this; - } - - public BaseMPMessageBuilder attributes(JSONObject attributes) { - if (attributes != null && attributes.length() > 0) { - try { - put(Constants.MessageKey.ATTRIBUTES, attributes); - if (mLength != null) { - addEventLengthAttributes(mLength); - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - return this; - } - - public String getMessageType() { - return optString(Constants.MessageKey.TYPE); - } - - public BaseMPMessage build(InternalSession session, @Nullable Location location, long mpId) throws JSONException { - return new BaseMPMessage(this, session, location, mpId); - } - - public BaseMPMessageBuilder dataConnection(String dataConnection) { - if (dataConnection != null) { - try { - put(Constants.MessageKey.STATE_INFO_DATA_CONNECTION, dataConnection); - } catch (JSONException e) { - e.printStackTrace(); - } - } - return this; - } - - public BaseMPMessageBuilder length(Double length) { - mLength = length; - if (length != null) { - try { - put(Constants.MessageKey.EVENT_DURATION, length); - addEventLengthAttributes(length); - } catch (JSONException e) { - e.printStackTrace(); - } - } - return this; - } - - private void addEventLengthAttributes(Double length) { - try { - if (!has(Constants.MessageKey.ATTRIBUTES)) { - put(Constants.MessageKey.ATTRIBUTES, new JSONObject()); - } - if (!getJSONObject(Constants.MessageKey.ATTRIBUTES).has("EventLength")) { - //can't be longer than max int milliseconds - getJSONObject(Constants.MessageKey.ATTRIBUTES).put("EventLength", Integer.toString(length.intValue())); - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - - public BaseMPMessageBuilder flags(Map> customFlags) { - if (customFlags != null) { - try { - - JSONObject flagsObject = new JSONObject(); - for (Map.Entry> entry : customFlags.entrySet()) { - List values = entry.getValue(); - JSONArray valueArray = new JSONArray(values); - flagsObject.put(entry.getKey(), valueArray); - } - put(Constants.MessageKey.EVENT_FLAGS, flagsObject); - } catch (JSONException e) { - e.printStackTrace(); - } - } - return this; - } - - String[] getKeys() { - List strings = new ArrayList(); - Iterator iterator = keys(); - while (iterator.hasNext()) { - strings.add(iterator.next()); - } - return strings.toArray(new String[0]); - } -} \ No newline at end of file diff --git a/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessage.kt b/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessage.kt new file mode 100644 index 000000000..392de1cc6 --- /dev/null +++ b/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessage.kt @@ -0,0 +1,75 @@ +package com.mparticle.internal.messages + +import android.location.Location +import com.mparticle.internal.Constants +import com.mparticle.internal.InternalSession +import com.mparticle.internal.MPUtility +import org.json.JSONException +import org.json.JSONObject + +open class BaseMPMessage : JSONObject { + var mpId: Long = 0 + private set + + protected constructor() + + constructor(builder: BaseMPMessageBuilder, session: InternalSession, location: Location?, mpId: Long) : super(builder, builder.keys) { + this.mpId = mpId + if (!has(Constants.MessageKey.TIMESTAMP)) { + put(Constants.MessageKey.TIMESTAMP, session.mLastEventTime) + } + if (Constants.MessageType.SESSION_START == builder.messageType) { + put(Constants.MessageKey.ID, session.mSessionID) + } else { + put(Constants.MessageKey.SESSION_ID, session.mSessionID) + + if (session.mSessionStartTime > 0) { + put(Constants.MessageKey.SESSION_START_TIMESTAMP, session.mSessionStartTime) + } + } + + if (!(Constants.MessageType.ERROR == builder.messageType && + Constants.MessageType.OPT_OUT != builder.messageType) + ) { + if (location != null) { + val locJSON = JSONObject() + locJSON.put(Constants.MessageKey.LATITUDE, location.latitude) + locJSON.put(Constants.MessageKey.LONGITUDE, location.longitude) + locJSON.put(Constants.MessageKey.ACCURACY, location.accuracy.toDouble()) + put(Constants.MessageKey.LOCATION, locJSON) + } + } + } + + val attributes: JSONObject? + get() = optJSONObject(Constants.MessageKey.ATTRIBUTES) + + val timestamp: Long + get() { + try { + return getLong(Constants.MessageKey.TIMESTAMP) + } catch (_: JSONException) { + } + return 0 + } + + val sessionId: String + get() = + if (Constants.MessageType.SESSION_START == messageType) { + optString(Constants.MessageKey.ID, Constants.NO_SESSION_ID) + } else { + optString(Constants.MessageKey.SESSION_ID, Constants.NO_SESSION_ID) + } + + val messageType: String + get() = optString(Constants.MessageKey.TYPE) + + val typeNameHash: Int + get() = MPUtility.mpHash(messageType + name) + + + val name: String + get() = optString(Constants.MessageKey.NAME) + + class Builder(messageType: String) : BaseMPMessageBuilder(messageType) +} diff --git a/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessageBuilder.kt b/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessageBuilder.kt new file mode 100644 index 000000000..1ec4ae44d --- /dev/null +++ b/android-core/src/main/kotlin/com/mparticle/internal/messages/BaseMPMessageBuilder.kt @@ -0,0 +1,125 @@ +package com.mparticle.internal.messages + +import android.location.Location +import com.mparticle.internal.Constants +import com.mparticle.internal.InternalSession +import org.json.JSONArray +import org.json.JSONException +import org.json.JSONObject + + +open class BaseMPMessageBuilder(messageType: String) : JSONObject() { + private var mLength: Double? = null + + init { + try { + put(Constants.MessageKey.TYPE, messageType) + } catch (e: JSONException) { + e.printStackTrace() + } + } + + + open fun timestamp(timestamp: Long): BaseMPMessageBuilder { + try { + put(Constants.MessageKey.TIMESTAMP, timestamp) + } catch (e: JSONException) { + e.printStackTrace() + } + return this + } + + val timestamp: Long? + get() { + val timestamp = optLong(Constants.MessageKey.TIMESTAMP, -1) + return if (timestamp != -1L) { + timestamp + } else { + null + } + } + + fun name(name: String): BaseMPMessageBuilder { + try { + put(Constants.MessageKey.NAME, name) + } catch (e: JSONException) { + e.printStackTrace() + } + return this + } + + fun attributes(attributes: JSONObject?): BaseMPMessageBuilder { + if (attributes != null && attributes.length() > 0) { + try { + put(Constants.MessageKey.ATTRIBUTES, attributes) + mLength?.let { addEventLengthAttributes(it) } + } catch (e: JSONException) { + e.printStackTrace() + } + } + return this + } + + val messageType: String + get() = optString(Constants.MessageKey.TYPE) + + @Throws(JSONException::class) + open fun build(session: InternalSession, location: Location?, mpId: Long): BaseMPMessage { + return BaseMPMessage(this, session, location, mpId) + } + + fun dataConnection(dataConnection: String): BaseMPMessageBuilder { + try { + put(Constants.MessageKey.STATE_INFO_DATA_CONNECTION, dataConnection) + } catch (e: JSONException) { + e.printStackTrace() + } + return this + } + + fun length(length: Double?): BaseMPMessageBuilder { + mLength = length + if (length != null) { + try { + put(Constants.MessageKey.EVENT_DURATION, length) + addEventLengthAttributes(length) + } catch (e: JSONException) { + e.printStackTrace() + } + } + return this + } + + private fun addEventLengthAttributes(length: Double) { + try { + if (!has(Constants.MessageKey.ATTRIBUTES)) { + put(Constants.MessageKey.ATTRIBUTES, JSONObject()) + } + if (!getJSONObject(Constants.MessageKey.ATTRIBUTES).has("EventLength")) { + //can't be longer than max int milliseconds + getJSONObject(Constants.MessageKey.ATTRIBUTES).put("EventLength", length.toInt().toString()) + } + } catch (e: JSONException) { + e.printStackTrace() + } + } + + fun flags(customFlags: Map>?): BaseMPMessageBuilder { + if (customFlags != null) { + try { + val flagsObject = JSONObject() + for ((key, values) in customFlags) { + val valueArray = JSONArray(values) + flagsObject.put(key, valueArray) + } + put(Constants.MessageKey.EVENT_FLAGS, flagsObject) + } catch (e: JSONException) { + e.printStackTrace() + } + } + return this + } + + val keys: Array + get() = keys().asSequence().toList().toTypedArray() +} \ No newline at end of file diff --git a/android-core/src/test/kotlin/com/mparticle/internal/BaseMPMessageTest.kt b/android-core/src/test/kotlin/com/mparticle/internal/BaseMPMessageTest.kt index fac65c92b..edc5e463b 100644 --- a/android-core/src/test/kotlin/com/mparticle/internal/BaseMPMessageTest.kt +++ b/android-core/src/test/kotlin/com/mparticle/internal/BaseMPMessageTest.kt @@ -1,16 +1,39 @@ package com.mparticle.internal +import android.location.Location import com.mparticle.MPEvent import com.mparticle.MParticle import com.mparticle.MockMParticle import com.mparticle.commerce.CommerceEvent import com.mparticle.commerce.Product import com.mparticle.internal.messages.BaseMPMessage +import com.mparticle.internal.messages.BaseMPMessageBuilder import com.mparticle.internal.messages.MPCommerceMessage import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.powermock.api.mockito.PowerMockito +import org.powermock.core.classloader.annotations.PrepareForTest +import org.powermock.modules.junit4.PowerMockRunner +@RunWith(PowerMockRunner::class) +@PrepareForTest(Location::class, BaseMPMessageBuilder::class) class BaseMPMessageTest { + @Mock + lateinit var location: Location + private lateinit var builder: BaseMPMessageBuilder + + @Before + fun setUp() { + // Initialize PowerMockito mocks + PowerMockito.mockStatic(Location::class.java) + builder = PowerMockito.mock(BaseMPMessageBuilder::class.java) + } + @Test @Throws(Exception::class) fun testEventLength() { @@ -34,7 +57,7 @@ class BaseMPMessageTest { .length(event2.length) .attributes(MPUtility.enforceAttributeConstraints(event2.customAttributeStrings)) .build(session, null, 1) - Assert.assertEquals(message2.attributes.getString("EventLength"), "321") + Assert.assertEquals(message2.attributes?.getString("EventLength"), "321") val event3 = MPEvent.Builder("test name", MParticle.EventType.Navigation).duration(123.0).build() val message3 = BaseMPMessage.Builder(Constants.MessageType.EVENT) @@ -43,7 +66,24 @@ class BaseMPMessageTest { .length(event3.length) .attributes(MPUtility.enforceAttributeConstraints(event.customAttributeStrings)) .build(session, null, 1) - Assert.assertEquals(message3.attributes.getString("EventLength"), "123") + Assert.assertEquals(message3.attributes?.getString("EventLength"), "123") + } + + @Test + fun testNotNullOnTimestampAndSessionId() { + val location = mock(Location::class.java) + val event = MPEvent.Builder("test name", MParticle.EventType.Navigation).build() + val session = InternalSession() + val message = BaseMPMessage.Builder(Constants.MessageType.SESSION_START) + .name(event.eventName) + .length(event.length) + .timestamp(System.currentTimeMillis()) + .attributes(MPUtility.enforceAttributeConstraints(event.customAttributeStrings)) + .build(session, location, 1) + Assert.assertNull(message.opt("el")) + Assert.assertNotNull(message.timestamp) + Assert.assertNotNull(message.typeNameHash) + Assert.assertNotNull(message.sessionId) } @Test diff --git a/tooling/custom-lint-rules/build.gradle b/tooling/custom-lint-rules/build.gradle index 99af1017b..3c717ae1e 100755 --- a/tooling/custom-lint-rules/build.gradle +++ b/tooling/custom-lint-rules/build.gradle @@ -74,10 +74,17 @@ rootProject.project('android-core').android.buildTypes.all { task zipSources(type: Jar) { def fileName = "$project.rootDir/android-core/build/intermediates/javac/${targetBuildType}/classes" + def kotlinClassesPath = "$project.rootDir/android-core/build/tmp/kotlin-classes/${targetBuildType}" + from(fileTree(dir: fileName)) { - destinationDir new File("$project.projectDir/libs") - archiveName "mparticle.jar" + include '**/*.class' // Include Java classes + + } + from(fileTree(dir: kotlinClassesPath)) { + include '**/*.class' // Include Kotlin classes } + destinationDir new File("$project.projectDir/libs") + archiveName "mparticle.jar" outputs.upToDateWhen { false } }