Skip to content

Commit 8e0e377

Browse files
Merge branch 'staging' into web_sdk_support
2 parents dbc8091 + 5c19e7c commit 8e0e377

28 files changed

+569
-219
lines changed

CHANGELOG.md

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,55 @@
1-
## XX.XX.XX
2-
Added experimental support for the web platform in the Countly Flutter SDK. Some functionalities are not yet fully supported. Below is the list of limitations for the web platform:
3-
4-
* Hybrid sessions are the default; full manual sessions are not supported.
5-
* Features Not Supported: Push Notifications, APM, and Attribution.
6-
* Countly.setUserLocation and Countly.disableLocation are unavailable.
7-
* In Views, the following view-related functions are not supported:
8-
* startView
9-
* stopViewWithName
10-
* stopViewWithID
11-
* stopAllViews
12-
* pauseViewWithID
13-
* resumeViewWithID
14-
* addSegmentationToViewWithName
15-
* addSegmentationToViewWithID
16-
* updateGlobalViewSegmentation
17-
* setGlobalViewSegmentation
18-
* In Remote Config, the clearAll function is unavailable, and caching functionality is not provided.
19-
* In A/B Testing the following are not supported:
20-
* exitABTestsForKeys
21-
* Variant-level control
22-
* Experiment-level control
23-
* Star Rating and related configuration options are unavailable.
24-
* User properties during initialization are not supported.
25-
* Custom network headers are not supported.
26-
* Dropping old requests is not supported.
27-
* Content zone global callback is not supported.
28-
* Experimental configuration options are not supported.
29-
30-
* Added underlying Web SDK version to 24.11.2
1+
## 25.1.0
2+
* Added experimental support for the web platform in the Countly Flutter SDK. Some functionalities are not yet fully supported. Below is the list of limitations for the web platform:
3+
* Hybrid sessions are the default; full manual sessions are not supported.
4+
* Features Not Supported: Push Notifications, APM, and Attribution.
5+
* Countly.setUserLocation and Countly.disableLocation are unavailable.
6+
* In Views, the following view-related functions are not supported:
7+
* startView
8+
* stopViewWithName
9+
* stopViewWithID
10+
* stopAllViews
11+
* pauseViewWithID
12+
* resumeViewWithID
13+
* addSegmentationToViewWithName
14+
* addSegmentationToViewWithID
15+
* updateGlobalViewSegmentation
16+
* setGlobalViewSegmentation
17+
* In Remote Config, the clearAll function is unavailable, and caching functionality is not provided.
18+
* In A/B Testing the following are not supported:
19+
* exitABTestsForKeys
20+
* Variant-level control
21+
* Experiment-level control
22+
* Star Rating and related configuration options are unavailable.
23+
* User properties during initialization are not supported.
24+
* Custom network headers are not supported.
25+
* Dropping old requests is not supported.
26+
* Content zone global callback is not supported.
27+
* Experimental configuration options are not supported.
28+
29+
* Added 'event' interface for events methods which are
30+
* recordEvent(String key, [Map<String, Object>? segmentation, int? count, int? sum, int? duration])
31+
* startEvent(String key)
32+
* endEvent(String key, [Map<String, Object>? segmentation, int? count, int? sum])
33+
* cancelEvent(String key)
34+
* Added 'cancelEvent' to cancel a timed event, it is accesible through 'events' interface
35+
36+
* Deprecated the following methods from the Countly
37+
* 'recordEvent(options)', instead use 'events.recordEvent(key, [segmentation, count, sum, duration])
38+
* 'startEvent(key)', instead use 'events.startEvent(key)'
39+
* 'endEvent(options)', instead use 'events.endEvent(key, [segmentation, count, sum])'
40+
41+
* Improved content size management for better adaptability across devices.
42+
* Resolved an issue where the action bar overlapped with the content display.
43+
* Added dynamic resizing functionality for the content zone for enhanced responsiveness.
44+
* Introduced a configuration option (setZoneTimerInterval) to customize the content zone timer interval.
45+
46+
* Enhanced CertificateTrustManager to support domain-specific configurations with hostname-aware checkServerTrusted calls (Android).
47+
48+
* Fixed an issue where the build UUID and executable name were missing from crash reports (iOS).
49+
50+
* Updated underlying Android SDK version to 25.1.0
51+
* Updated underlying iOS SDK version to 25.1.0
52+
* Added underlying Web SDK version to 24.11.4
3153

