Skip to content

Commit

Permalink
feat: update MParticleUser.incrementUserAttribute to accept Number (#188
Browse files Browse the repository at this point in the history
)

* feat: change incrementUserAttribute method signature to accept Number

* feat: add TypedUserAttributeListener for the async MParticleUser.getUserAttributes() method to fetch non-String attribute values
  • Loading branch information
jshin-tse authored and willpassidomo committed Jul 21, 2022
1 parent 0ecd278 commit 670bdd6
Show file tree
Hide file tree
Showing 33 changed files with 423 additions and 107 deletions.
11 changes: 1 addition & 10 deletions android-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ android {
lintConfig file('lint-baseline.xml')
}
sourceSets {
main.kotlin.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
Expand Down Expand Up @@ -165,16 +166,6 @@ configurations {
}
}

afterEvaluate {
android.buildTypes.all { theBuildType ->
android.sourceSets.all { sourceSet ->
if (!sourceSet.name.startsWith("test") && !sourceSet.name.startsWith("androidTest")) {
sourceSet.kotlin.setSrcDirs([])
}
}
}
}

boolean useOrchestrator() {
return project.hasProperty('orchestrator') ? project.property('orchestrator') : false
}
2 changes: 2 additions & 0 deletions android-core/proguard.pro
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@
-keep class com.mparticle.AttributionResult { *; }
-keep class com.mparticle.MParticleTask { *; }
-keep class com.mparticle.UserAttributeListener { *; }
-keep class com.mparticle.TypedUserAttributeListener { *; }
-keep class com.mparticle.UserAttributeListenerType { *; }
-keep class com.mparticle.BaseEvent { *; }
-keep class com.mparticle.BaseEvent$Type { *; }
-keep class com.mparticle.BaseEvent$MessageType { *; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

public class MParticleUserDelegateTest extends BaseCleanStartedEachTest {
public class MParticleUserDelegateITest extends BaseCleanStartedEachTest {
MParticleUserDelegate mUserDelegate;

@Before
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

import android.os.Handler;
import android.os.Looper;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.mparticle.TypedUserAttributeListener;
import com.mparticle.UserAttributeListener;
import com.mparticle.identity.UserAttributeListenerWrapper;
import com.mparticle.testutils.AndroidUtils.Mutable;
import com.mparticle.testutils.BaseCleanInstallEachTest;
import com.mparticle.testutils.MPLatch;
Expand Down Expand Up @@ -109,7 +113,7 @@ public void testGetUserAttributesAsync() throws InterruptedException {
final Mutable<Thread> dbAccessThread = new Mutable<Thread>(null);
final MParticleDBManager manager = new MParticleDBManager(){
@Override
public TreeMap<String, String> getUserAttributeSingles(long mpId) {
public Map<String, Object> getUserAttributeSingles(long mpId) {
dbAccessThread.value = Thread.currentThread();
return null;
}
Expand All @@ -126,13 +130,14 @@ public TreeMap<String, List<String>> getUserAttributeLists(long mpId) {
//when not on the main thread, it should callback on the current thread, and access the DB on the same thread
assertNotEquals("main", Thread.currentThread().getName());

manager.getUserAttributes(new UserAttributeListener() {
TypedUserAttributeListener listener = new TypedUserAttributeListener() {
@Override
public void onUserAttributesReceived(@Nullable Map<String, String> userAttributes, @Nullable Map<String, List<String>> userAttributeLists, @Nullable Long mpid) {
public void onUserAttributesReceived(@NonNull Map<String, ?> userAttributes, @NonNull Map<String, ? extends List<String>> userAttributeLists, long mpid) {
callbackThread.value = Thread.currentThread();
latch.value.countDown();
}
}, 1);
};
manager.getUserAttributes(new UserAttributeListenerWrapper(listener), 1);

assertNotNull(callbackThread.value);
assertEquals(Thread.currentThread().getName(), callbackThread.value.getName());
Expand All @@ -143,16 +148,17 @@ public void onUserAttributesReceived(@Nullable Map<String, String> userAttribute
latch.value = new MPLatch(1);

//when run from the main thread, it should be called back on the main thread, but NOT access the DB on the same thread
TypedUserAttributeListener listener1 = new TypedUserAttributeListener() {
@Override
public void onUserAttributesReceived(@NonNull Map<String, ?> userAttributes, @NonNull Map<String, ? extends List<String>> userAttributeLists, long mpid) {
callbackThread.value = Thread.currentThread();
latch.value.countDown();
}
};
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
manager.getUserAttributes(new UserAttributeListener() {
@Override
public void onUserAttributesReceived(@Nullable Map<String, String> userAttributes, @Nullable Map<String, List<String>> userAttributeLists, @Nullable Long mpid) {
callbackThread.value = Thread.currentThread();
latch.value.countDown();
}
}, 1);
manager.getUserAttributes(new UserAttributeListenerWrapper(listener), 1);
}
});
latch.value.await();
Expand Down
187 changes: 187 additions & 0 deletions android-core/src/androidTest/kotlin/com.mparticle/MPUserTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package com.mparticle

import com.mparticle.internal.AccessUtils
import com.mparticle.testutils.BaseCleanStartedEachTest
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue

class MPUserTest : BaseCleanStartedEachTest() {

@Test
fun testGetAttributeSyncWithAndroidHack() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { userAttributes.isEmpty() }
setUserAttribute("foo", "bar")

android_test_hack()

assertEquals(1, userAttributes.size)
assertEquals("bar", userAttributes["foo"])
}
}

@Test
fun testGetAttributeAsync() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { getUserAttributes().isEmpty() }
setUserAttribute("foo", "bar")
setUserAttribute("fooInt", 123)
setUserAttribute("fooLong", 12345L)
setUserAttribute("fooDouble", 10.15)
setUserAttribute("fooNegInt", -10L)
setUserAttribute("fooNegLong", -1010L)
android_test_hack()

getUserAttributes(object : UserAttributeListener {
override fun onUserAttributesReceived(
userAttributes: Map<String, String?>?,
userAttributeLists: Map<String, List<String?>>?,
mpid: Long?
) {
assertNotNull(userAttributes)
assertEquals(6, userAttributes.size)
assertEquals("bar", userAttributes["foo"])
assertEquals("123", userAttributes["fooInt"])
assertEquals("12345", userAttributes["fooLong"])
assertEquals("10.15", userAttributes["fooDouble"])
assertEquals("-10", userAttributes["fooNegInt"])
assertEquals("-1010", userAttributes["fooNegLong"])
}
})

getUserAttributes(object : TypedUserAttributeListener {
override fun onUserAttributesReceived(
userAttributes: Map<String, Any?>,
userAttributeLists: Map<String, List<String?>?>,
mpid: Long
) {
assertEquals(6, userAttributes.size)
assertEquals("bar", userAttributes["foo"])
assertEquals(123L, userAttributes["fooInt"])
assertEquals(12345L, userAttributes["fooLong"])
assertEquals(10.15, userAttributes["fooDouble"])
assertEquals(-10L, userAttributes["fooNegInt"])
assertEquals(-1010L, userAttributes["fooNegLong"])
}
})
}
}

@Test
fun testIncrementIntegerAttribute() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { getUserAttributes().isEmpty() }
setUserAttribute("foo", 1)

android_test_hack()
assertEquals(1, userAttributes.size)
incrementUserAttribute("foo", 3)

android_test_hack()
assertEquals(4L, userAttributes["foo"])

// test negative increment
incrementUserAttribute("foo", -2)
android_test_hack()
assertEquals(2L, userAttributes["foo"])

// test remove incremented attribute
removeUserAttribute("foo")
android_test_hack()
assertEquals(0, userAttributes.size)
}
}

@Test
fun testIncrementDoubleAttribute() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { getUserAttributes().isEmpty() }
android_test_hack()

setUserAttribute("foo", 1.5)

android_test_hack()
assertEquals(1, userAttributes.size)
incrementUserAttribute("foo", 3.2)

android_test_hack()
assertEquals(4.7, userAttributes["foo"])

android_test_hack()

// test negative increment
incrementUserAttribute("foo", -2.1)
android_test_hack()
assertEquals(2.6, userAttributes["foo"])

// test remove incremented attribute
removeUserAttribute("foo")
android_test_hack()
assertEquals(0, userAttributes.size)
}
}

