diff --git a/CHANGELOG.md b/CHANGELOG.md index d62e1e8d..f806ba4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 2021-08-04 - v5.7.0 + +#### Improvements + +* Add the ability to randomly target a percentage of customers. + # 2021-07-27 - v5.6.4 #### Fixes diff --git a/README.md b/README.md index 66b6dbde..06f726f9 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ use your app, to talk to them at the right time, and in the right way. ##### [Release Notes](https://learn.apptentive.com/knowledge-base/android-sdk-release-notes/) -##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|5.6.4|aar) +##### Binary releases are hosted for Maven [here](http://search.maven.org/#artifactdetails|com.apptentive|apptentive-android|5.7.0|aar) #### Reporting Bugs diff --git a/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProviderTest.java b/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProviderTest.java index 412ef3d6..a9ca0f96 100644 --- a/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProviderTest.java +++ b/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProviderTest.java @@ -10,18 +10,18 @@ import static org.junit.Assert.*; public class DefaultRandomPercentProviderTest { -// private final Context context = InstrumentationRegistry.getInstrumentation().getContext(); -// -// @Before -// public void before() { -// DefaultRandomPercentProvider.clear(context); -// } -// -// @Test -// public void testRandomPercent() { -// final RandomPercentProvider provider = new DefaultRandomPercentProvider(context, "id"); -// final double percent1 = provider.getPercent("key"); -// final double percent2 = provider.getPercent("key"); -// assertEquals(percent1, percent2, 0.0000001f); -// } + private final Context context = InstrumentationRegistry.getInstrumentation().getContext(); + + @Before + public void before() { + DefaultRandomPercentProvider.clear(context); + } + + @Test + public void testRandomPercent() { + final RandomPercentProvider provider = new DefaultRandomPercentProvider(context, "id"); + final double percent1 = provider.getPercent("key"); + final double percent2 = provider.getPercent("key"); + assertEquals(percent1, percent2, 0.0000001f); + } } \ No newline at end of file diff --git a/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/FieldManagerTest.java b/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/FieldManagerTest.java index ee0dc30b..9da603d1 100644 --- a/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/FieldManagerTest.java +++ b/apptentive/src/androidTest/java/com/apptentive/android/sdk/module/engagement/logic/FieldManagerTest.java @@ -17,54 +17,54 @@ import static org.junit.Assert.assertEquals; public class FieldManagerTest { -// private final Context context = InstrumentationRegistry.getInstrumentation().getContext(); -// -// @Test -// public void testRandomPercentWithKey() { -// final FieldManager fieldManager = createFieldManager(50); -// final Comparable expected = new BigDecimal(50); -// final Comparable actual = fieldManager.getValue("random/abc123xyz/percent"); -// assertEquals(expected, actual); -// } -// -// @Test -// public void testRandomPercentWithoutKey() { -// final FieldManager fieldManager = createFieldManager(50); -// final Comparable expected = new BigDecimal(50); -// final Comparable actual = fieldManager.getValue("random/percent"); -// assertEquals(expected, actual); -// } -// -// @Test -// public void testRandomPercentWithKeyDescription() { -// final FieldManager fieldManager = createFieldManager(50); -// final String expected = "random percent for key 'abc123xyz'"; -// final String actual = fieldManager.getDescription("random/abc123xyz/percent"); -// assertEquals(expected, actual); -// } -// -// @Test -// public void testRandomPercentWithoutKeyDescription() { -// final FieldManager fieldManager = createFieldManager(50); -// final String expected = "random percent"; -// final String actual = fieldManager.getDescription("random/percent"); -// assertEquals(expected, actual); -// } -// -// private FieldManager createFieldManager(double percent) { -// return new FieldManager(context, new VersionHistory(), new EventData(), new Person(), new Device(), new AppRelease(), new MockRandomPercentProvider(percent)); -// } -// -// private static class MockRandomPercentProvider implements RandomPercentProvider { -// private final double percent; -// -// private MockRandomPercentProvider(double percent) { -// this.percent = percent; -// } -// -// @Override -// public double getPercent(String key) { -// return percent; -// } -// } + private final Context context = InstrumentationRegistry.getInstrumentation().getContext(); + + @Test + public void testRandomPercentWithKey() { + final FieldManager fieldManager = createFieldManager(50); + final Comparable expected = new BigDecimal(50); + final Comparable actual = fieldManager.getValue("random/abc123xyz/percent"); + assertEquals(expected, actual); + } + + @Test + public void testRandomPercentWithoutKey() { + final FieldManager fieldManager = createFieldManager(50); + final Comparable expected = new BigDecimal(50); + final Comparable actual = fieldManager.getValue("random/percent"); + assertEquals(expected, actual); + } + + @Test + public void testRandomPercentWithKeyDescription() { + final FieldManager fieldManager = createFieldManager(50); + final String expected = "random percent for key 'abc123xyz'"; + final String actual = fieldManager.getDescription("random/abc123xyz/percent"); + assertEquals(expected, actual); + } + + @Test + public void testRandomPercentWithoutKeyDescription() { + final FieldManager fieldManager = createFieldManager(50); + final String expected = "random percent"; + final String actual = fieldManager.getDescription("random/percent"); + assertEquals(expected, actual); + } + + private FieldManager createFieldManager(double percent) { + return new FieldManager(context, new VersionHistory(), new EventData(), new Person(), new Device(), new AppRelease(), new MockRandomPercentProvider(percent)); + } + + private static class MockRandomPercentProvider implements RandomPercentProvider { + private final double percent; + + private MockRandomPercentProvider(double percent) { + this.percent = percent; + } + + @Override + public double getPercent(String key) { + return percent; + } + } } \ No newline at end of file diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationProxy.java b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationProxy.java index fd019f83..2252be24 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationProxy.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/conversation/ConversationProxy.java @@ -202,9 +202,9 @@ public String getConversationToken() { return conversation.getConversationToken(); } -// public String getLocalIdentifier() { -// return conversation.getLocalIdentifier(); -// } + public String getLocalIdentifier() { + return conversation.getLocalIdentifier(); + } //endregion } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/NoteFragment.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/NoteFragment.java index 2244b6ca..d16355bb 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/NoteFragment.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/fragment/NoteFragment.java @@ -129,9 +129,9 @@ public void onClick(View view) { LaunchInteractionAction launchInteractionButton = (LaunchInteractionAction) buttonAction; List invocations = launchInteractionButton.getInvocations(); String interactionIdToLaunch = null; - //final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(getContext(), getConversation().getLocalIdentifier()); + final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(getContext(), getConversation().getLocalIdentifier()); for (Invocation invocation : invocations) { - FieldManager fieldManager = new FieldManager(getContext(), getConversation().getVersionHistory(), getConversation().getEventData(), getConversation().getPerson(), getConversation().getDevice(), getConversation().getAppRelease()/*,percentProvider*/); + FieldManager fieldManager = new FieldManager(getContext(), getConversation().getVersionHistory(), getConversation().getEventData(), getConversation().getPerson(), getConversation().getDevice(), getConversation().getAppRelease(), percentProvider); if (invocation.isCriteriaMet(fieldManager, false)) { // TODO: should we print details here as well? interactionIdToLaunch = invocation.getInteractionId(); break; diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/model/Targets.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/model/Targets.java index cd5cfee4..08fae102 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/model/Targets.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/interaction/model/Targets.java @@ -43,8 +43,8 @@ public String getApplicableInteraction(String eventLabel, boolean verbose) { Invocation invocation = new Invocation(invocationObject.toString()); Conversation conversation = ApptentiveInternal.getInstance().getConversation(); final Context context = ApptentiveInternal.getInstance().getApplicationContext(); - //final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(context, conversation.getLocalIdentifier()); - FieldManager fieldManager = new FieldManager(context, conversation.getVersionHistory(), conversation.getEventData(), conversation.getPerson(), conversation.getDevice(), conversation.getAppRelease()/*,percentProvider*/); + final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(context, conversation.getLocalIdentifier()); + FieldManager fieldManager = new FieldManager(context, conversation.getVersionHistory(), conversation.getEventData(), conversation.getPerson(), conversation.getDevice(), conversation.getAppRelease(), percentProvider); if (invocation.isCriteriaMet(fieldManager, verbose)) { return invocation.getInteractionId(); diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProvider.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProvider.java index a767885c..ab8e7b55 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProvider.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/DefaultRandomPercentProvider.java @@ -15,55 +15,55 @@ * give key once and stores it in shared preferences. */ public class DefaultRandomPercentProvider implements RandomPercentProvider { -// private final Context context; -// private final String id; -// -// /** -// * @param id - unique key for making a distinction between same keys used in different conversations. -// */ -// public DefaultRandomPercentProvider(Context context, String id) { -// if (context == null) { -// throw new IllegalArgumentException("Context is null"); -// } -// if (id == null) { -// throw new IllegalArgumentException("Id is null"); -// } -// this.context = context.getApplicationContext(); -// this.id = id; -// } -// -// @Override -// public double getPercent(String key) { -// final SharedPreferences prefs = getPrefs(context); -// if (key == null) { -// return getRandomPercent(); -// } else { -// final String prefsKey = id + "_" + key; -// if (prefs.contains(prefsKey)) { -// return prefs.getFloat(prefsKey, 0.0f); -// } -// final float percent = getRandomPercent(); -// prefs.edit().putFloat(prefsKey, percent).apply(); -// return percent; -// } -// } -// -// private float getRandomPercent() { -// ApplicationInfo applicationInfo = RuntimeUtils.getApplicationInfo(context); -// -// if (applicationInfo.isDebuggable()) { -// return (float) 50; -// } else { -// return new Random().nextFloat() * 100; -// } -// } -// -// private static SharedPreferences getPrefs(Context context) { -// return context.getSharedPreferences("com.apptentive.RandomPercentProvider", Context.MODE_PRIVATE); -// } -// -// @VisibleForTesting -// public static void clear(Context context) { -// getPrefs(context).edit().clear().apply(); -// } + private final Context context; + private final String id; + + /** + * @param id - unique key for making a distinction between same keys used in different conversations. + */ + public DefaultRandomPercentProvider(Context context, String id) { + if (context == null) { + throw new IllegalArgumentException("Context is null"); + } + if (id == null) { + throw new IllegalArgumentException("Id is null"); + } + this.context = context.getApplicationContext(); + this.id = id; + } + + @Override + public double getPercent(String key) { + final SharedPreferences prefs = getPrefs(context); + if (key == null) { + return getRandomPercent(); + } else { + final String prefsKey = id + "_" + key; + if (prefs.contains(prefsKey)) { + return prefs.getFloat(prefsKey, 0.0f); + } + final float percent = getRandomPercent(); + prefs.edit().putFloat(prefsKey, percent).apply(); + return percent; + } + } + + private float getRandomPercent() { + ApplicationInfo applicationInfo = RuntimeUtils.getApplicationInfo(context); + + if (applicationInfo.isDebuggable()) { + return (float) 50; + } else { + return new Random().nextFloat() * 100; + } + } + + private static SharedPreferences getPrefs(Context context) { + return context.getSharedPreferences("com.apptentive.RandomPercentProvider", Context.MODE_PRIVATE); + } + + @VisibleForTesting + public static void clear(Context context) { + getPrefs(context).edit().clear().apply(); + } } diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/FieldManager.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/FieldManager.java index c83aae41..00d67118 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/FieldManager.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/FieldManager.java @@ -34,22 +34,22 @@ public class FieldManager { Person person; Device device; AppRelease appRelease; - //private final RandomPercentProvider randomPercentProvider; + private final RandomPercentProvider randomPercentProvider; - public FieldManager(Context context, VersionHistory versionHistory, EventData eventData, Person person, Device device, AppRelease appRelease/*,RandomPercentProvider randomPercentProvider*/) { + public FieldManager(Context context, VersionHistory versionHistory, EventData eventData, Person person, Device device, AppRelease appRelease, RandomPercentProvider randomPercentProvider) { Assert.notNull(context); Assert.notNull(versionHistory); Assert.notNull(eventData); Assert.notNull(person); Assert.notNull(device); - //Assert.notNull(randomPercentProvider); + Assert.notNull(randomPercentProvider); this.context = context; this.versionHistory = versionHistory; this.eventData = eventData; this.person = person; this.device = device; this.appRelease = appRelease; - //this.randomPercentProvider = randomPercentProvider; + this.randomPercentProvider = randomPercentProvider; } public boolean exists(String query) { @@ -289,22 +289,22 @@ private Object doGetValue(String query) { return null; } } -// case random: { -// if (tokens.length == 3) { // random//percent -// final String randomNumberKey = tokens[1]; -// QueryPart subQuery = QueryPart.valueOf(tokens[2]); -// switch (subQuery) { -// case percent: -// return randomPercentProvider.getPercent(randomNumberKey); -// } -// } else if (tokens.length == 2) { // random/percent -// QueryPart subQuery = QueryPart.valueOf(tokens[1]); -// switch (subQuery) { -// case percent: -// return randomPercentProvider.getPercent(null); -// } -// } -// } + case random: { + if (tokens.length == 3) { // random//percent + final String randomNumberKey = tokens[1]; + QueryPart subQuery = QueryPart.valueOf(tokens[2]); + switch (subQuery) { + case percent: + return randomPercentProvider.getPercent(randomNumberKey); + } + } else if (tokens.length == 2) { // random/percent + QueryPart subQuery = QueryPart.valueOf(tokens[1]); + switch (subQuery) { + case percent: + return randomPercentProvider.getPercent(null); + } + } + } default: break; } @@ -506,22 +506,22 @@ public String getDescription(String query) { return null; } } -// case random: { -// if (tokens.length == 3) { // random//percent -// final String randomNumberKey = tokens[1]; -// QueryPart subQuery = QueryPart.valueOf(tokens[2]); -// switch (subQuery) { -// case percent: -// return StringUtils.format("random percent for key '%s'", randomNumberKey); -// } -// } else if (tokens.length == 2) { // random/percent -// QueryPart subQuery = QueryPart.valueOf(tokens[1]); -// switch (subQuery) { -// case percent: -// return StringUtils.format("random percent"); -// } -// } -// } + case random: { + if (tokens.length == 3) { // random//percent + final String randomNumberKey = tokens[1]; + QueryPart subQuery = QueryPart.valueOf(tokens[2]); + switch (subQuery) { + case percent: + return StringUtils.format("random percent for key '%s'", randomNumberKey); + } + } else if (tokens.length == 2) { // random/percent + QueryPart subQuery = QueryPart.valueOf(tokens[1]); + switch (subQuery) { + case percent: + return StringUtils.format("random percent"); + } + } + } default: break; } @@ -577,8 +577,8 @@ private enum QueryPart { build, time_ago, -// random, -// percent, + random, + percent, other; diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/RandomPercentProvider.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/RandomPercentProvider.java index 3107a751..79e641bd 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/RandomPercentProvider.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/logic/RandomPercentProvider.java @@ -4,8 +4,8 @@ * Represents an object which returns a random percent for a give key. */ public interface RandomPercentProvider { -// /** -// * Returns a random percent for a give key in range from [0..100] -// */ -// double getPercent(String key); + /** + * Returns a random percent for a give key in range from [0..100] + */ + double getPercent(String key); } \ No newline at end of file diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/notification/NoteInteractionNotificationAdapter.java b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/notification/NoteInteractionNotificationAdapter.java index 6ef11946..a3cd5653 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/notification/NoteInteractionNotificationAdapter.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/module/engagement/notification/NoteInteractionNotificationAdapter.java @@ -207,9 +207,9 @@ protected boolean execute(Conversation conversation) { String interactionIdToLaunch = null; // Need to check each Invocation object's criteria to find the right one. - //final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(context, conversation.getLocalIdentifier()); + final RandomPercentProvider percentProvider = new DefaultRandomPercentProvider(context, conversation.getLocalIdentifier()); for (Invocation invocation : invocations) { - FieldManager fieldManager = new FieldManager(context, conversation.getVersionHistory(), conversation.getEventData(), conversation.getPerson(), conversation.getDevice(), conversation.getAppRelease()/*,percentProvider*/); + FieldManager fieldManager = new FieldManager(context, conversation.getVersionHistory(), conversation.getEventData(), conversation.getPerson(), conversation.getDevice(), conversation.getAppRelease(), percentProvider); if (invocation.isCriteriaMet(fieldManager, true)) { interactionIdToLaunch = invocation.getInteractionId(); ApptentiveLog.v(NOTIFICATION_INTERACTIONS, "Found an Interaction to launch with id %s", interactionIdToLaunch); diff --git a/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java b/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java index ee3e0591..e8a00751 100644 --- a/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java +++ b/apptentive/src/main/java/com/apptentive/android/sdk/util/Constants.java @@ -9,7 +9,7 @@ public class Constants { public static final int API_VERSION = 10; - private static final String APPTENTIVE_SDK_VERSION = "5.6.4"; + private static final String APPTENTIVE_SDK_VERSION = "5.7.0"; public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 45000; public static final int DEFAULT_READ_TIMEOUT_MILLIS = 45000;