@@ -37,6 +37,7 @@ Countly::Countly() : max_events(COUNTLY_MAX_EVENTS_DEFAULT), wait_milliseconds(C
3737 began_session = false ;
3838 always_use_post = false ;
3939 is_being_disposed = false ;
40+ is_sdk_initialized = false ;
4041 remote_config_enabled = false ;
4142
4243 // Petting to null values
@@ -123,48 +124,125 @@ void Countly::setUserDetails(const std::map<std::string, std::string>& value) {
123124 mutex.lock ();
124125 session_params[" user_details" ] = value;
125126
126- if (began_session) {
127- std::map<std::string, std::string> data = {
128- {" app_key" , session_params[" app_key" ].dump ()},
129- {" device_id" , session_params[" device_id" ].dump ()},
127+ if (!is_sdk_initialized) {
128+ log (Countly::LogLevel::ERROR, " [Countly][setUserDetails] Can not send user detail if the SDK has not been initialized." );
129+ mutex.unlock ();
130+ return ;
131+ }
132+
133+ std::map<std::string, std::string> data = {
134+ {" app_key" , session_params[" app_key" ].get <std::string>()},
135+ {" device_id" , session_params[" device_id" ].get <std::string>()},
130136 {" user_details" , session_params[" user_details" ].dump ()}
131- };
137+ };
132138
133- sendHTTP (" /i" , Countly::serializeForm (data));
134- }
139+ sendHTTP (" /i" , Countly::serializeForm (data));
135140 mutex.unlock ();
136141}
137142
138143void Countly::setCustomUserDetails (const std::map<std::string, std::string>& value) {
139144 mutex.lock ();
140145 session_params[" user_details" ][" custom" ] = value;
141146
142- if (began_session) {
143- std::map<std::string, std::string> data = {
144- {" app_key" , session_params[" app_key" ].dump ()},
145- {" device_id" , session_params[" device_id" ].dump ()},
147+ if (!is_sdk_initialized) {
148+ log (Countly::LogLevel::ERROR, " [Countly][setCustomUserDetails] Can not send user detail if the SDK has not been initialized." );
149+ mutex.unlock ();
150+ return ;
151+ }
152+
153+ std::map<std::string, std::string> data = {
154+ {" app_key" , session_params[" app_key" ].get <std::string>()},
155+ {" device_id" , session_params[" device_id" ].get <std::string>()},
146156 {" user_details" , session_params[" user_details" ].dump ()}
147- };
157+ };
148158
149- sendHTTP (" /i" , Countly::serializeForm (data));
150- }
159+ sendHTTP (" /i" , Countly::serializeForm (data));
151160 mutex.unlock ();
152161}
153162
163+ #pragma region User location
164+
154165void Countly::setCountry (const std::string& country_code) {
155- session_params[" country_code" ] = country_code;
166+ log (Countly::LogLevel::WARNING, " [Countly][setCountry] 'setCountry' is deprecated, please use 'setLocation(countryCode, city, gpsCoordinates, ipAddress)' method instead." );
167+ setLocation (country_code, " " , " " , " " );
156168}
157169
158170void Countly::setCity (const std::string& city_name) {
159- session_params[" city" ] = city_name;
171+ log (Countly::LogLevel::WARNING, " [Countly][setCity] 'setCity' is deprecated, please use 'setLocation(countryCode, city, gpsCoordinates, ipAddress)' method instead." );
172+ setLocation (" " , city_name, " " , " " );
160173}
161174
162175void Countly::setLocation (double lattitude, double longitude) {
176+ log (Countly::LogLevel::WARNING, " [Countly][setLocation] 'setLocation(latitude, longitude)' is deprecated, please use 'setLocation(countryCode, city, gpsCoordinates, ipAddress)' method instead." );
177+
163178 std::ostringstream location_stream;
164179 location_stream << lattitude << ' ,' << longitude;
165- session_params[" location" ] = location_stream.str ();
180+ setLocation (" " , " " , location_stream.str (), " " );
181+ }
182+
183+ void Countly::setLocation (const std::string& countryCode, const std::string& city, const std::string& gpsCoordinates, const std::string& ipAddress) {
184+ mutex.lock ();
185+ log (Countly::LogLevel::INFO, " [Countly][setLocation] SetLocation : countryCode = " + countryCode + " , city = " + city + " , gpsCoordinates = " + gpsCoordinates + " , ipAddress = " + ipAddress);
186+
187+
188+ if ((!countryCode.empty () && city.empty ())
189+ || (!city.empty () && countryCode.empty ())) {
190+ log (Countly::LogLevel::WARNING, " [Countly][setLocation] In \" SetLocation\" both country code and city should be set together" );
191+ }
192+
193+ session_params[" city" ] = city;
194+ session_params[" ip_address" ] = ipAddress;
195+ session_params[" location" ] = gpsCoordinates;
196+ session_params[" country_code" ] = countryCode;
197+
198+ mutex.unlock ();
199+
200+ if (is_sdk_initialized) {
201+ _sendIndependantLocationRequest ();
202+ }
166203}
167204
205+ void Countly::_sendIndependantLocationRequest () {
206+ mutex.lock ();
207+ log (Countly::LogLevel::DEBUG, " [Countly] [_sendIndependantLocationRequest]" );
208+
209+ /*
210+ * Empty country code, city and IP address can not be sent.
211+ */
212+
213+ std::map<std::string, std::string> data;
214+
215+ if (session_params.contains (" city" ) && session_params[" city" ].is_string () && !session_params[" city" ].get <std::string>().empty ()) {
216+ data[" city" ] = session_params[" city" ].get <std::string>();
217+ }
218+
219+ if (session_params.contains (" location" ) && session_params[" location" ].is_string () && !session_params[" location" ].get <std::string>().empty ()) {
220+ data[" location" ] = session_params[" location" ].get <std::string>();
221+ }
222+
223+ if (session_params.contains (" country_code" ) && session_params[" country_code" ].is_string () && !session_params[" country_code" ].get <std::string>().empty ()) {
224+ data[" country_code" ] = session_params[" country_code" ].get <std::string>();
225+ }
226+
227+ if (session_params.contains (" ip_address" ) && session_params[" ip_address" ].is_string () && !session_params[" ip_address" ].get <std::string>().empty ()) {
228+ data[" ip_address" ] = session_params[" ip_address" ].get <std::string>();
229+ }
230+
231+ const std::chrono::system_clock::time_point now = Countly::getTimestamp ();
232+ const auto timestamp = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch ());
233+
234+ if (!data.empty ()) {
235+ data[" app_key" ] = session_params[" app_key" ].get <std::string>();
236+ data[" device_id" ] = session_params[" device_id" ].get <std::string>();
237+ data[" timestamp" ] = std::to_string (timestamp.count ());
238+ sendHTTP (" /i" , Countly::serializeForm (data));
239+ }
240+
241+ mutex.unlock ();
242+ }
243+
244+ #pragma endregion User location
245+
168246#pragma region Device Id
169247void Countly::setDeviceID (const std::string& value, bool same_user) {
170248 mutex.lock ();
@@ -184,8 +262,12 @@ void Countly::setDeviceID(const std::string& value, bool same_user) {
184262 return ;
185263 }
186264
187- // If code does reach here without sdk init, it will throw an exception.
188265 mutex.unlock ();
266+ if (!is_sdk_initialized) {
267+ log (Countly::LogLevel::ERROR, " [Countly][setDeviceID] Can not change the device id if the SDK has not been initialized." );
268+ return ;
269+ }
270+
189271 if (same_user) {
190272 _changeDeviceIdWithMerge (value);
191273 }
@@ -260,6 +342,8 @@ void Countly::start(const std::string& app_key, const std::string& host, int por
260342 this ->port = port;
261343 }
262344
345+ is_sdk_initialized = true ; // after this point SDK is initialized.
346+
263347 if (!running) {
264348
265349 mutex.unlock ();
@@ -455,18 +539,38 @@ bool Countly::beginSession() {
455539 {" sdk_name" , COUNTLY_SDK_NAME},
456540 {" sdk_version" , COUNTLY_API_VERSION},
457541 {" timestamp" , std::to_string (timestamp.count ())},
542+ {" app_key" , session_params[" app_key" ].get <std::string>()},
543+ {" device_id" , session_params[" device_id" ].get <std::string>()},
458544 {" begin_session" , " 1" }
459545 };
460546
461- for (auto & element : session_params.items ()) {
462- if (element.value ().is_string ()) {
463- data[element.key ()] = element.value ().get <std::string>();
464- } else {
465- data[element.key ()] = element.value ().dump ();
466- }
547+ if (session_params.contains (" city" ) && session_params[" city" ].is_string () && !session_params[" city" ].get <std::string>().empty ()) {
548+ data[" city" ] = session_params[" city" ].get <std::string>();
549+ }
550+
551+ if (session_params.contains (" location" ) && session_params[" location" ].is_string () && !session_params[" location" ].get <std::string>().empty ()) {
552+ data[" location" ] = session_params[" location" ].get <std::string>();
553+ }
554+
555+ if (session_params.contains (" country_code" ) && session_params[" country_code" ].is_string () && !session_params[" country_code" ].get <std::string>().empty ()) {
556+ data[" country_code" ] = session_params[" country_code" ].get <std::string>();
557+ }
558+
559+ if (session_params.contains (" ip_address" ) && session_params[" ip_address" ].is_string () && !session_params[" ip_address" ].get <std::string>().empty ()) {
560+ data[" ip_address" ] = session_params[" ip_address" ].get <std::string>();
561+ }
562+
563+ if (session_params.contains (" user_details" )) {
564+ data[" user_details" ] = session_params[" user_details" ].dump ();
565+ session_params.erase (" user_details" );
566+ }
567+
568+ if (session_params.contains (" metrics" )) {
569+ data[" metrics" ] = session_params[" metrics" ].dump ();
467570 }
468571
469572 if (sendHTTP (" /i" , Countly::serializeForm (data)).success ) {
573+ session_params.erase (" user_details" );
470574 last_sent_session_request = Countly::getTimestamp ();
471575 began_session = true ;
472576 }
0 commit comments