From 4f2738942eeb66c244e254ff37ac1f0c8b3d88dc Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 10 Nov 2026 18:19:10 +0300 Subject: [PATCH 1/3] feat: add changeServerUrl, changeAppKey, flushQueues and attemptToSendStoredRequests --- CHANGELOG.md | 5 ++ Countly.d.ts | 48 ++++++++++ Countly.js | 90 +++++++++++++++++++ .../sdk/react/CountlyReactNativeImpl.java | 34 +++++++ .../android/sdk/react/CountlyReactNative.java | 36 ++++++++ .../android/sdk/react/CountlyReactNative.java | 36 ++++++++ ios/src/CountlyReactNative.h | 4 + ios/src/CountlyReactNative.m | 39 ++++++++ src/js/NativeCountlyReactNative.ts | 8 ++ 9 files changed, 300 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d40aa01..5b37f5cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ * Added support for objects as campaign data in `recordDirectAttribution` and as values in `recordIndirectAttribution`, in `Countly` and in `CountlyConfig`. * Added the names "DEVELOPMENT", "PRODUCTION" and "ADHOC" as accepted values of `CountlyConfig.setPushTokenType`. * Added `CountlyConfig.setUpdateSessionTimerDelay(delay)` to set how often, in seconds, the SDK sends automatic session updates and the queued events. The default is 60 seconds, and a value that is not a whole number from 1 to 2147483647 is ignored with an error log. +* Added `Countly.changeServerUrl(serverUrl)` to change the server URL after init. +* Added `Countly.changeAppKey(appKey)` to change the app key after init. +* Added `Countly.flushQueues()` to delete the stored requests and events. +* Added `Countly.attemptToSendStoredRequests()` to send the stored requests now. * Mitigated remote config update issues: * Updates without remote config consent, in temporary device ID mode or with SDK networking disabled now complete with an error in iOS, instead of never completing. @@ -82,6 +86,7 @@ * `Countly.setUserData`, `Countly.userDataBulk.setUserProperties`, `Countly.userData.setProperty` and `Countly.userDataBulk.setProperty` now use the iOS SDK's user property API: pending events are sent before the user details, and `null` values and nested objects are skipped with a log line instead of being sent, as on Android. * The iOS SDK's default log level is now debug instead of verbose. +* Updated the underlying Android SDK version to XX.XX.XX * Updated the underlying iOS SDK version to 26.8.0 ## 26.1.0 diff --git a/Countly.d.ts b/Countly.d.ts index 5b6a36e2..a43d9b8c 100644 --- a/Countly.d.ts +++ b/Countly.d.ts @@ -1069,6 +1069,54 @@ declare module "countly-sdk-react-native-bridge" { */ export function removeDifferentAppKeysFromQueue(): string | void; + /** + * Sends the requests to another server from now on, for example when the app moves to another server. + * The requests already in the queue, the immediate requests and the web views opened after the call use the new server too. + * The app key stays, and the URL is kept in memory only: the next init uses the one in the config. + * In Android, an invalid URL, or an "http" URL while certificate pinning is set, is ignored with an error log. After a change, + * the SDK sends the push token and the consent state to the new server and fetches the SDK behavior settings and the remote + * config values from it, as after init. In iOS, the URL is used as given, so pass a valid one, and call 'sendPushToken' and + * 'remoteConfig.update' again when the new server needs them. + * Should be called after init. + * @param {string} serverUrl the new server URL, for example "https://your.server.ly" + * @return {string | void} error message or void + */ + export function changeServerUrl(serverUrl: string): string | void; + + /** + * Reports to another app from now on, for example when a white label app learns at login which customer it belongs to. + * Before the switch, the running views stop, the recorded events are packed into a request and the running session ends, + * all with the old app key. After it, a session begins for the new app if one was running, and the views start again + * unless 'disableViewRestartForManualRecording' is set without automatic view tracking. + * Requests already in the queue keep the app key they were created with: call 'replaceAllAppKeysInQueueWithCurrentAppKey' + * to send them to the new app, or 'removeDifferentAppKeysFromQueue' to drop them. + * The device ID, consent and server URL stay, and the key is kept in memory only: the next init uses the one in the config. + * In Android, the "YOUR_APP_KEY" placeholder and the current key are ignored, and the new app then gets the push token, + * the consent state, its SDK behavior settings and its remote config values, as after init. In iOS, only an automatic + * session ends and begins again, running APM traces are cancelled, and 'sendPushToken' and 'remoteConfig.update' have to + * be called again when the new app needs them. + * Should be called after init. + * @param {string} appKey the app key of the new app + * @return {string | void} error message or void + */ + export function changeAppKey(appKey: string): string | void; + + /** + * Deletes the stored requests and the recorded events that are not in a request yet. + * Started timed events, running views and pending user profile changes are kept, and a request already on its way is not stopped. + * Should be called after init. + * @return {string | void} error message or void + */ + export function flushQueues(): string | void; + + /** + * Packs the recorded events into a request and starts sending the stored requests now, without waiting for the next timer tick. + * The backoff mechanism and the temporary device ID mode still hold the requests back. + * Should be called after init. + * @return {string | void} error message or void + */ + export function attemptToSendStoredRequests(): string | void; + /** * Call this function when app is loaded, so that the app launch duration can be recorded. * Should be called after init. diff --git a/Countly.js b/Countly.js index 4c345e12..9daa66d2 100644 --- a/Countly.js +++ b/Countly.js @@ -1726,6 +1726,96 @@ Countly.removeDifferentAppKeysFromQueue = function () { CountlyNativeModule.removeDifferentAppKeysFromQueue(); }; +/** + * Sends the requests to another server from now on, for example when the app moves to another server. + * The requests already in the queue, the immediate requests and the web views opened after the call use the new server too. + * The app key stays, and the URL is kept in memory only: the next init uses the one in the config. + * In Android, an invalid URL, or an "http" URL while certificate pinning is set, is ignored with an error log. After a change, + * the SDK sends the push token and the consent state to the new server and fetches the SDK behavior settings and the remote + * config values from it, as after init. In iOS, the URL is used as given, so pass a valid one, and call 'sendPushToken' and + * 'remoteConfig.update' again when the new server needs them. + * Should be called after init. + * @param {string} serverUrl the new server URL, for example "https://your.server.ly" + * @return {string | void} error message or void + */ +Countly.changeServerUrl = function (serverUrl) { + if (!_state.isInitialized) { + const message = "'init' must be called before 'changeServerUrl'"; + L.e(`changeServerUrl, ${message}`); + return message; + } + const message = Validate.String(serverUrl, "serverUrl", "changeServerUrl"); + if (message) { + L.w(`changeServerUrl, ${message}`); + return message; + } + L.i(`changeServerUrl, Changing the server URL to: [${serverUrl}]`); + CountlyNativeModule.changeServerUrl([serverUrl]); +}; + +/** + * Reports to another app from now on, for example when a white label app learns at login which customer it belongs to. + * Before the switch, the running views stop, the recorded events are packed into a request and the running session ends, + * all with the old app key. After it, a session begins for the new app if one was running, and the views start again + * unless 'disableViewRestartForManualRecording' is set without automatic view tracking. + * Requests already in the queue keep the app key they were created with: call 'replaceAllAppKeysInQueueWithCurrentAppKey' + * to send them to the new app, or 'removeDifferentAppKeysFromQueue' to drop them. + * The device ID, consent and server URL stay, and the key is kept in memory only: the next init uses the one in the config. + * In Android, the "YOUR_APP_KEY" placeholder and the current key are ignored, and the new app then gets the push token, + * the consent state, its SDK behavior settings and its remote config values, as after init. In iOS, only an automatic + * session ends and begins again, running APM traces are cancelled, and 'sendPushToken' and 'remoteConfig.update' have to + * be called again when the new app needs them. + * Should be called after init. + * @param {string} appKey the app key of the new app + * @return {string | void} error message or void + */ +Countly.changeAppKey = function (appKey) { + if (!_state.isInitialized) { + const message = "'init' must be called before 'changeAppKey'"; + L.e(`changeAppKey, ${message}`); + return message; + } + const message = Validate.String(appKey, "appKey", "changeAppKey"); + if (message) { + L.w(`changeAppKey, ${message}`); + return message; + } + L.i(`changeAppKey, Changing the app key to: [${appKey}]`); + CountlyNativeModule.changeAppKey([appKey]); +}; + +/** + * Deletes the stored requests and the recorded events that are not in a request yet. + * Started timed events, running views and pending user profile changes are kept, and a request already on its way is not stopped. + * Should be called after init. + * @return {string | void} error message or void + */ +Countly.flushQueues = function () { + if (!_state.isInitialized) { + const message = "'init' must be called before 'flushQueues'"; + L.e(`flushQueues, ${message}`); + return message; + } + L.i("flushQueues, Deleting the stored requests and the recorded events"); + CountlyNativeModule.flushQueues(); +}; + +/** + * Packs the recorded events into a request and starts sending the stored requests now, without waiting for the next timer tick. + * The backoff mechanism and the temporary device ID mode still hold the requests back. + * Should be called after init. + * @return {string | void} error message or void + */ +Countly.attemptToSendStoredRequests = function () { + if (!_state.isInitialized) { + const message = "'init' must be called before 'attemptToSendStoredRequests'"; + L.e(`attemptToSendStoredRequests, ${message}`); + return message; + } + L.i("attemptToSendStoredRequests, Sending the recorded events and the stored requests"); + CountlyNativeModule.attemptToSendStoredRequests(); +}; + /** * Call this function when app is loaded, so that the app launch duration can be recorded. * Should be called after init. diff --git a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNativeImpl.java b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNativeImpl.java index db2ab5dd..d54896bd 100644 --- a/android/src/main/java/ly/count/android/sdk/react/CountlyReactNativeImpl.java +++ b/android/src/main/java/ly/count/android/sdk/react/CountlyReactNativeImpl.java @@ -2366,6 +2366,40 @@ public void removeDifferentAppKeysFromQueue() { Countly.sharedInstance().requestQueue().eraseWrongAppKeyRequests(); } + /** + * Sends the requests to another server from now on. The Android SDK validates the URL. + * + * @param args the new server URL at index 0 + */ + public void changeServerUrl(ReadableArray args) { + String serverUrl = args.getString(0); + Countly.sharedInstance().requestQueue().changeServerUrl(serverUrl); + } + + /** + * Reports to another app from now on. The Android SDK validates the key and closes what belongs to the old app first. + * + * @param args the app key of the new app at index 0 + */ + public void changeAppKey(ReadableArray args) { + String appKey = args.getString(0); + Countly.sharedInstance().requestQueue().changeAppKey(appKey); + } + + /** + * Deletes the stored requests and the recorded events that are not in a request yet. + */ + public void flushQueues() { + Countly.sharedInstance().requestQueue().flushQueues(); + } + + /** + * Packs the recorded events into a request and starts sending the stored requests now. + */ + public void attemptToSendStoredRequests() { + Countly.sharedInstance().requestQueue().attemptToSendStoredRequests(); + } + public void setEventSendThreshold(ReadableArray args) { try { diff --git a/android/src/newarch/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/newarch/java/ly/count/android/sdk/react/CountlyReactNative.java index 22f33102..1ddf365f 100644 --- a/android/src/newarch/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/newarch/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -522,6 +522,42 @@ public void removeDifferentAppKeysFromQueue() { this.impl.removeDifferentAppKeysFromQueue(); } + /** + * Sends the requests to another server from now on. + * + * @param args the new server URL at index 0 + */ + @Override + public void changeServerUrl(ReadableArray args) { + this.impl.changeServerUrl(args); + } + + /** + * Reports to another app from now on. + * + * @param args the app key of the new app at index 0 + */ + @Override + public void changeAppKey(ReadableArray args) { + this.impl.changeAppKey(args); + } + + /** + * Deletes the stored requests and the recorded events that are not in a request yet. + */ + @Override + public void flushQueues() { + this.impl.flushQueues(); + } + + /** + * Packs the recorded events into a request and starts sending the stored requests now. + */ + @Override + public void attemptToSendStoredRequests() { + this.impl.attemptToSendStoredRequests(); + } + @Override public void setEventSendThreshold(ReadableArray args) { this.impl.setEventSendThreshold(args); diff --git a/android/src/oldarch/java/ly/count/android/sdk/react/CountlyReactNative.java b/android/src/oldarch/java/ly/count/android/sdk/react/CountlyReactNative.java index d298446f..dfafb9c5 100644 --- a/android/src/oldarch/java/ly/count/android/sdk/react/CountlyReactNative.java +++ b/android/src/oldarch/java/ly/count/android/sdk/react/CountlyReactNative.java @@ -442,6 +442,42 @@ public void removeDifferentAppKeysFromQueue() { this.impl.removeDifferentAppKeysFromQueue(); } + /** + * Sends the requests to another server from now on. + * + * @param args the new server URL at index 0 + */ + @ReactMethod + public void changeServerUrl(ReadableArray args) { + this.impl.changeServerUrl(args); + } + + /** + * Reports to another app from now on. + * + * @param args the app key of the new app at index 0 + */ + @ReactMethod + public void changeAppKey(ReadableArray args) { + this.impl.changeAppKey(args); + } + + /** + * Deletes the stored requests and the recorded events that are not in a request yet. + */ + @ReactMethod + public void flushQueues() { + this.impl.flushQueues(); + } + + /** + * Packs the recorded events into a request and starts sending the stored requests now. + */ + @ReactMethod + public void attemptToSendStoredRequests() { + this.impl.attemptToSendStoredRequests(); + } + @ReactMethod public void setEventSendThreshold(ReadableArray args) { this.impl.setEventSendThreshold(args); diff --git a/ios/src/CountlyReactNative.h b/ios/src/CountlyReactNative.h index 3b68bf4d..2ca421a7 100644 --- a/ios/src/CountlyReactNative.h +++ b/ios/src/CountlyReactNative.h @@ -35,6 +35,10 @@ typedef void (^Result)(id _Nullable result); - (void)presentRating:(NSArray *_Nullable)arguments; - (void)replaceAllAppKeysInQueueWithCurrentAppKey; - (void)removeDifferentAppKeysFromQueue; +- (void)changeServerUrl:(NSArray *_Nullable)arguments; +- (void)changeAppKey:(NSArray *_Nullable)arguments; +- (void)flushQueues; +- (void)attemptToSendStoredRequests; - (void)setEventSendThreshold:(NSArray *_Nullable)arguments; - (void)sendPushToken:(NSArray *_Nullable)arguments; - (void)askForNotificationPermission:(NSArray *_Nullable)arguments; diff --git a/ios/src/CountlyReactNative.m b/ios/src/CountlyReactNative.m index 9681a0e4..002ce05b 100644 --- a/ios/src/CountlyReactNative.m +++ b/ios/src/CountlyReactNative.m @@ -1778,6 +1778,45 @@ - (CountlyFeedbackWidget *)getFeedbackWidget:(NSString *)widgetId { }); } +/** + * Sends the requests to another server from now on, and points the request capture at the host the iOS SDK uses then. + */ +RCT_EXPORT_METHOD(changeServerUrl : (NSArray *)arguments) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString *serverUrl = [arguments objectAtIndex:0]; + [Countly.sharedInstance setNewHost:serverUrl]; + countlyRNCapturedHost = [NSURLComponents componentsWithString:CountlyConnectionManager.sharedInstance.host].host; + }); +} + +/** + * Reports to another app from now on. + */ +RCT_EXPORT_METHOD(changeAppKey : (NSArray *)arguments) { + dispatch_async(dispatch_get_main_queue(), ^{ + NSString *appKey = [arguments objectAtIndex:0]; + [Countly.sharedInstance setNewAppKey:appKey]; + }); +} + +/** + * Deletes the stored requests and the recorded events that are not in a request yet. + */ +RCT_EXPORT_METHOD(flushQueues) { + dispatch_async(dispatch_get_main_queue(), ^{ + [Countly.sharedInstance flushQueues]; + }); +} + +/** + * Packs the recorded events into a request and starts sending the stored requests now. + */ +RCT_EXPORT_METHOD(attemptToSendStoredRequests) { + dispatch_async(dispatch_get_main_queue(), ^{ + [Countly.sharedInstance attemptToSendStoredRequests]; + }); +} + RCT_EXPORT_METHOD(setEventSendThreshold : (NSArray *)arguments) { dispatch_async(dispatch_get_main_queue(), ^{ NSString *size = [arguments objectAtIndex:0]; diff --git a/src/js/NativeCountlyReactNative.ts b/src/js/NativeCountlyReactNative.ts index b40d10af..0cf92547 100644 --- a/src/js/NativeCountlyReactNative.ts +++ b/src/js/NativeCountlyReactNative.ts @@ -89,6 +89,14 @@ export interface Spec extends TurboModule { presentFeedbackWidget(args: Array): Promise; replaceAllAppKeysInQueueWithCurrentAppKey(): void; removeDifferentAppKeysFromQueue(): void; + /** Sends the requests to the server URL in args[0] from now on. */ + changeServerUrl(args: Array): void; + /** Reports to the app whose key is in args[0] from now on. */ + changeAppKey(args: Array): void; + /** Deletes the stored requests and the recorded events that are not in a request yet. */ + flushQueues(): void; + /** Packs the recorded events into a request and starts sending the stored requests now. */ + attemptToSendStoredRequests(): void; setEventSendThreshold(args: Array): void; addCustomNetworkRequestHeaders(args: Array): void; startTrace(args: Array): void; From f0deaed385048684e7462168faf97aaa2b31a900 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 10 Nov 2026 18:19:14 +0300 Subject: [PATCH 2/3] test: cover the server URL, app key and queue calls --- __tests__/countly.bridge.test.js | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/__tests__/countly.bridge.test.js b/__tests__/countly.bridge.test.js index 8f0a802c..2917e75d 100644 --- a/__tests__/countly.bridge.test.js +++ b/__tests__/countly.bridge.test.js @@ -71,6 +71,10 @@ function createNativeModuleMock(overrides = {}) { recordDirectAttribution: jest.fn(), recordIndirectAttribution: jest.fn(), removeDifferentAppKeysFromQueue: jest.fn(), + changeServerUrl: jest.fn(), + changeAppKey: jest.fn(), + flushQueues: jest.fn(), + attemptToSendStoredRequests: jest.fn(), appLoadingFinished: jest.fn(), setCustomMetrics: jest.fn(), enterContentZone: jest.fn(), @@ -548,6 +552,50 @@ test('notification, location, trace, and attribution helpers forward expected pa expect(typeof notificationSubscription.remove).toBe('function'); }); +test('server URL, app key and queue calls guard before init, refuse a missing value and leave the rest to the native SDK', async () => { + const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + const infoSpy = jest.spyOn(console, 'info').mockImplementation(() => {}); + const debugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {}); + const { Countly, CountlyConfig, nativeModule } = loadCountlyBridge({ platform: 'android' }); + const config = new CountlyConfig('https://server.count.ly', 'app-key').setLoggingEnabled(true); + + expect(Countly.changeServerUrl('https://other.count.ly')).toBe("'init' must be called before 'changeServerUrl'"); + expect(Countly.changeAppKey('other-app-key')).toBe("'init' must be called before 'changeAppKey'"); + expect(Countly.flushQueues()).toBe("'init' must be called before 'flushQueues'"); + expect(Countly.attemptToSendStoredRequests()).toBe("'init' must be called before 'attemptToSendStoredRequests'"); + expect(errorSpy).toHaveBeenCalledWith("[CountlyReactNative] changeServerUrl, 'init' must be called before 'changeServerUrl'"); + + await Countly.initWithConfig(config); + + expect(Countly.changeServerUrl('')).toBe('serverUrl should not be null, undefined or empty'); + expect(Countly.changeServerUrl(undefined)).toBe('serverUrl should not be null, undefined or empty'); + expect(Countly.changeServerUrl(42)).toBe("skipping value for 'serverUrl', due to unsupported data type 'number', its data type should be 'string'"); + expect(Countly.changeAppKey(null)).toBe('appKey should not be null, undefined or empty'); + expect(Countly.changeAppKey({ key: 'app-key-2' })).toBe("skipping value for 'appKey', due to unsupported data type 'object', its data type should be 'string'"); + expect(warnSpy).toHaveBeenCalledWith('[CountlyReactNative] changeServerUrl, serverUrl should not be null, undefined or empty'); + expect(warnSpy).toHaveBeenCalledWith('[CountlyReactNative] changeAppKey, appKey should not be null, undefined or empty'); + expect(nativeModule.changeServerUrl).not.toHaveBeenCalled(); + expect(nativeModule.changeAppKey).not.toHaveBeenCalled(); + expect(nativeModule.flushQueues).not.toHaveBeenCalled(); + expect(nativeModule.attemptToSendStoredRequests).not.toHaveBeenCalled(); + + expect(Countly.changeServerUrl('https://other.count.ly///')).toBeUndefined(); + expect(Countly.changeServerUrl('ftp://other.count.ly')).toBeUndefined(); + expect(Countly.changeAppKey('YOUR_APP_KEY')).toBeUndefined(); + expect(Countly.changeAppKey('app-key-2')).toBeUndefined(); + expect(Countly.flushQueues()).toBeUndefined(); + expect(Countly.attemptToSendStoredRequests()).toBeUndefined(); + + expect(nativeModule.changeServerUrl.mock.calls).toEqual([[['https://other.count.ly///']], [['ftp://other.count.ly']]]); + expect(nativeModule.changeAppKey.mock.calls).toEqual([[['YOUR_APP_KEY']], [['app-key-2']]]); + expect(nativeModule.flushQueues).toHaveBeenCalledWith(); + expect(nativeModule.attemptToSendStoredRequests).toHaveBeenCalledWith(); + expect(infoSpy).toHaveBeenCalledWith('[CountlyReactNative] changeAppKey, Changing the app key to: [app-key-2]'); + + [warnSpy, errorSpy, infoSpy, debugSpy].forEach((spy) => spy.mockRestore()); +}); + test('metrics, crash, and custom metric helpers normalize payloads', async () => { const { Countly, CountlyConfig, nativeModule } = loadCountlyBridge(); From 16ccf254af67106f1db127debc1d1661c7c2a472 Mon Sep 17 00:00:00 2001 From: Arif Burak Demiray Date: Tue, 10 Nov 2026 18:19:19 +0300 Subject: [PATCH 3/3] example: add the server URL, app key and queue calls to the Others screen --- example/CountlyRNExample/Others.tsx | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/example/CountlyRNExample/Others.tsx b/example/CountlyRNExample/Others.tsx index 7d286d61..d5bc3eb8 100644 --- a/example/CountlyRNExample/Others.tsx +++ b/example/CountlyRNExample/Others.tsx @@ -3,6 +3,7 @@ import { Platform, ScrollView, StyleSheet, Text, TextInput } from "react-native" import { SafeAreaView } from "react-native-safe-area-context"; import Countly from "countly-sdk-react-native-bridge"; import CountlyButton from "./CountlyButton"; +import { COUNTLY_APP_KEY, COUNTLY_SERVER_URL } from "./Configuration"; class AttributionKey { static IDFA = "idfa"; @@ -33,10 +34,14 @@ const styles = StyleSheet.create({ function OthersScreen() { const [contentId, setContentId] = React.useState("homepage-banner"); const [pushToken, setPushToken] = React.useState("sample-push-token"); + const [serverUrl, setServerUrl] = React.useState(COUNTLY_SERVER_URL); + const [appKey, setAppKey] = React.useState(COUNTLY_APP_KEY); const [status, setStatus] = React.useState("Utility actions will appear here."); const currentContentId = contentId.trim() || "homepage-banner"; const currentPushToken = pushToken.trim() || "sample-push-token"; + const currentServerUrl = serverUrl.trim(); + const currentAppKey = appKey.trim(); const updateStatus = (message: string) => { console.log(message); @@ -112,6 +117,30 @@ function OthersScreen() { updateStatus(result ? String(result) : "Removed queued requests that used a different app key."); }; + /** Sends the requests to the server URL typed in the field from now on. */ + const changeServerUrl = () => { + const result = Countly.changeServerUrl(currentServerUrl); + updateStatus(result ? String(result) : `Asked to send the requests to '${currentServerUrl}' from now on.`); + }; + + /** Reports to the app whose key is typed in the field from now on. */ + const changeAppKey = () => { + const result = Countly.changeAppKey(currentAppKey); + updateStatus(result ? String(result) : `Asked to report to the app key '${currentAppKey}' from now on.`); + }; + + /** Deletes the stored requests and the recorded events. */ + const flushQueues = () => { + const result = Countly.flushQueues(); + updateStatus(result ? String(result) : "Deleted the stored requests and events."); + }; + + /** Packs the recorded events into a request and sends the stored requests now. */ + const attemptToSendStoredRequests = () => { + const result = Countly.attemptToSendStoredRequests(); + updateStatus(result ? String(result) : "Started sending the stored requests."); + }; + const enterContentZone = () => { Countly.content.enterContentZone(); updateStatus("Entered the content zone."); @@ -153,6 +182,8 @@ function OthersScreen() { + + {status} void checkInitialization()} title="Check SDK Initialization" color="#00b5ad" /> @@ -165,6 +196,10 @@ function OthersScreen() { + + + +