@Test
fun testIncrementLongAttribute() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { getUserAttributes().isEmpty() }
setUserAttribute("foo", 10L)

android_test_hack()
assertEquals(1, userAttributes.size)
assertEquals(10L, userAttributes["foo"])
incrementUserAttribute("foo", 37L)

android_test_hack()
assertEquals(47L, userAttributes["foo"])

// test negative increment
incrementUserAttribute("foo", -21L)
android_test_hack()
assertEquals(26L, userAttributes["foo"])

// test remove incremented attribute
removeUserAttribute("foo")
android_test_hack()
assertEquals(0, userAttributes.size)
}
}

@Test
fun testRemoveUserAttribute() {
MParticle.getInstance()!!.Identity().currentUser!!.apply {
assertTrue { userAttributes.isEmpty() }
setUserAttribute("foo", "bar")
removeUserAttribute("foo")

android_test_hack()
assertEquals(0, userAttributes.size)

setUserAttribute("foo", "bar")
setUserAttribute("fuzz", "baz")

android_test_hack()
assertEquals(2, userAttributes.size)
assertEquals("bar", userAttributes["foo"])
assertEquals("baz", userAttributes["fuzz"])

// remove just 1
removeUserAttribute("fuzz")
android_test_hack()
assertEquals(1, userAttributes?.size)
assertEquals("bar", userAttributes["foo"])

// remove last
removeUserAttribute("foo")
android_test_hack()
assertEquals(0, userAttributes?.size)
}
}