3254
## 24.11.2
3355
* Improved view tracking capabilities in iOS.

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ android {
3838
}
3939

4040
dependencies {
41-
implementation 'ly.count.android:sdk:24.7.7'
41+
implementation 'ly.count.android:sdk:25.1.0'
4242
implementation 'com.google.firebase:firebase-messaging:24.0.3'
4343
}

android/src/main/java/ly/count/dart/countly_flutter/CountlyFlutterPlugin.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ public void run() {
513513
String startEvent = args.getString(0);
514514
Countly.sharedInstance().events().startEvent(startEvent);
515515
result.success("startEvent for: " + startEvent);
516+
} else if ("cancelEvent".equals(call.method)) {
517+
String cancelEvent = args.getString(0);
518+
Countly.sharedInstance().events().cancelEvent(cancelEvent);
519+
result.success("cancelEvent for: " + cancelEvent);
516520
} else if ("endEvent".equals(call.method)) {
517521
String key = args.getString(0);
518522
int count = Integer.parseInt(args.getString(1));
@@ -1759,10 +1763,15 @@ public void callback(String error) {
17591763
if (_config.has("visibilityTracking")) {
17601764
this.config.experimental.enableVisibilityTracking();
17611765
}
1766+
17621767
if (_config.has("previousNameRecording")) {
17631768
this.config.experimental.enablePreviousNameRecording();
17641769
}
17651770

1771+
if (_config.has("zoneTimerInterval")) {
1772+
this.config.content.setZoneTimerInterval(_config.getInt("zoneTimerInterval"));
1773+
}
1774+
17661775
this.config.content.setGlobalContentCallback(new ContentCallback() {
17671776
@Override
17681777
public void onContentCallback(ContentStatus contentStatus, Map<String, Object> contentData) {
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import 'package:countly_flutter/countly_flutter.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:integration_test/integration_test.dart';
4+
import '../utils.dart';
5+
import 'event_utils.dart';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
9+
void main() {
10+
const int MAX_INT = -1 >>> 1;
11+
12+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
13+
testWidgets('Init SDK record some events and check what is in the queues with new interface', (WidgetTester tester) async {
14+
// Initialize the SDK
15+
CountlyConfig config = CountlyConfig(SERVER_URL, APP_KEY).setLoggingEnabled(true);
16+
await Countly.initWithConfig(config);
17+
18+
Map<String, Object> segmentation = {
19+
'nar': 'uto',
20+
'sasuke': MAX_INT,
21+
'sakura': false,
22+
'itachi': double.maxFinite,
23+
'kakaski': double.minPositive,
24+
'hashirama': -MAX_INT,
25+
'tobi': {'obito': 'uchiha', 'zetsu': 'white'},
26+
'stringList': ['value1', 'value2', 'value3'],
27+
'intList': [1, 2, 3],
28+
'doubleList': [1.1, 2.2, 3.3],
29+
'boolList': [true, false, true],
30+
'mixedList': ['value1', 2, 3.3, true],
31+
'mapList': [
32+
{'key1': 'value1', 'key2': 2},
33+
{'key1': 'value2', 'key2': 3},
34+
{'key1': 'value3', 'key2': 4}
35+
],
36+
};
37+
await Countly.instance.events.recordEvent('event');
38+
await Countly.instance.events.recordEvent('event_s', segmentation);
39+
await Countly.instance.events.recordEvent('event_s_c_s', segmentation, 23, 90.1);
40+
await Countly.instance.events.recordEvent('event_s_c_s_d', segmentation, 98, 13.2, 123);
41+
42+
await Countly.instance.events.startEvent('timed_event');
43+
await Future.delayed(const Duration(milliseconds: 250));
44+
await Countly.instance.events.endEvent('timed_event', segmentation);
45+
46+
await Countly.instance.events.startEvent('timed_event_s_c_s');
47+
await Future.delayed(const Duration(milliseconds: 250));
48+
await Countly.instance.events.endEvent('timed_event_s_c_s', segmentation, 45, 65.6);
49+
50+
// Get request and event queues from native side
51+
List<String> requestList = await getRequestQueue(); // List of strings
52+
List<String> eventList = await getEventQueue(); // List of json objects
53+
54+
// Some logs for debugging
55+
print('RQ: $requestList');
56+
print('EQ: $eventList');
57+
print('RQ length: ${requestList.length}');
58+
print('EQ length: ${eventList.length}');
59+
60+
expect(requestList.length, 1);
61+
expect(eventList.length, Platform.isAndroid ? 7 : 6);
62+
63+
// begin session
64+
Map<String, List<String>> queryParams = Uri.parse("?" + requestList[0]).queryParametersAll;
65+
testCommonRequestParams(queryParams);
66+
67+
int n = 0;
68+
Map<String, dynamic> currentEvent;
69+
if (Platform.isAndroid) {
70+
currentEvent = json.decode(eventList[n]);
71+
// 1. orientation event
72+
expect("[CLY]_orientation", currentEvent['key']);
73+
expect(1, currentEvent['count']);
74+
n++;
75+
}
76+
77+
Map<String, Object> expectedSegmentation = {
78+
"sasuke": MAX_INT,
79+
"sakura": false,
80+
"mapList": [],
81+
"intList": [1, 2, 3],
82+
"doubleList": [1.1, 2.2, 3.3],
83+
"boolList": [true, false, true],
84+
"mixedList": ["value1", 2, 3.3, true],
85+
"itachi": double.maxFinite,
86+
"stringList": ["value1", "value2", "value3"],
87+
"kakaski": double.minPositive,
88+
"nar": "uto",
89+
"hashirama": -MAX_INT,
90+
};
91+
92+
// 2. event
93+
currentEvent = json.decode(eventList[n++]);
94+
validateEvent(event: currentEvent, key: 'event');
95+
96+
// 3. event_s
97+
currentEvent = json.decode(eventList[n++]);
98+
validateEvent(event: currentEvent, key: 'event_s', segmentation: expectedSegmentation);
99+
100+
// 4. event_s_c_s
101+
currentEvent = json.decode(eventList[n++]);
102+
validateEvent(event: currentEvent, key: 'event_s_c_s', segmentation: expectedSegmentation, count: 23, sum: 90.1);
103+
104+
// 5. event_s_c_s_d
105+
currentEvent = json.decode(eventList[n++]);
106+
validateEvent(event: currentEvent, key: 'event_s_c_s_d', segmentation: expectedSegmentation, count: 98, sum: 13.2, dur: 123);
107+
108+
// 6. timed_event
109+
currentEvent = json.decode(eventList[n++]);
110+
validateEvent(event: currentEvent, key: 'timed_event', segmentation: expectedSegmentation, isTimed: true);
111+
112+
// 7. timed_event_s_c_s
113+
currentEvent = json.decode(eventList[n++]);
114+
validateEvent(event: currentEvent, key: 'timed_event_s_c_s', segmentation: expectedSegmentation, count: 45, sum: 65.6, isTimed: true);
115+
});
116+
}

example/integration_test/event_tests/event_test.dart

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ void main() {
5252

5353
// 4. event_c_s
5454
currentEvent = json.decode(eventList[n++]);
55-
validateEvent(event: currentEvent, key: event_c_s['key'] as String, count: event_c_s['count'] as int, sum: event_c_s['sum'] as int);
55+
validateEvent(event: currentEvent, key: event_c_s['key'] as String, count: event_c_s['count'] as int, sum: event_c_s['sum'] as double);
5656

5757
// 5. event_c_d
5858
currentEvent = json.decode(eventList[n++]);
@@ -64,35 +64,35 @@ void main() {
6464

6565
// 7. event_c_s_d
6666
currentEvent = json.decode(eventList[n++]);
67-
validateEvent(event: currentEvent, key: event_c_s_d['key'] as String, count: event_c_s_d['count'] as int, sum: event_c_s_d['sum'] as int, dur: event_c_s_d['duration'] as int);
67+
validateEvent(event: currentEvent, key: event_c_s_d['key'] as String, count: event_c_s_d['count'] as int, sum: event_c_s_d['sum'] as double, dur: event_c_s_d['duration'] as int);
6868

6969
// 8. event_c_s_se
7070
currentEvent = json.decode(eventList[n++]);
71-
validateEvent(event: currentEvent, key: event_c_s_se['key'] as String, count: event_c_s_se['count'] as int, sum: event_c_s_se['sum'] as int, segmentation: expectedSegmentation);
71+
validateEvent(event: currentEvent, key: event_c_s_se['key'] as String, count: event_c_s_se['count'] as int, sum: event_c_s_se['sum'] as double, segmentation: expectedSegmentation);
7272

7373
// 9. event_c_d_se
7474
currentEvent = json.decode(eventList[n++]);
7575
validateEvent(event: currentEvent, key: event_c_d_se['key'] as String, count: event_c_d_se['count'] as int, dur: event_c_d_se['duration'] as int, segmentation: expectedSegmentation);
7676

7777
// 10. event_c_s_d_se
7878
currentEvent = json.decode(eventList[n++]);
79-
validateEvent(event: currentEvent, key: event_c_s_d_se['key'] as String, count: event_c_s_d_se['count'] as int, sum: event_c_s_d_se['sum'] as int, dur: event_c_s_d_se['duration'] as int, segmentation: expectedSegmentation);
79+
validateEvent(event: currentEvent, key: event_c_s_d_se['key'] as String, count: event_c_s_d_se['count'] as int, sum: event_c_s_d_se['sum'] as double, dur: event_c_s_d_se['duration'] as int, segmentation: expectedSegmentation);
8080

8181
// 11. event_s
8282
currentEvent = json.decode(eventList[n++]);
83-
validateEvent(event: currentEvent, key: event_s['key'] as String, sum: event_s['sum'] as int);
83+
validateEvent(event: currentEvent, key: event_s['key'] as String, sum: event_s['sum'] as double);
8484

8585
// 12. event_s_d
8686
currentEvent = json.decode(eventList[n++]);
87-
validateEvent(event: currentEvent, key: event_s_d['key'] as String, sum: event_s_d['sum'] as int, dur: event_s_d['duration'] as int);
87+
validateEvent(event: currentEvent, key: event_s_d['key'] as String, sum: event_s_d['sum'] as double, dur: event_s_d['duration'] as int);
8888

8989
// 13. event_s_se
9090
currentEvent = json.decode(eventList[n++]);
91-
validateEvent(event: currentEvent, key: event_s_se['key'] as String, sum: event_s_se['sum'] as int, segmentation: expectedSegmentation);
91+
validateEvent(event: currentEvent, key: event_s_se['key'] as String, sum: event_s_se['sum'] as double, segmentation: expectedSegmentation);
9292

9393
// 14. event_s_d_se
9494
currentEvent = json.decode(eventList[n++]);
95-
validateEvent(event: currentEvent, key: event_s_d_se['key'] as String, sum: event_s_d_se['sum'] as int, dur: event_s_d_se['duration'] as int, segmentation: expectedSegmentation);
95+
validateEvent(event: currentEvent, key: event_s_d_se['key'] as String, sum: event_s_d_se['sum'] as double, dur: event_s_d_se['duration'] as int, segmentation: expectedSegmentation);
9696

9797
// 15. event_d
9898
currentEvent = json.decode(eventList[n++]);
@@ -116,7 +116,7 @@ void main() {
116116

117117
// 20. timed_event_c_s
118118
currentEvent = json.decode(eventList[n++]);
119-
validateEvent(event: currentEvent, key: timed_event_c_s['key'] as String, count: 1, sum: timed_event_c_s['sum'] as int, isTimed: true);
119+
validateEvent(event: currentEvent, key: timed_event_c_s['key'] as String, count: 1, sum: timed_event_c_s['sum'] as double, isTimed: true);
120120

121121
// 21. timed_event_c_d
122122
currentEvent = json.decode(eventList[n++]);
@@ -128,35 +128,35 @@ void main() {
128128

129129
// 23. timed_event_c_s_d
130130
currentEvent = json.decode(eventList[n++]);
131-
validateEvent(event: currentEvent, key: timed_event_c_s_d['key'] as String, count: 1, sum: timed_event_c_s_d['sum'] as int, dur: timed_event_c_s_d['duration'] as int, isTimed: true);
131+
validateEvent(event: currentEvent, key: timed_event_c_s_d['key'] as String, count: 1, sum: timed_event_c_s_d['sum'] as double, dur: timed_event_c_s_d['duration'] as int, isTimed: true);
132132

133133
// 24. timed_event_c_s_se
134134
currentEvent = json.decode(eventList[n++]);
135-
validateEvent(event: currentEvent, key: timed_event_c_s_se['key'] as String, count: 1, sum: timed_event_c_s_se['sum'] as int, segmentation: expectedSegmentation, isTimed: true);
135+
validateEvent(event: currentEvent, key: timed_event_c_s_se['key'] as String, count: 1, sum: timed_event_c_s_se['sum'] as double, segmentation: expectedSegmentation, isTimed: true);
136136

137137
// 25. timed_event_c_d_se
138138
currentEvent = json.decode(eventList[n++]);
139139
validateEvent(event: currentEvent, key: timed_event_c_d_se['key'] as String, count: 1, dur: timed_event_c_d_se['duration'] as int, segmentation: expectedSegmentation, isTimed: true);
140140

141141
// 26. timed_event_c_s_d_se
142142
currentEvent = json.decode(eventList[n++]);
143-
validateEvent(event: currentEvent, key: timed_event_c_s_d_se['key'] as String, count: 1, sum: timed_event_c_s_d_se['sum'] as int, dur: timed_event_c_s_d_se['duration'] as int, segmentation: expectedSegmentation, isTimed: true);
143+
validateEvent(event: currentEvent, key: timed_event_c_s_d_se['key'] as String, count: 1, sum: timed_event_c_s_d_se['sum'] as double, dur: timed_event_c_s_d_se['duration'] as int, segmentation: expectedSegmentation, isTimed: true);
144144

145145
// 27. timed_event_s
146146
currentEvent = json.decode(eventList[n++]);
147-
validateEvent(event: currentEvent, key: timed_event_s['key'] as String, sum: timed_event_s['sum'] as int, isTimed: true);
147+
validateEvent(event: currentEvent, key: timed_event_s['key'] as String, sum: timed_event_s['sum'] as double, isTimed: true);
148148

149149
// 28. timed_event_s_d
150150
currentEvent = json.decode(eventList[n++]);
151-
validateEvent(event: currentEvent, key: timed_event_s_d['key'] as String, sum: timed_event_s_d['sum'] as int, dur: timed_event_s_d['duration'] as int, isTimed: true);
151+
validateEvent(event: currentEvent, key: timed_event_s_d['key'] as String, sum: timed_event_s_d['sum'] as double, dur: timed_event_s_d['duration'] as int, isTimed: true);
152152

153153
// 29. timed_event_s_se
154154
currentEvent = json.decode(eventList[n++]);
155-
validateEvent(event: currentEvent, key: timed_event_s_se['key'] as String, sum: timed_event_s_se['sum'] as int, segmentation: expectedSegmentation, isTimed: true);
155+
validateEvent(event: currentEvent, key: timed_event_s_se['key'] as String, sum: timed_event_s_se['sum'] as double, segmentation: expectedSegmentation, isTimed: true);
156156

157157
// 30. timed_event_s_d_se
158158
currentEvent = json.decode(eventList[n++]);
159-
validateEvent(event: currentEvent, key: timed_event_s_d_se['key'] as String, sum: timed_event_s_d_se['sum'] as int, dur: timed_event_s_d_se['duration'] as int, segmentation: expectedSegmentation, isTimed: true);
159+
validateEvent(event: currentEvent, key: timed_event_s_d_se['key'] as String, sum: timed_event_s_d_se['sum'] as double, dur: timed_event_s_d_se['duration'] as int, segmentation: expectedSegmentation, isTimed: true);
160160

161161
// 31. timed_event_d
162162
currentEvent = json.decode(eventList[n++]);

0 commit comments

Comments
 (0)