Skip to content

Commit 16c6663

Browse files
committed
Merge branch 'staging' of github.com:Countly/countly-sdk-flutter-bridge into staging-np
2 parents f5b17a7 + b96ede0 commit 16c6663

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1507
-246
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
## 25.4.1-np
2+
* Added fullscreen support for feedback widgets.
3+
* Added "disableSDKBehaviorSettingsUpdates()" init config method to disable server config updates.
4+
* Improved request queue handling with a built-in backoff mechanism which is enabled by default.
5+
* Added "disableBackoffMechanism()" init config method to disable backoff behavior.
6+
* Added "attemptToSendStoredRequests()" method for events that needs quick sending.
7+
* Added support for SDK health checks after initialization for iOS.
8+
* Added timezone support for Web.
9+
10+
* Mitigated an issue when an install referrer triggered.
11+
12+
* Updated underlying Android SDK version to 25.4.1
13+
* Updated underlying iOS SDK version to 25.4.2
14+
* Updated underlying Web SDK version to 25.4.1
15+
116
## 25.4.0-np
217
* ! Minor breaking change ! Removed Secure.ANDROID_ID on Android and UIDevice.currentDevice.identifierForVendor on iOS usages in device ID generation. The SDKs now exclusively uses random UUIDs for device ID generation.
318
* ! Minor breaking change ! SDK now has Server Configuration feature and it is enabled by default. Changes made on SDK Manager > SDK Configuration on your server will affect SDK behavior directly.

android/build.gradle

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

4040
dependencies {
41-
implementation 'ly.count.android:sdk:25.4.0'
41+
implementation 'ly.count.android:sdk:25.4.1'
4242
}
Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
11
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
22