private fun android_test_hack() {
// force sync attribute writes to complete for Android
AccessUtils.awaitMessageHandler()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import java.util.List;
import java.util.Map;

public interface UserAttributeListener {
/**
* @deprecated Use TypedUserAttributeListener instead
*/
@Deprecated
public interface UserAttributeListener extends UserAttributeListenerType {
void onUserAttributesReceived(@Nullable Map<String, String> userAttributes, @Nullable Map<String, List<String>> userAttributeLists, @Nullable Long mpid);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import androidx.annotation.WorkerThread;

import com.mparticle.MParticle;
import com.mparticle.UserAttributeListener;
import com.mparticle.UserAttributeListenerType;
import com.mparticle.consent.ConsentState;

import java.util.Map;
Expand Down Expand Up @@ -37,7 +37,7 @@ public interface MParticleUser {
* @return
*/
@Nullable
Map<String, Object> getUserAttributes(@Nullable final UserAttributeListener listener);
Map<String, Object> getUserAttributes(@Nullable final UserAttributeListenerType listener);

/**
* assign attributes to the User in bulk
Expand Down Expand Up @@ -85,7 +85,7 @@ public interface MParticleUser {
*
* @return whether the attributes were successfully set
*/
boolean incrementUserAttribute(@NonNull String key, int value);
boolean incrementUserAttribute(@NonNull String key, Number value);

/**
* remove an attribute for the user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import android.os.Build;

import com.mparticle.MParticle;
import com.mparticle.UserAttributeListener;
import com.mparticle.UserAttributeListenerType;
import com.mparticle.consent.ConsentState;
import com.mparticle.internal.AppStateManager;
import com.mparticle.internal.ConfigManager;
Expand All @@ -21,7 +21,10 @@
import org.json.JSONException;
import org.json.JSONObject;

import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

Expand All @@ -42,8 +45,8 @@ public Map<String, Object> getUserAttributes(long mpId) {
return mMessageManager.getUserAttributes(null, mpId);
}

public Map<String, Object> getUserAttributes(final UserAttributeListener listener, long mpId) {
return mMessageManager.getUserAttributes(listener, mpId);
public Map<String, Object> getUserAttributes(final UserAttributeListenerType listener, long mpId) {
return mMessageManager.getUserAttributes(new UserAttributeListenerWrapper(listener), mpId);
}

public Map<MParticle.IdentityType, String> getUserIdentities(long mpId){
Expand Down Expand Up @@ -202,7 +205,7 @@ public boolean setUserAttributeList(String key, Object value, long userMpId) {
return setUserAttribute(key, value, userMpId);
}

public boolean incrementUserAttribute(String key, int value, long userMpId) {
public boolean incrementUserAttribute(String key, Number value, long userMpId) {
if (key == null) {
Logger.warning("incrementUserAttribute called with a null key. Ignoring...");
return false;
Expand Down
Loading

0 comments on commit 670bdd6

Please sign in to comment.