diff --git a/CHANGELOG.md b/CHANGELOG.md index dc2909b20..7569dafe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ ## 25.4.4 * Improved disk size calculation in crash reports. -* Added "setRequestTimeoutDuration(requestTimeoutDuration)" init config method configure request timeout in seconds. + +* Added a new function "sendMetricsRequest(metricsOverride)" to send a device metrics request, accessible through the requestQueue interface. +* Added a new Consent option "metrics" for controlling "sendMetricsRequest" method. (This has no effect on Session metrics.) +* Added "setRequestTimeoutDuration(requestTimeoutDuration)" init config method to change request timeout duration in seconds. * Mitigated an issue displaying Content on API level 35 and above. diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/DeviceInfoTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/DeviceInfoTests.java index e0dc47f69..15f3c8366 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/DeviceInfoTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/DeviceInfoTests.java @@ -74,7 +74,7 @@ public void testGetDevice() { @Test public void testGetResolution() { final DisplayMetrics metrics = new DisplayMetrics(); - ((WindowManager) TestUtils.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(metrics); + ((WindowManager) TestUtils.getContext().getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRealMetrics(metrics); final String expected = metrics.widthPixels + "x" + metrics.heightPixels; assertEquals(expected, regularDeviceInfo.mp.getResolution(TestUtils.getContext())); } diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java index f87647231..625d43649 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConfigurationTests.java @@ -212,7 +212,7 @@ public void consentEnabled_allFeatures() throws JSONException, InterruptedExcept Assert.assertEquals(2, TestUtils.getCurrentRQ().length); Assert.assertEquals(0, countlyStore.getEventQueueSize()); - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 0, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 0, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); TestUtils.validateRequest(TestUtils.commonDeviceId, TestUtils.map("location", ""), 1); flow_allFeatures(); @@ -959,7 +959,7 @@ public void scenario_consentRequiredDisabled() throws JSONException { Countly.sharedInstance().attribution().recordDirectAttribution("_special_test", "_special_test"); Assert.assertEquals(3, TestUtils.getCurrentRQ().length); // changes nothing because no consent for attribution - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 1, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 1, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); TestUtils.validateRequest(TestUtils.commonDeviceId, TestUtils.map("location", ""), 2); } diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConsentTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConsentTests.java index 52f45fc6c..7a20e22d6 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConsentTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleConsentTests.java @@ -32,6 +32,7 @@ public class ModuleConsentTests { Countly.CountlyFeatureNames.clicks, Countly.CountlyFeatureNames.scrolls, Countly.CountlyFeatureNames.content, + Countly.CountlyFeatureNames.metrics, }; @Before @@ -253,12 +254,12 @@ public void initTimeSetConsentRQ_4() throws JSONException { protected static void validateConsentRequest(String deviceId, int idx, boolean[] consents) { Map consentsMap = TestUtils.map("sessions", consents[0], "crashes", consents[1], "users", consents[2], "push", consents[3], "feedback", consents[4], "scrolls", consents[5], "remote-config", consents[6], "attribution", consents[7], "clicks", consents[8], "location", consents[9], "star-rating", - consents[10], "events", consents[11], "views", consents[12], "apm", consents[13], "content", consents[14]); + consents[10], "events", consents[11], "views", consents[12], "apm", consents[13], "content", consents[14], "metrics", consents[15]); TestUtils.validateRequest(deviceId, TestUtils.map("consent", consentsMap), idx); } protected static void validateAllConsentRequest(String deviceId, int idx) { - validateConsentRequest(deviceId, idx, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, true, true, true }); + validateConsentRequest(deviceId, idx, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true }); } // TODO test that makes sure that the consent change request is created correctly diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleViewsTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleViewsTests.java index a7a184262..cd9158f66 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/ModuleViewsTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/ModuleViewsTests.java @@ -1342,7 +1342,7 @@ public void clearFirstViewFlagSessionConsentRemoved() throws JSONException { mCountly.views().startView("a", null); // 0 is consent request - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 0, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, true, false, false }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 0, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false }); TestUtils.validateRequest(TestUtils.commonDeviceId, TestUtils.map("location", ""), 1); // start false because session did not start validateView("a", 0.0, 2, 3, false, true, null, vals[0], ""); @@ -1351,12 +1351,12 @@ public void clearFirstViewFlagSessionConsentRemoved() throws JSONException { mCountly.consent().giveConsent(new String[] { Countly.CountlyFeatureNames.sessions }); mCountly.views().startView("b", null); - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 3, new boolean[] { true, false, false, false, false, false, false, false, false, false, false, false, true, false, false }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 3, new boolean[] { true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false }); validateView("b", 0.0, 4, 5, false, true, null, vals[1], vals[0]); //internal flag should be reset whens session consent is removed mCountly.consent().removeConsent(new String[] { Countly.CountlyFeatureNames.sessions }); - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, true, false, false }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false }); mCountly.views().startView("c", null); // start false because session did not start @@ -1941,7 +1941,7 @@ public void startView_consentRemoval() throws JSONException { validateView("test", 0.0, 4, 6, false, false, TestUtils.map(), "_CLY_", "_CLY_", null); validateView("test2", 0.0, 3, 6, false, false, TestUtils.map(), "_CLY_", "_CLY_", null); } - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, true }); countly.consent().giveConsent(new String[] { Countly.CountlyFeatureNames.views }); ModuleConsentTests.validateAllConsentRequest(TestUtils.commonDeviceId, 6); @@ -1975,7 +1975,7 @@ public void startAutoStoppedView_consentRemoval() throws JSONException { countly.consent().removeConsent(new String[] { Countly.CountlyFeatureNames.views }); validateView("test2", 0.0, 4, 6, false, false, TestUtils.map(), "_CLY_", "_CLY_", null); - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 5, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, true }); countly.consent().giveConsent(new String[] { Countly.CountlyFeatureNames.views }); ModuleConsentTests.validateAllConsentRequest(TestUtils.commonDeviceId, 6); @@ -2054,7 +2054,7 @@ public void autoViewTracking_consentRemoval() throws JSONException { countly.consent().removeConsent(new String[] { Countly.CountlyFeatureNames.views }); validateView(activity2.getClass().getName(), 0.0, 6, 8, false, false, TestUtils.map(), "_CLY_", "_CLY_", null); - ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 7, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true }); + ModuleConsentTests.validateConsentRequest(TestUtils.commonDeviceId, 7, new boolean[] { true, true, true, true, true, true, true, true, true, true, true, true, false, true, true, true }); countly.consent().giveConsent(new String[] { Countly.CountlyFeatureNames.views }); diff --git a/sdk/src/androidTest/java/ly/count/android/sdk/scSE_SessionsTests.java b/sdk/src/androidTest/java/ly/count/android/sdk/scSE_SessionsTests.java index f0b07ccec..604b9f1ef 100644 --- a/sdk/src/androidTest/java/ly/count/android/sdk/scSE_SessionsTests.java +++ b/sdk/src/androidTest/java/ly/count/android/sdk/scSE_SessionsTests.java @@ -392,7 +392,7 @@ protected static Map validateSessionUpdateRequest(int idx, Integ } private void validateSessionConsentRequest(int idx, boolean consentForSession, String deviceId) { - ModuleConsentTests.validateConsentRequest(deviceId, idx, new boolean[] { consentForSession, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); + ModuleConsentTests.validateConsentRequest(deviceId, idx, new boolean[] { consentForSession, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false }); } private void validateRequest(Map expectedExtras, int idx) { diff --git a/sdk/src/main/java/ly/count/android/sdk/ConnectionQueue.java b/sdk/src/main/java/ly/count/android/sdk/ConnectionQueue.java index d147e6363..246c7c172 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ConnectionQueue.java +++ b/sdk/src/main/java/ly/count/android/sdk/ConnectionQueue.java @@ -539,6 +539,17 @@ public void sendDirectRequest(@NonNull final Map requestData) { tick(); } + @Override + public void sendMetricsRequest(@NonNull String preparedMetrics) { + if (!checkInternalState()) { + return; + } + + L.d("[ConnectionQueue] sendMetricsRequest"); + addRequestToQueue(prepareCommonRequestData() + preparedMetrics, false); + tick(); + } + /** * Records the specified events and sends them to the server. * diff --git a/sdk/src/main/java/ly/count/android/sdk/Countly.java b/sdk/src/main/java/ly/count/android/sdk/Countly.java index 9d233f82f..fbcb9b64b 100644 --- a/sdk/src/main/java/ly/count/android/sdk/Countly.java +++ b/sdk/src/main/java/ly/count/android/sdk/Countly.java @@ -227,6 +227,7 @@ public static class CountlyFeatureNames { public static final String feedback = "feedback"; public static final String remoteConfig = "remote-config"; public static final String content = "content"; + public static final String metrics = "metrics"; //public static final String accessoryDevices = "accessory-devices"; } diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleConsent.java b/sdk/src/main/java/ly/count/android/sdk/ModuleConsent.java index c6793c253..a4477d57e 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleConsent.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleConsent.java @@ -30,6 +30,7 @@ public class ModuleConsent extends ModuleBase implements ConsentProvider { Countly.CountlyFeatureNames.clicks, Countly.CountlyFeatureNames.scrolls, Countly.CountlyFeatureNames.content, + Countly.CountlyFeatureNames.metrics }; public enum ConsentChangeSource {ChangeConsentCall, DeviceIDChangedNotMerged} diff --git a/sdk/src/main/java/ly/count/android/sdk/ModuleRequestQueue.java b/sdk/src/main/java/ly/count/android/sdk/ModuleRequestQueue.java index 76292fc4e..abd1bb4c9 100644 --- a/sdk/src/main/java/ly/count/android/sdk/ModuleRequestQueue.java +++ b/sdk/src/main/java/ly/count/android/sdk/ModuleRequestQueue.java @@ -7,6 +7,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class ModuleRequestQueue extends ModuleBase implements BaseInfoProvider { RequestQueue requestQueueInterface; @@ -296,6 +297,16 @@ synchronized public void addDirectRequestInternal(@NonNull Map r } } + private void recordMetricsInternal(@NonNull Map metricsOverride) { + if (!consentProvider.getConsent(Countly.CountlyFeatureNames.metrics)) { + L.d("[ModuleRequestQueue] recordMetricsInternal, no consent given for metrics"); + return; + } + + String preparedMetrics = deviceInfo.getMetrics(_cly.context_, metricsOverride, L); + requestQueueProvider.sendMetricsRequest(preparedMetrics); + } + void esWriteCachesToPersistenceInternal(@Nullable ExplicitStorageCallback callback) { L.i("[ModuleRequestQueue] Calling esWriteCachesToPersistenceInternal"); storageProvider.esWriteCacheToStorage(callback); @@ -428,5 +439,22 @@ public void esWriteCachesToPersistence(@Nullable ExplicitStorageCallback callbac esWriteCachesToPersistenceInternal(callback); } } + + /** + * Record device metrics manually as a standalone call + * + * @param metricsOverride map of key value pairs to override the default metrics + */ + public void recordMetrics(@Nullable Map metricsOverride) { + synchronized (_cly) { + L.i("[RequestQueue] recordMetrics, Calling recordMetrics"); + Map tempMetricsOverride = metricsOverride; + if (tempMetricsOverride == null) { + tempMetricsOverride = new ConcurrentHashMap<>(); + } + + recordMetricsInternal(tempMetricsOverride); + } + } } } diff --git a/sdk/src/main/java/ly/count/android/sdk/RequestQueueProvider.java b/sdk/src/main/java/ly/count/android/sdk/RequestQueueProvider.java index 50cebbee9..19fa17936 100644 --- a/sdk/src/main/java/ly/count/android/sdk/RequestQueueProvider.java +++ b/sdk/src/main/java/ly/count/android/sdk/RequestQueueProvider.java @@ -41,6 +41,8 @@ interface RequestQueueProvider { void sendDirectRequest(@NonNull final Map requestData); + void sendMetricsRequest(@NonNull String preparedMetrics); + void enrollToKeys(@NonNull String[] keys); void exitForKeys(@NonNull String[] keys);