33
<uses-permission android:name="android.permission.INTERNET" />
4-
<application>
5-
<receiver
6-
android:name="ly.count.android.sdk.ReferrerReceiver"
7-
android:exported="true">
8-
<intent-filter>
9-
<action android:name="com.android.vending.INSTALL_REFERRER" />
10-
</intent-filter>
11-
</receiver>
12-
</application>
134
</manifest>

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
*/
6363
public class CountlyFlutterPlugin implements MethodCallHandler, FlutterPlugin, ActivityAware, DefaultLifecycleObserver {
6464
private static final String TAG = "CountlyFlutterPlugin";
65-
private final String COUNTLY_FLUTTER_SDK_VERSION_STRING = "25.4.0";
65+
private final String COUNTLY_FLUTTER_SDK_VERSION_STRING = "25.4.1";
6666
private final String COUNTLY_FLUTTER_SDK_NAME = "dart-flutterb-android";
6767
private final String COUNTLY_FLUTTER_SDK_NAME_NO_PUSH = "dart-flutterbnp-android";
6868

@@ -329,7 +329,10 @@ else if ("getID".equals(call.method)) {
329329
Countly.sharedInstance().deviceId().enableTemporaryIdMode();
330330
result.success("enableTemporaryIDMode success");
331331
} // END DEVICE ID METHODS
332-
332+
else if ("attemptToSendStoredRequests".equals(call.method)) {
333+
Countly.sharedInstance().requestQueue().attemptToSendStoredRequests();
334+
result.success("attemptToSendStoredRequests success!");
335+
}
333336
else if ("setHttpPostForced".equals(call.method)) {
334337
boolean isEnabled = args.getBoolean(0);
335338
this.config.setHttpPostForced(isEnabled);
@@ -1382,6 +1385,19 @@ else if ("getRequestQueue".equals(call.method)) {
13821385
} else if ("getEventQueue".equals(call.method)) {
13831386
CountlyStore countlyStore = new CountlyStore(context, new ModuleLog());
13841387
result.success(Arrays.asList(countlyStore.getEvents()));
1388+
} else if ("storeRequest".equals(call.method)) {
1389+
CountlyStore countlyStore = new CountlyStore(context, new ModuleLog());
1390+
countlyStore.addRequest(args.getString(0), true);
1391+
result.success("storeRequest: success");
1392+
} else if ("addDirectRequest".equals(call.method)) {
1393+
JSONObject jsonObject = args.getJSONObject(0);
1394+
Map<String, String> requestMap = new HashMap<>();
1395+
for (Iterator<String> it = jsonObject.keys(); it.hasNext(); ) {
1396+
String key = it.next();
1397+
requestMap.put(key, jsonObject.get(key).toString());
1398+
}
1399+
Countly.sharedInstance().requestQueue().addDirectRequest(requestMap);
1400+
result.success("addDirectRequest: success");
13851401
} else if ("halt".equals(call.method)) {
13861402
Countly.sharedInstance().halt();
13871403
result.success("halt: success");
@@ -1625,6 +1641,10 @@ private void populateConfig(JSONObject _config) throws JSONException {
16251641
this.config.setSDKBehaviorSettings(_config.getString("sdkBehaviorSettings"));
16261642
}
16271643

1644+
if (_config.has("sdkBehaviorSettingsUpdatesDisabled")) {
1645+
this.config.disableSDKBehaviorSettingsUpdates();
1646+
}
1647+
16281648
// APM ------------------------------------------------
16291649
if (_config.has("trackAppStartTime")) {
16301650
this.config.apm.enableAppStartTimeTracking();

example/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
**/ios/.generated/
6060
**/ios/Flutter/App.framework
6161
**/ios/Flutter/Flutter.framework
62+
**/ios/Flutter/Flutter.podspec
6263
**/ios/Flutter/Generated.xcconfig
6364
**/ios/Flutter/app.flx
6465
**/ios/Flutter/app.zip

example/android/app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ flutter {
6969
dependencies {
7070
implementation 'com.google.firebase:firebase-messaging:24.0.3'
7171
constraints {
72-
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
72+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.10") {
7373
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
7474
}
75-
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
75+
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.10") {
7676
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
7777
}
7878
}

example/android/app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
</queries>
55
<application
66
android:label="example"
7+
android:networkSecurityConfig="@xml/network_security_config"
78
android:name="${applicationName}"
89
android:icon="@mipmap/ic_launcher">
910
<activity
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
5+
import '../utils.dart';
6+
7+
void main() {
8+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
9+
testWidgets('BM_000_base', (WidgetTester tester) async {
10+
List<Map<String, List<String>>> requestArray = <Map<String, List<String>>>[];
11+
createServer(requestArray);
12+
// Initialize the SDK
13+
CountlyConfig config = CountlyConfig("http://0.0.0.0:8080", APP_KEY).enableManualSessionHandling().setLoggingEnabled(true);
14+
await Countly.initWithConfig(config);
15+
16+
Countly.instance.sessions.beginSession();
17+
Countly.instance.events.recordEvent("test_event");
18+
19+
// Get request and event queues from native side
20+
List<String> requestList = await getRequestQueue(); // List of strings
21+
List<String> eventList = await getEventQueue(); // List of json objects
22+
// check the queues are not empty
23+
printQueues(requestList, eventList);
24+
expect(requestList.isNotEmpty, true);
25+
expect(eventList.isNotEmpty, true);
26+
Countly.instance.attemptToSendStoredRequests();
27+
28+
// check queues are empty
29+
await Future.delayed(const Duration(seconds: 10));
30+
requestList = await getRequestQueue();
31+
eventList = await getEventQueue();
32+
printQueues(requestList, eventList);
33+
expect(requestList.isEmpty, true);
34+
expect(eventList.isEmpty, true);
35+
});
36+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'dart:io';
2+
3+
import 'package:countly_flutter/countly_flutter.dart';
4+
import 'package:flutter_test/flutter_test.dart';
5+
import 'package:integration_test/integration_test.dart';
6+
7+
import '../utils.dart';
8+
9+
void main() {
10+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
11+
testWidgets('BM_200_timeoutDelay', (WidgetTester tester) async {
12+
List<Map<String, List<String>>> requestArray = <Map<String, List<String>>>[];
13+
createServer(requestArray, delay: 31);
14+
CountlyConfig config = CountlyConfig("http://0.0.0.0:8080", APP_KEY).enableManualSessionHandling().setLoggingEnabled(true);
15+
await Countly.initWithConfig(config);
16+
17+
Countly.instance.sessions.beginSession();
18+
await Future.delayed(const Duration(seconds: 5));
19+
20+
List<String> requestList = await getRequestQueue();
21+
List<String> eventList = await getEventQueue();
22+
expect(requestList.isNotEmpty, true);
23+
expect(requestList.length, 1);
24+
expect(eventList.isNotEmpty, true);
25+
expect(eventList.length, 1);
26+
27+
Countly.instance.attemptToSendStoredRequests();
28+
29+
await Future.delayed(const Duration(seconds: 90));
30+
requestList = await getRequestQueue();
31+
eventList = await getEventQueue();
32+
33+
expect(requestList.length, 2);
34+
expect(eventList.isEmpty, true);
35+
expect(requestList[0], contains("begin_session"));
36+
37+
if (Platform.isAndroid) {
38+
expect(requestArray.length, 5);
39+
expect(requestArray[0]['method'], contains("sc"));
40+
expect(requestArray[1]['begin_session'], ['1']);
41+
expect(requestArray[2]['hc'], isNotNull);
42+
expect(requestArray[3]['begin_session'], ['1']);
43+
expect(requestArray[4]['method'], contains("sc"));
44+
} else if (Platform.isIOS) {
45+
expect(requestArray.length, 4);
46+
int beginSessions = 0, scCount = 0, hcCount = 0;
47+
for (final req in requestArray) {
48+
if (req.containsKey('begin_session')) beginSessions++;
49+
if (req.containsKey('method') && req['method']!.contains('sc')) scCount++;
50+
if (req.containsKey('hc')) hcCount++;
51+
}
52+
expect(beginSessions, equals(2));
53+
expect(scCount, equals(1));
54+
expect(hcCount, equals(1));
55+
}
56+
});
57+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
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+
5+
import '../utils.dart';
6+
7+
Future<void> verifyQueuesAfterDelay({
8+
required int expectedRequestCount,
9+
required List<String> expectedRequestContains,
10+
required int expectedEventCount,
11+
int delaySeconds = 30,
12+
}) async {
13+
await Future.delayed(Duration(seconds: delaySeconds));
14+
15+
final requestList = await getRequestQueue();
16+
final eventList = await getEventQueue();
17+
printQueues(requestList, eventList);
18+
19+
expect(requestList.length, expectedRequestCount);
20+
for (int i = 0; i < expectedRequestContains.length; i++) {
21+
expect(requestList[i], contains(expectedRequestContains[i]));
22+
}
23+
expect(eventList.length, expectedEventCount);
24+
}
25+
26+
void main() {
27+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
28+
29+
testWidgets('BM_201A_backoffDelay', (WidgetTester tester) async {
30+
List<Map<String, List<String>>> requestArray = <Map<String, List<String>>>[];
31+
createServer(requestArray, delay: 11);
32+
33+
// Initialize the SDK
34+
CountlyConfig config = CountlyConfig("http://0.0.0.0:8080", APP_KEY).enableManualSessionHandling().setLoggingEnabled(true);
35+
await Countly.initWithConfig(config); // generates 0.begin_session
36+
37+
// Perform session operations
38+
Countly.instance.sessions.beginSession(); // this should be sent to the server
39+
await Future.delayed(const Duration(seconds: 2));
40+
Countly.instance.sessions.updateSession(); // this should back off
41+
await Future.delayed(const Duration(seconds: 2));
42+
Countly.instance.sessions.endSession(); // this should be still backed off
43+
44+
// Get initial request and event queues from native side
45+
await getRequestQueue();
46+
await getEventQueue();
47+
48+
// begin session sent. backed off, 30 seconds later nothing sent
49+
await verifyQueuesAfterDelay(
50+
expectedRequestCount: 3,
51+
expectedRequestContains: ['session_duration', 'events', 'end_session'],
52+
expectedEventCount: 0,
53+
);
54+
55+
// 30 more seconds later, still nothing sent
56+
await verifyQueuesAfterDelay(
57+
expectedRequestCount: 3,
58+
expectedRequestContains: ['session_duration', 'events', 'end_session'],
59+
expectedEventCount: 0,
60+
);
61+
62+
// session update sent, backed off, 30 seconds later nothing sent
63+
await verifyQueuesAfterDelay(
64+
expectedRequestCount: 2,
65+
expectedRequestContains: ['events', 'end_session'],
66+
expectedEventCount: 0,
67+
);
68+
69+
changeServerDelay(0);
70+
71+
// 60 seconds later, after non delayed responses everything sent
72+
await verifyQueuesAfterDelay(
73+
expectedRequestCount: 0,
74+
expectedRequestContains: [],
75+
expectedEventCount: 0,
76+
delaySeconds: 60,
77+
);
78+
79+
});
80+
}

0 commit comments

Comments
 (0)