From 57925e3b02c9bb2f565150c94e5a39ddf8511783 Mon Sep 17 00:00:00 2001 From: Zohaib Kambrani Date: Thu, 24 Dec 2020 18:01:32 +0300 Subject: [PATCH] Geofence fixes by JobScheduler --- android/app/src/main/AndroidManifest.xml | 3 + .../diplomaticquarterapp/MainActivity.kt | 13 +- .../geofence/GeoZoneModel.kt | 6 +- .../geofence/HMG_Geofence.kt | 74 ++++++--- .../GeofenceBroadcastReceiver.kt | 22 ++- ...GeofenceBroadcastReceiverWithJobService.kt | 3 + .../intent_receivers/GeofenceErrorMessages.kt | 4 +- .../GeofenceJobIntentService.kt | 29 ---- .../GeofenceTransitionsJobIntentService.kt | 32 +++- .../GeofencingRebootBroadcastReceiver.kt | 6 +- .../ReregisterGeofenceJobService.kt | 23 +++ .../diplomaticquarterapp/utils/HMGUtils.kt | 84 +++++++--- .../cloud/diplomaticquarterapp/utils/Logs.kt | 146 ++++++++++++++++++ ios/Flutter/.last_build_id | 2 +- ios/Runner.xcodeproj/project.pbxproj | 4 + ios/Runner/AppDelegate.swift | 39 ++++- ios/Runner/Helper/API.swift | 7 +- ios/Runner/Helper/FlutterConstants.swift | 36 +++++ ios/Runner/Helper/GlobalHelper.swift | 2 +- ios/Runner/Helper/HMGPlatformBridge.swift | 3 + lib/config/config.dart | 6 +- 21 files changed, 442 insertions(+), 102 deletions(-) delete mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceJobIntentService.kt create mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/ReregisterGeofenceJobService.kt create mode 100644 android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt create mode 100644 ios/Runner/Helper/FlutterConstants.swift diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 649af435..79bc3dc4 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -81,6 +81,7 @@ + @@ -88,6 +89,8 @@ + + diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt index 8b73b0c7..e67f646f 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/MainActivity.kt @@ -2,8 +2,7 @@ package com.cloud.diplomaticquarterapp import android.os.Bundle import android.util.Log import androidx.annotation.NonNull; -import com.cloud.diplomaticquarterapp.utils.FlutterText -import com.cloud.diplomaticquarterapp.utils.PlatformBridge +import com.cloud.diplomaticquarterapp.utils.* import io.flutter.embedding.android.FlutterFragmentActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel @@ -15,6 +14,16 @@ class MainActivity: FlutterFragmentActivity() { // Create Flutter Platform Bridge PlatformBridge(flutterEngine.dartExecutor.binaryMessenger, this).create() + val time = timeToMillis("04:00:00", "HH:mm:ss") + print(time) + +// val d1 = Logs.list(this) +// val d2 = Logs.raw(this) +// val d3 = Logs.RegisterGeofence.list(this) +// val d4 = Logs.RegisterGeofence.raw(this) +// val d5 = Logs.GeofenceEvent.list(this) +// val d6 = Logs.GeofenceEvent.raw(this) + print("") } override fun onResume() { diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/GeoZoneModel.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/GeoZoneModel.kt index 328014e6..b3fb4f56 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/GeoZoneModel.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/GeoZoneModel.kt @@ -37,7 +37,7 @@ class GeoZoneModel { val rad = Radius.toFloat() if(lat != null && long != null){ - val loiteringDelayMinutes:Int = 5 // in Minutes + val loiteringDelayMinutes:Int = 2 // in Minutes return Geofence.Builder() .setRequestId(identifier()) .setCircularRegion( @@ -46,8 +46,8 @@ class GeoZoneModel { rad ) .setTransitionTypes(GeofenceTransition.ENTER_EXIT.value) -// .setNotificationResponsiveness(0) -// .setLoiteringDelay(loiteringDelayMinutes * 60 * 1000) + .setNotificationResponsiveness(0) + .setLoiteringDelay(loiteringDelayMinutes * 60 * 1000) .setExpirationDuration(Geofence.NEVER_EXPIRE) .build() } diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/HMG_Geofence.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/HMG_Geofence.kt index 919d478f..ba6691f7 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/HMG_Geofence.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/HMG_Geofence.kt @@ -9,6 +9,7 @@ import android.content.pm.PackageManager import android.location.Location import androidx.core.content.ContextCompat import com.cloud.diplomaticquarterapp.geofence.intent_receivers.GeofenceBroadcastReceiver +import com.cloud.diplomaticquarterapp.geofence.intent_receivers.ReregisterGeofenceJobService import com.cloud.diplomaticquarterapp.utils.* import com.google.android.gms.location.Geofence import com.google.android.gms.location.GeofencingClient @@ -20,8 +21,10 @@ import com.google.gson.reflect.TypeToken enum class GeofenceTransition(val value: Int) { ENTER(1), EXIT(2), + DWELL(4), + ENTER_EXIT((ENTER.value or EXIT.value)), - DWELL(4); + DWELL_EXIT((DWELL.value or EXIT.value)); companion object { fun fromInt(value: Int) = GeofenceTransition.values().first { it.value == value } @@ -30,8 +33,9 @@ enum class GeofenceTransition(val value: Int) { fun named():String{ if (value == 1)return "Enter" if (value == 2)return "Exit" - if (value == (ENTER.value or EXIT.value))return "Enter or Exit" if (value == 4)return "dWell" + if (value == (ENTER.value or EXIT.value))return "Enter or Exit" + if (value == (DWELL.value or EXIT.value))return "DWell or Exit" return "unknown" } } @@ -73,13 +77,22 @@ class HMG_Geofence { } } + fun limitize(zones: List):List{ + var geoZones_ = zones + if(zones.size > 100) + geoZones_ = zones.subList(0, 99) + return geoZones_ + } + fun register(geoZones: List){ if (geoZones.isEmpty()) return + var geoZones_ = limitize(geoZones) + fun buildGeofencingRequest(geofences: List): GeofencingRequest { return GeofencingRequest.Builder() - .setInitialTrigger(0) + .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_DWELL) .addGeofences(geofences) .build() } @@ -87,9 +100,9 @@ class HMG_Geofence { getActiveGeofences({ active -> val geofences = mutableListOf() - geoZones.forEach { - it.toGeofence()?.let { geof -> - if(!active.contains(geof.requestId)){ // if not already registered then register + geoZones_.forEach { + it.toGeofence()?.let { geof -> + if (!active.contains(geof.requestId)) { // if not already registered then register geofences.add(geof) } } @@ -99,19 +112,25 @@ class HMG_Geofence { geofencingClient .addGeofences(buildGeofencingRequest(geofences), geofencePendingIntent) .addOnSuccessListener { + Logs.RegisterGeofence.save(context,"SUCCESS", "Successfuly registered the geofences", Logs.STATUS.SUCCESS) saveActiveGeofence(geofences.map { it.requestId }, listOf()) } .addOnFailureListener { - print(it.localizedMessage) - saveLog(context,"error:ADD_GEOFENCES", it.localizedMessage) + Logs.RegisterGeofence.save(context,"FAILED_TO_REGISTER", "Failed to register geofence",Logs.STATUS.ERROR) } + + // Schedule the job to register after specified duration (due to: events not calling after long period.. days or days [Needs to register fences again]) + HMGUtils.scheduleJob(context, ReregisterGeofenceJobService::class.java,ReregisterGeofenceJobService.JobID, ReregisterGeofenceJobService.TriggerIntervalMillis) } - },null) + + }, null) + } - fun unRegisterAll(completion: (status: Boolean, exception:Exception?) -> Unit){ + fun unRegisterAll(completion: (status: Boolean, exception: Exception?) -> Unit){ getActiveGeofences({ success -> val mList = success.toMutableList() + removeActiveGeofences() geofencingClient .removeGeofences(success) .addOnSuccessListener { @@ -119,14 +138,20 @@ class HMG_Geofence { } .addOnFailureListener { completion(false, it) - saveLog(context,"error:REMOVE_GEOFENCES", it.localizedMessage) + saveLog(context, "error:REMOVE_GEOFENCES", it.localizedMessage) } - removeActiveGeofences() }, { failed -> // Nothing to do with failed geofences. }) } + fun reRegister(){ + unRegisterAll { status, exception -> + val geoZones = HMGUtils.getGeoZonesFromPreference(context) + register(geoZones) + } + } + fun saveActiveGeofence(success: List, failed: List){ val jsonSuccess = gson.toJson(success) val jsonFailure = gson.toJson(failed) @@ -135,8 +160,8 @@ class HMG_Geofence { } fun removeActiveGeofences(){ - preferences.edit().putString(PREF_KEY_SUCCESS,"[]").apply() - preferences.edit().putString(PREF_KEY_FAILED,"[]").apply() + preferences.edit().putString(PREF_KEY_SUCCESS, "[]").apply() + preferences.edit().putString(PREF_KEY_FAILED, "[]").apply() } fun getActiveGeofences(success: (success: List) -> Unit, failure: ((failed: List) -> Unit)?){ @@ -164,7 +189,7 @@ class HMG_Geofence { profileJson = preferences.getString("flutter.user-profile", null) val type = object : TypeToken?>() {}.type - return gson.fromJson?>(profileJson,type) + return gson.fromJson?>(profileJson, type) ?.get("PatientID") .toString() .toDoubleOrNull() @@ -172,32 +197,35 @@ class HMG_Geofence { } - fun handleEvent(triggerGeofences: List, location: Location, transition:GeofenceTransition) { + fun handleEvent(triggerGeofences: List, location: Location, transition: GeofenceTransition) { getPatientID()?.let { patientId -> getActiveGeofences({ activeGeofences -> triggerGeofences.forEach { geofence -> // Extract PointID from 'geofence.requestId' and find from active geofences - val pointID = activeGeofences.firstOrNull {it == geofence.requestId}?.split('_')?.first() - if(!pointID.isNullOrEmpty() && pointID.toIntOrNull() != null){ + val pointID = activeGeofences.firstOrNull { it == geofence.requestId }?.split('_')?.first() + if (!pointID.isNullOrEmpty() && pointID.toIntOrNull() != null) { - val body = mutableMapOf( + val body = mutableMapOf( "PointsID" to pointID.toIntOrNull(), "GeoType" to transition.value, "PatientID" to patientId ) body.putAll(HMGUtils.defaultHTTPParams(context)) - httpPost>(API.LOG_GEOFENCE, body, { response -> + httpPost>(API.LOG_GEOFENCE, body, { response -> + saveLog(context, "HMG_GEOFENCE_NOTIFY", "Success: Notified to server\uD83D\uDE0E.") sendNotification(context, transition.named(), geofence.requestId, "Notified to server.๐Ÿ˜Ž") - },{ exception -> - sendNotification(context, transition.named(), geofence.requestId, "Failed to notify server.๐Ÿ˜”") + }, { exception -> + val errorMessage = "${transition.named()}, ${geofence.requestId}" + saveLog(context, "HMG_GEOFENCE_NOTIFY", "failed: $errorMessage | error: ${exception.localizedMessage}") + sendNotification(context, transition.named(), geofence.requestId, "Failed to notify server๐Ÿ˜” -> ${exception.localizedMessage}") }) } } - },null) + }, null) } } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiver.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiver.kt index 11fc4850..80ef2c96 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiver.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiver.kt @@ -8,7 +8,9 @@ import android.content.Intent import android.util.Log import com.cloud.diplomaticquarterapp.geofence.GeofenceTransition import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence +import com.cloud.diplomaticquarterapp.utils.Logs import com.cloud.diplomaticquarterapp.utils.saveLog +import com.google.android.gms.location.GeofenceStatusCodes import com.google.android.gms.location.GeofencingEvent class GeofenceBroadcastReceiver : BroadcastReceiver() { @@ -19,12 +21,28 @@ class GeofenceBroadcastReceiver : BroadcastReceiver() { if (geofencingEvent.hasError()) { val errorMessage = GeofenceErrorMessages.getErrorString(context, geofencingEvent.errorCode) Log.e(LOG_TAG, errorMessage) - saveLog(context,LOG_TAG,errorMessage) + + Logs.GeofenceEvent.save(context,LOG_TAG,"Error while triggering geofence event",Logs.STATUS.ERROR) + doReRegisterIfRequired(context,geofencingEvent.errorCode) + return } - + + Logs.GeofenceEvent.save(context,LOG_TAG,"Geofence event triggered: ${GeofenceTransition.fromInt(geofencingEvent.geofenceTransition).value} for ${geofencingEvent.triggeringGeofences.map {it.requestId}}",Logs.STATUS.SUCCESS) HMG_Geofence.shared(context).handleEvent(geofencingEvent.triggeringGeofences,geofencingEvent.triggeringLocation, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition)); } + fun doReRegisterIfRequired(context: Context, errorCode: Int){ + val errorRequiredReregister = listOf( + GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE, + GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES, + GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS, + GeofenceStatusCodes.GEOFENCE_REQUEST_TOO_FREQUENT + ) + + if(errorRequiredReregister.contains(errorCode)) + HMG_Geofence.shared(context).reRegister() + + } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiverWithJobService.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiverWithJobService.kt index 250040f6..a9924ca1 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiverWithJobService.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceBroadcastReceiverWithJobService.kt @@ -5,9 +5,12 @@ package com.cloud.diplomaticquarterapp.geofence.intent_receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence +import com.google.android.gms.location.GeofenceStatusCodes class GeofenceBroadcastReceiverWithJobService : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { GeofenceTransitionsJobIntentService.enqueueWork(context, intent) } + } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceErrorMessages.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceErrorMessages.kt index 08d5ba62..01377d49 100755 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceErrorMessages.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceErrorMessages.kt @@ -4,7 +4,7 @@ package com.cloud.diplomaticquarterapp.geofence.intent_receivers import android.content.Context import com.cloud.diplomaticquarterapp.R -import com.cloud.diplomaticquarterapp.utils.saveLog +import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence import com.google.android.gms.common.api.ApiException import com.google.android.gms.location.GeofenceStatusCodes @@ -38,8 +38,6 @@ object GeofenceErrorMessages { else -> resources.getString(R.string.geofence_unknown_error) } - saveLog(context,"GeofenceErrorMessages","$errorCode | $errorMessage") - return errorMessage } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceJobIntentService.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceJobIntentService.kt deleted file mode 100644 index 84d3f230..00000000 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceJobIntentService.kt +++ /dev/null @@ -1,29 +0,0 @@ - - -package com.cloud.diplomaticquarterapp.geofence.intent_receivers - -import android.content.Intent -import android.util.Log -import androidx.core.app.JobIntentService -import com.cloud.diplomaticquarterapp.geofence.GeofenceTransition -import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence -import com.cloud.diplomaticquarterapp.utils.saveLog -import com.google.android.gms.location.GeofencingEvent - -class GeofenceJobIntentService : JobIntentService(){ - - private val LOG_TAG = "GeofenceBroadcastReceiver" - override fun onHandleWork(intent: Intent) { - - val geofencingEvent = GeofencingEvent.fromIntent(intent) - if (geofencingEvent.hasError()) { - val errorMessage = GeofenceErrorMessages.getErrorString(this, geofencingEvent.errorCode) - Log.e(LOG_TAG, errorMessage) - saveLog(this, LOG_TAG,errorMessage) - return - } - - HMG_Geofence.shared(this).handleEvent(geofencingEvent.triggeringGeofences,geofencingEvent.triggeringLocation, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition)) - } - -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceTransitionsJobIntentService.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceTransitionsJobIntentService.kt index 2be216a1..c008c0a5 100755 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceTransitionsJobIntentService.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofenceTransitionsJobIntentService.kt @@ -37,7 +37,9 @@ import android.util.Log import androidx.core.app.JobIntentService import com.cloud.diplomaticquarterapp.geofence.GeofenceTransition import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence +import com.cloud.diplomaticquarterapp.utils.saveLog import com.google.android.gms.location.Geofence +import com.google.android.gms.location.GeofenceStatusCodes import com.google.android.gms.location.GeofencingEvent class GeofenceTransitionsJobIntentService : JobIntentService() { @@ -45,7 +47,7 @@ class GeofenceTransitionsJobIntentService : JobIntentService() { companion object { private const val LOG_TAG = "GeoTrIntentService" - private const val JOB_ID = 573 + private const val JOB_ID = 95902 var context_: Context? = null fun enqueueWork(context: Context, intent: Intent) { context_ = context @@ -59,15 +61,31 @@ class GeofenceTransitionsJobIntentService : JobIntentService() { override fun onHandleWork(intent: Intent) { val geofencingEvent = GeofencingEvent.fromIntent(intent) if (geofencingEvent.hasError()) { - val errorMessage = GeofenceErrorMessages.getErrorString(this, geofencingEvent.errorCode) + val errorMessage = GeofenceErrorMessages.getErrorString(context_!!, geofencingEvent.errorCode) Log.e(LOG_TAG, errorMessage) + + + saveLog(context_!!,LOG_TAG,errorMessage) + doReRegisterIfRequired(context_!!, geofencingEvent.errorCode) + return } - if (geofencingEvent.geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER || geofencingEvent.geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) { - context_?.let { - HMG_Geofence.shared(it).handleEvent(geofencingEvent.triggeringGeofences,geofencingEvent.triggeringLocation, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition)); - } - } + HMG_Geofence.shared(context_!!).handleEvent(geofencingEvent.triggeringGeofences,geofencingEvent.triggeringLocation, GeofenceTransition.fromInt(geofencingEvent.geofenceTransition)); + + } + + + fun doReRegisterIfRequired(context: Context, errorCode: Int){ + val errorRequiredReregister = listOf( + GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE, + GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES, + GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS, + GeofenceStatusCodes.GEOFENCE_REQUEST_TOO_FREQUENT + ) + + if(errorRequiredReregister.contains(errorCode)) + HMG_Geofence.shared(context).reRegister() + } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofencingRebootBroadcastReceiver.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofencingRebootBroadcastReceiver.kt index e1eda3b9..0663db3b 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofencingRebootBroadcastReceiver.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/GeofencingRebootBroadcastReceiver.kt @@ -7,7 +7,6 @@ import android.content.Context import android.content.Intent import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence import com.cloud.diplomaticquarterapp.geofence.PREFS_STORAGE -import com.cloud.diplomaticquarterapp.utils.HMGUtils class GeofencingRebootBroadcastReceiver : BroadcastReceiver(){ override fun onReceive(context: Context, intent: Intent) { @@ -17,10 +16,7 @@ class GeofencingRebootBroadcastReceiver : BroadcastReceiver(){ val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) pref.edit().putString("REBOOT_DETECTED","YES").apply() - HMG_Geofence.shared(context).unRegisterAll { status, exception -> - val geoZones = HMGUtils.getGeoZonesFromPreference(context) - HMG_Geofence.shared(context).register(geoZones) - } + HMG_Geofence.shared(context).reRegister() } } diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/ReregisterGeofenceJobService.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/ReregisterGeofenceJobService.kt new file mode 100644 index 00000000..043c5c96 --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/geofence/intent_receivers/ReregisterGeofenceJobService.kt @@ -0,0 +1,23 @@ +package com.cloud.diplomaticquarterapp.geofence.intent_receivers + +import android.app.job.JobParameters +import android.app.job.JobService +import com.cloud.diplomaticquarterapp.geofence.HMG_Geofence +import com.cloud.diplomaticquarterapp.utils.Logs +import com.cloud.diplomaticquarterapp.utils.timeToMillis + +class ReregisterGeofenceJobService : JobService(){ + companion object{ + val TriggerIntervalMillis:String = "24:00:00" + val JobID = 918273 + } + override fun onStartJob(params: JobParameters?): Boolean { + HMG_Geofence.shared(applicationContext).reRegister() + Logs.save(applicationContext,"ReregisterGeofenceJobService.onStartJob", "triggered to re-register the geofences after $TriggerIntervalMillis >> [HH:mm:ss]") + return true + } + + override fun onStopJob(params: JobParameters?): Boolean { + return true + } +} \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt index 0dc0234c..7880a7ef 100644 --- a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/HMGUtils.kt @@ -3,11 +3,15 @@ package com.cloud.diplomaticquarterapp.utils import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent +import android.app.job.JobInfo +import android.app.job.JobScheduler +import android.content.ComponentName import android.content.Context import android.content.Intent import android.os.Build import android.widget.Toast import androidx.annotation.Nullable +import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.core.app.TaskStackBuilder import com.cloud.diplomaticquarterapp.BuildConfig @@ -17,15 +21,17 @@ import com.cloud.diplomaticquarterapp.geofence.GeoZoneModel import com.cloud.diplomaticquarterapp.geofence.PREFS_STORAGE import com.cloud.diplomaticquarterapp.geofence.PREF_KEY_HMG_ZONES import com.cloud.diplomaticquarterapp.geofence.PREF_KEY_LANGUAGE +import com.cloud.diplomaticquarterapp.geofence.intent_receivers.ReregisterGeofenceJobService import com.github.kittinunf.fuel.core.extensions.jsonBody import com.github.kittinunf.fuel.httpPost -import com.google.android.gms.location.Geofence import com.google.gson.Gson import com.google.gson.reflect.TypeToken import io.flutter.plugin.common.MethodChannel +import org.jetbrains.anko.doAsyncResult import org.json.JSONArray import org.json.JSONException import org.json.JSONObject +import java.text.SimpleDateFormat import java.util.* import kotlin.concurrent.timerTask @@ -71,7 +77,7 @@ class HMGUtils { fun getGeoZonesFromPreference(context: Context):List{ val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) - val json = pref.getString(PREF_KEY_HMG_ZONES,"[]") + val json = pref.getString(PREF_KEY_HMG_ZONES, "[]") val geoZones = GeoZoneModel().listFrom(json) return geoZones @@ -79,14 +85,14 @@ class HMGUtils { fun getLanguageCode(context: Context) : Int{ val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) - val lang = pref.getString(PREF_KEY_LANGUAGE,"ar") + val lang = pref.getString(PREF_KEY_LANGUAGE, "ar") return if(lang == "ar") 2 else 1 } - fun defaultHTTPParams(context: Context) : Map{ + fun defaultHTTPParams(context: Context) : Map{ return mapOf( "ZipCode" to "966", - "VersionID" to 5.6, + "VersionID" to 5.8, "Channel" to 3, "LanguageID" to getLanguageCode(context), "IPAdress" to "10.20.10.20", @@ -97,16 +103,44 @@ class HMGUtils { "DeviceTypeID" to 2) } + + fun scheduleJob(context: Context, pendingIntentClassType:Class, jobId:Int, intervalDuration:String, deadlineMillis:Long = (30 * 1000)) { // default deadline: 30 Seconds + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + val jobScheduler: JobScheduler = context.getSystemService(JobScheduler::class.java) + + val serviceComponent = ComponentName(context, pendingIntentClassType) + val builder = JobInfo.Builder(jobId, serviceComponent) + builder.setPersisted(true) + + val intervalMillis = timeToMillis(intervalDuration,"HH:mm:ss") + builder.setMinimumLatency(intervalMillis) // wait at least + builder.setOverrideDeadline((intervalMillis + deadlineMillis)) // maximum delay + if (jobScheduler.schedule(builder.build()) == JobScheduler.RESULT_SUCCESS){ + Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Job scheduled to trigger after duration $intervalDuration >> HH:mm:ss --('MinimumLatency:$intervalMillis Deadline:${(intervalMillis + deadlineMillis)}')--",Logs.STATUS.SUCCESS) + }else{ + Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job",Logs.STATUS.ERROR) + } + + } else { + Logs.save(context,"ScheduleJob", "${pendingIntentClassType.simpleName}: Failed to scheduled Job on VERSION.SDK_INT < ${android.os.Build.VERSION_CODES.M}",Logs.STATUS.ERROR) + } + } + } } -private fun Timer.schedule(timerTask: TimerTask) { -} private const val NOTIFICATION_CHANNEL_ID = BuildConfig.APPLICATION_ID + ".channel" -fun sendNotification(context: Context, title:String, @Nullable subtitle:String?, message:String?) { + +fun timeToMillis(time:String, format:String):Long{ + val sdf = SimpleDateFormat(format, Locale.US) + val millis = sdf.parse(time).time + TimeZone.getDefault().rawOffset + return millis +} + +fun sendNotification(context: Context, title: String, @Nullable subtitle: String?, message: String?) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O @@ -126,7 +160,7 @@ fun sendNotification(context: Context, title:String, @Nullable subtitle:String?, .addNextIntent(intent) val notificationPendingIntent = stackBuilder.getPendingIntent(getUniqueId(), PendingIntent.FLAG_UPDATE_CURRENT) - val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID + ".geofence") + val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.mipmap.ic_launcher) .setContentIntent(notificationPendingIntent) .setAutoCancel(true) @@ -141,7 +175,15 @@ fun sendNotification(context: Context, title:String, @Nullable subtitle:String?, //------------------------- // Open Helper Methods //------------------------- -private fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt()) +fun getUniqueId() = ((System.currentTimeMillis() % 10000).toInt()) + +object DateUtils { + @JvmStatic + fun dateTimeNow() : String { + val format = SimpleDateFormat("dd-MMM-yyy hh:mm:ss") + return format.format(Date()) + } +} fun isJSONValid(jsonString: String?): Boolean { try { JSONObject(jsonString) } catch (ex: JSONException) { @@ -152,11 +194,16 @@ fun isJSONValid(jsonString: String?): Boolean { return true } -fun saveLog(context:Context, tag:String, message:String){ +fun saveLog(context: Context, tag: String, message: String){ val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) - var logs = pref.getString("GEOFENCE_LOGS","") + var logs = pref.getString("LOGS", "") logs += "$tag -> $message \n" - pref.edit().putString("GEOFENCE_LOGS", logs).apply(); + pref.edit().putString("LOGS", logs).apply(); +} + +fun getLogs(context: Context) : String?{ + val pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) + return pref.getString("LOGS", "") } class HTTPResponse(data: T){ @@ -170,15 +217,16 @@ fun httpPost(url: String, body: Map, onSuccess: (response: HTTP url.httpPost() .jsonBody(jsonBody, Charsets.UTF_8) .timeout(10000) - .header("Content-Type","application/json") - .header("Allow","*/*") + .header("Content-Type", "application/json") + .header("Allow", "*/*") .response { request, response, result -> + result.doAsyncResult { } result.fold({ data -> val dataString = String(data) - if(isJSONValid(dataString)){ - val responseData = gson.fromJson(dataString,type) + if (isJSONValid(dataString)) { + val responseData = gson.fromJson(dataString, type) onSuccess(HTTPResponse(responseData)) - }else{ + } else { onError(Exception("Invalid response from server (Not a valid JSON)")) } }, { diff --git a/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt new file mode 100644 index 00000000..5c11943e --- /dev/null +++ b/android/app/src/main/kotlin/com/cloud/diplomaticquarterapp/utils/Logs.kt @@ -0,0 +1,146 @@ +package com.cloud.diplomaticquarterapp.utils + +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import com.cloud.diplomaticquarterapp.BuildConfig +import com.cloud.diplomaticquarterapp.geofence.PREFS_STORAGE +import com.google.gson.Gson + +class Logs { + + enum class STATUS{ + SUCCESS, + ERROR; + } + class GeofenceEvent{ + companion object{ + fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){ + Logs.Common.save(context,"GeofenceEvent", tag, message, status) + } + + fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{ + return Logs.Common.list(context,"GeofenceEvent", tag, status) + } + + fun raw(context: Context):String{ + return Logs.Common.raw(context,"GeofenceEvent") + } + } + } + + class RegisterGeofence{ + companion object{ + fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){ + Logs.Common.save(context,"RegisterGeofence", tag, message, status) + } + + fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{ + return Logs.Common.list(context,"RegisterGeofence", tag, status) + } + + fun raw(context: Context):String{ + return Logs.Common.raw(context,"RegisterGeofence"); + } + } + } + + + companion object{ + private var pref:SharedPreferences? = null + fun save(context: Context, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){ + Logs.Common.save(context,"Logs", tag, message, status) + } + + fun list(context: Context, tag:String? = null, status:Logs.STATUS? = null):List{ + return Logs.Common.list(context,"Logs", tag, status) + } + + fun raw(context: Context):String{ + return Logs.Common.raw(context,"Logs"); + } + + private fun storage(context: Context):SharedPreferences{ + if(pref == null) { + pref = context.getSharedPreferences(PREFS_STORAGE, Context.MODE_PRIVATE) + } + return pref!! + } + } + + private class Common{ + companion object{ + private val gson = Gson() + + fun save(context: Context, key:String, tag:String, message:String, status:Logs.STATUS = STATUS.SUCCESS){ + if(!BuildConfig.DEBUG) + return + + val pref = Logs.storage(context) + + val string = pref.getString(key,"{}") + val json = gson.fromJson(string,LogsContainerModel::class.java) + json.add( + LogModel().apply { + this.TAG = tag + this.MESSAGE = message + this.STATUS = status.name + this.DATE = DateUtils.dateTimeNow() + } + ) + + pref.edit().putString(key,gson.toJson(json)).apply() + } + + fun list(context: Context, key:String, tag:String? = null, status:Logs.STATUS? = null):List{ + val pref = Logs.storage(context) + val string = pref.getString(key,"{}") + val json = gson.fromJson(string,LogsContainerModel::class.java) + if(tag == null && status == null) { + return json.LOGS + }else if(tag != null && status != null){ + return json.LOGS.filter { (it.TAG == tag && it.STATUS == status.name) } + }else if(tag != null){ + return json.LOGS.filter { (it.TAG == tag) } + }else if(status != null){ + return json.LOGS.filter { (it.STATUS == status.name) } + } + return listOf() + } + + fun raw(context: Context, key:String):String{ + val pref = Logs.storage(context) + val string = pref.getString(key,"{}") + return string!! + } + + } + } + + class LogModel{ + lateinit var TAG:String + lateinit var MESSAGE:String + lateinit var STATUS:String + lateinit var DATE:String + + companion object{ + fun with(tag:String, message:String, status:String):LogModel{ + return LogModel().apply { + this.TAG = tag + this.MESSAGE = message + this.STATUS = status + this.DATE = DateUtils.dateTimeNow() + } + } + } + } + + class LogsContainerModel{ + var LOGS = mutableListOf() + fun add(log:LogModel){ + LOGS.add(log) + } + } + + +} \ No newline at end of file diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id index a7923797..4e1980a7 100644 --- a/ios/Flutter/.last_build_id +++ b/ios/Flutter/.last_build_id @@ -1 +1 @@ -d9d141e787a8aa802f90b776d75f04fc \ No newline at end of file +e7c24319209ad9049a87d4c83aeeb7de \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 77d42044..8ef5d511 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -31,6 +31,7 @@ E9A35329258B8E8F00CBA688 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E9A35328258B8E8F00CBA688 /* GoogleService-Info.plist */; }; E9C8C136256BACDA00EFFB62 /* HMG_Guest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9C8C135256BACDA00EFFB62 /* HMG_Guest.swift */; }; E9E27168256E3A4000F49B69 /* LocalizedFromFlutter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9E27167256E3A4000F49B69 /* LocalizedFromFlutter.swift */; }; + E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -82,6 +83,7 @@ E9A35328258B8E8F00CBA688 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; E9C8C135256BACDA00EFFB62 /* HMG_Guest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HMG_Guest.swift; sourceTree = ""; }; E9E27167256E3A4000F49B69 /* LocalizedFromFlutter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizedFromFlutter.swift; sourceTree = ""; }; + E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlutterConstants.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -180,6 +182,7 @@ E923EFD125863FDF00E3E751 /* GeoZoneModel.swift */, E923EFD3258645C100E3E751 /* HMG_Geofence.swift */, E923EFD52587443800E3E751 /* HMGPlatformBridge.swift */, + E9F7623A25922BCE00FB5CCF /* FlutterConstants.swift */, ); path = Helper; sourceTree = ""; @@ -378,6 +381,7 @@ E91B5396256AAA6500E96549 /* GlobalHelper.swift in Sources */, E923EFD4258645C100E3E751 /* HMG_Geofence.swift in Sources */, E923EFD62587443800E3E751 /* HMGPlatformBridge.swift in Sources */, + E9F7623B25922BCE00FB5CCF /* FlutterConstants.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, E9E27168256E3A4000F49B69 /* LocalizedFromFlutter.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index d8f4c0c7..2dc828d1 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -7,25 +7,39 @@ var userNotificationCenterDelegate:UNUserNotificationCenterDelegate? = nil @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { let locationManager = CLLocationManager() + var flutterViewController:MainFlutterVC! override func application( _ application: UIApplication,didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { GMSServices.provideAPIKey("AIzaSyCiiJiHkocPbcziHt9O8rGWavDrxHRQys8") GeneratedPluginRegistrant.register(with: self) - - if let mainViewController = window.rootViewController as? MainFlutterVC{ - HMGPlatformBridge.initialize(flutterViewController: mainViewController) - } + initializePlatformChannel() if let _ = launchOptions?[.location] { HMG_Geofence.initGeofencing() } UNUserNotificationCenter.current().delegate = self - return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + + func initializePlatformChannel(){ + if let mainViewController = window.rootViewController as? MainFlutterVC{ // platform initialization suppose to be in foreground + flutterViewController = mainViewController + HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + + }else if let mainViewController = initialViewController(){ // platform initialization suppose to be in background + flutterViewController = mainViewController + HMGPlatformBridge.initialize(flutterViewController: flutterViewController) + } + } + + + func initialViewController() -> MainFlutterVC?{ + return nil //UIStoryboard(name: "Main", bundle: .main).instantiateInitialViewController() as? MainFlutterVC + } } extension AppDelegate{ @@ -37,3 +51,18 @@ extension AppDelegate{ } } } + + +/* + let dart = FlutterDartProject(precompiledDartBundle: .main) + let engine = FlutterEngine(name: "com.hmg.cs", project: dart, allowHeadlessExecution: true) + if engine.run(){ + flutterMethodChannel = FlutterMethodChannel(name: "HMG-Platform-Bridge", binaryMessenger: engine.binaryMessenger) + + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { (timer) in + FlutterText.with(key: "alreadyConnectedHmgNetwork"){ localized in + print(localized) + } + } + } + */ diff --git a/ios/Runner/Helper/API.swift b/ios/Runner/Helper/API.swift index 763147c8..b487f033 100644 --- a/ios/Runner/Helper/API.swift +++ b/ios/Runner/Helper/API.swift @@ -13,5 +13,10 @@ fileprivate let BASE_URL = "\(DOMAIN)/\(SERVICE)" struct API { static let WIFI_CREDENTIALS = "\(BASE_URL)/Hmg_SMS_Get_By_ProjectID_And_PatientID" - } + + +//struct API { +// static let WIFI_CREDENTIALS = FlutterConstants.WIFI_CREDENTIALS_URL +// static let LOG_GEOFENCE = FlutterConstants.LOG_GEOFENCE_URL +//} diff --git a/ios/Runner/Helper/FlutterConstants.swift b/ios/Runner/Helper/FlutterConstants.swift new file mode 100644 index 00000000..f1b3f098 --- /dev/null +++ b/ios/Runner/Helper/FlutterConstants.swift @@ -0,0 +1,36 @@ +// +// FlutterConstants.swift +// Runner +// +// Created by ZiKambrani on 22/12/2020. +// + +import UIKit + +class FlutterConstants{ + static var LOG_GEOFENCE_URL:String? + static var WIFI_CREDENTIALS_URL:String? + static var DEFAULT_HTTP_PARAMS:[String:Any?]? + + class func set(){ + + // (FiX) Take a start with FlutterMethodChannel (kikstart) + /* First call to flutter method is not returning the correct value (Always returning 'NSObject') then after it wroking fine and returning correct value*/ + FlutterText.with(key: "test") { (test) in + + flutterMethodChannel?.invokeMethod("getDefaultHttpParameters", arguments: nil){ (response) in + if let defaultHTTPParams = response as? [String:Any?]{ + DEFAULT_HTTP_PARAMS = defaultHTTPParams + } + + } + + flutterMethodChannel?.invokeMethod("getLogGeofenceFullUrl", arguments:nil){ (response) in + if let url = response as? String{ + LOG_GEOFENCE_URL = url + } + } + + } + } +} diff --git a/ios/Runner/Helper/GlobalHelper.swift b/ios/Runner/Helper/GlobalHelper.swift index 2e323a2a..37687806 100644 --- a/ios/Runner/Helper/GlobalHelper.swift +++ b/ios/Runner/Helper/GlobalHelper.swift @@ -70,7 +70,7 @@ func userProfile() -> [String:Any?]?{ fileprivate let defaultHTTPParams:[String : Any?] = [ "ZipCode" : "966", - "VersionID" : 5.6, + "VersionID" : 5.8, "Channel" : 3, "LanguageID" : appLanguageCode(), "IPAdress" : "10.20.10.20", diff --git a/ios/Runner/Helper/HMGPlatformBridge.swift b/ios/Runner/Helper/HMGPlatformBridge.swift index f897188c..f94f9b34 100644 --- a/ios/Runner/Helper/HMGPlatformBridge.swift +++ b/ios/Runner/Helper/HMGPlatformBridge.swift @@ -49,6 +49,9 @@ class HMGPlatformBridge{ print("") } + Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { (timer) in + FlutterConstants.set() + } } diff --git a/lib/config/config.dart b/lib/config/config.dart index 4a420ec8..10ec0b73 100644 --- a/lib/config/config.dart +++ b/lib/config/config.dart @@ -22,6 +22,9 @@ const GET_PROJECT = 'Services/Lists.svc/REST/GetProject'; const GET_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_GetAllPoints'; const LOG_GEO_ZONES = 'Services/Patients.svc/REST/GeoF_InsertPatientFileInfo'; +// Wifi Credentials +const WIFI_CREDENTIALS = "Services/Patients.svc/Hmg_SMS_Get_By_ProjectID_And_PatientID"; + ///Doctor const GET_MY_DOCTOR = 'Services/Doctors.svc/REST/GetPatientDoctorAppointmentResult'; const GET_DOCTOR_PROFILE = 'Services/Doctors.svc/REST/GetDocProfiles'; @@ -199,7 +202,7 @@ const UPDATE_HEALTH_TERMS = '/services/Patients.svc/REST/UpdatePateintHealthSumm const CHANNEL = 3; const GENERAL_ID = 'Cs2020@2016\$2958'; const IP_ADDRESS = '10.20.10.20'; -const VERSION_ID = 5.6; +const VERSION_ID = 5.8; const SETUP_ID = '91877'; const LANGUAGE = 2; const PATIENT_OUT_SA = 0; @@ -306,7 +309,6 @@ class AppGlobal { Request getPublicRequest() { Request request = new Request(); - request.VersionID = 5.6; //3.6; request.Channel = 3; request.IPAdress = "10.20.10.20"; request.generalid = 'Cs2020@2016\$2958';