diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/profile/ProfileSource.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/profile/ProfileSource.kt index 8dd18d2b7ee..d9127342f78 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/profile/ProfileSource.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/profile/ProfileSource.kt @@ -35,6 +35,6 @@ interface ProfileSource { var currentProfileIndex: Int fun currentProfile(): SingleProfile? - fun storeSettings(activity: FragmentActivity? = null) + fun storeSettings(activity: FragmentActivity? = null, emptyCreated: Boolean = false) } \ No newline at end of file diff --git a/core/interfaces/src/main/java/info/nightscout/interfaces/sync/NsClient.kt b/core/interfaces/src/main/java/info/nightscout/interfaces/sync/NsClient.kt index 778d103e20f..56a39beb2ed 100644 --- a/core/interfaces/src/main/java/info/nightscout/interfaces/sync/NsClient.kt +++ b/core/interfaces/src/main/java/info/nightscout/interfaces/sync/NsClient.kt @@ -16,7 +16,7 @@ interface NsClient : Sync { fun textLog(): Spanned fun clearLog() - enum class Collection { ENTRIES, TREATMENTS, FOODS } + enum class Collection { ENTRIES, TREATMENTS, FOODS, PROFILE } /** * NSC v3 does first load of all data * next loads are using srvModified property for sync diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt index d7bf25eaf71..8c3843315d0 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/NSAndroidClientImpl.kt @@ -456,6 +456,19 @@ class NSAndroidClientImpl( } + override suspend fun getProfileModifiedSince(from: Long): NSAndroidClient.ReadResponse> = callWrapper(dispatcher) { + + val response = api.getProfileModifiedSince(from) + if (response.isSuccessful) { + val eTagString = response.headers()["ETag"] + val eTag = eTagString?.substring(3, eTagString.length - 1)?.toLong() + return@callWrapper NSAndroidClient.ReadResponse(code = response.raw().networkResponse?.code ?: response.code(), lastServerModified = eTag, values = response.body()?.result.toNotNull()) + } else { + throw UnsuccessfullNightscoutException() + } + } + + private suspend fun callWrapper(dispatcher: CoroutineDispatcher, block: suspend () -> T): T = withContext(dispatcher) { retry( diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt index ca189425f17..290d05b4e80 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/interfaces/NSAndroidClient.kt @@ -35,6 +35,7 @@ interface NSAndroidClient { suspend fun getDeviceStatusModifiedSince(from: Long): List suspend fun createProfileStore(remoteProfileStore: JSONObject): CreateUpdateResponse + suspend fun getProfileModifiedSince(from: Long): ReadResponse> suspend fun getLastProfileStore(): ReadResponse> suspend fun createTreatment(nsTreatment: NSTreatment): CreateUpdateResponse diff --git a/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt b/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt index 38e35c3c840..5206bfa14d5 100644 --- a/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt +++ b/core/ns-sdk/src/main/java/info/nightscout/sdk/networking/NightscoutRemoteService.kt @@ -93,9 +93,14 @@ internal interface NightscoutRemoteService { @DELETE("v3/food") suspend fun deleteFood(@Path("identifier") identifier: String): Response> + @GET("v3/profile/history/{from}") + suspend fun getProfileModifiedSince(@Path("from") from: Long, @Query("limit") limit: Long = 10): Response>> + + @GET("v3/profile?sort\$desc=date&limit=1") suspend fun getLastProfile(): Response>> + @POST("v3/profile") suspend fun createProfile(@Body profile: JsonObject): Response> diff --git a/implementation/src/main/java/info/nightscout/implementation/profile/ProfileStoreObject.kt b/implementation/src/main/java/info/nightscout/implementation/profile/ProfileStoreObject.kt index e4312213270..6889af489de 100644 --- a/implementation/src/main/java/info/nightscout/implementation/profile/ProfileStoreObject.kt +++ b/implementation/src/main/java/info/nightscout/implementation/profile/ProfileStoreObject.kt @@ -45,7 +45,7 @@ class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JS } override fun getStartDate(): Long { - val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0 + val iso = JsonHelper.safeGetString(data, "created_at") ?: JsonHelper.safeGetString(data, "startDate") ?: return 0 return try { dateUtil.fromISODateString(iso) } catch (e: Exception) { diff --git a/plugins/aps/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt b/plugins/aps/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt index e4040d0369e..7367156f65e 100644 --- a/plugins/aps/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt +++ b/plugins/aps/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt @@ -45,7 +45,7 @@ class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JS } override fun getStartDate(): Long { - val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0 + val iso = JsonHelper.safeGetString(data, "created_at") ?: JsonHelper.safeGetString(data,"startDate") ?: return 0 return try { dateUtil.fromISODateString(iso) } catch (e: Exception) { diff --git a/plugins/automation/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt b/plugins/automation/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt index e4040d0369e..7367156f65e 100644 --- a/plugins/automation/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt +++ b/plugins/automation/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt @@ -45,7 +45,7 @@ class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JS } override fun getStartDate(): Long { - val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0 + val iso = JsonHelper.safeGetString(data, "created_at") ?: JsonHelper.safeGetString(data,"startDate") ?: return 0 return try { dateUtil.fromISODateString(iso) } catch (e: Exception) { diff --git a/plugins/main/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt b/plugins/main/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt index 66dd4159829..85b4fe2f74c 100644 --- a/plugins/main/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt +++ b/plugins/main/src/main/java/info/nightscout/plugins/profile/ProfilePlugin.kt @@ -175,7 +175,7 @@ class ProfilePlugin @Inject constructor( } @Synchronized - override fun storeSettings(activity: FragmentActivity?) { + override fun storeSettings(activity: FragmentActivity?, emptyCreated: Boolean) { for (i in 0 until numOfProfiles) { profiles[i].run { val localProfileNumbered = Constants.LOCAL_PROFILE + "_" + i + "_" @@ -191,7 +191,7 @@ class ProfilePlugin @Inject constructor( } sp.putInt(Constants.LOCAL_PROFILE + "_profiles", numOfProfiles) - sp.putLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, dateUtil.now()) + sp.putLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, if (emptyCreated) 0 else dateUtil.now()) createAndStoreConvertedProfile() isEdited = false aapsLogger.debug(LTag.PROFILE, "Storing settings: " + rawProfile?.data.toString()) @@ -360,7 +360,7 @@ class ProfilePlugin @Inject constructor( ) currentProfileIndex = profiles.size - 1 createAndStoreConvertedProfile() - storeSettings() + storeSettings(emptyCreated = true) } fun cloneProfile() { @@ -412,6 +412,7 @@ class ProfilePlugin @Inject constructor( if (numOfProfiles > 0) json.put("defaultProfile", currentProfile()?.name) val startDate = sp.getLong(info.nightscout.core.utils.R.string.key_local_profile_last_change, dateUtil.now()) json.put("date", startDate) + json.put("created_at", dateUtil.toISOAsUTC(startDate)) json.put("startDate", dateUtil.toISOAsUTC(startDate)) json.put("store", store) } catch (e: JSONException) { diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt index 3eff83723ba..4204f65c72b 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/NSClientV3Plugin.kt @@ -132,12 +132,14 @@ class NSClientV3Plugin @Inject constructor( when { sp.getBoolean(R.string.key_ns_client_paused, false) -> rh.gs(info.nightscout.core.ui.R.string.paused) isAllowed.not() -> blockingReason + lastOperationError != null -> rh.gs(info.nightscout.core.ui.R.string.error) nsAndroidClient?.lastStatus == null -> rh.gs(R.string.not_connected) workIsRunning(arrayOf(JOB_NAME)) -> rh.gs(R.string.working) nsAndroidClient?.lastStatus?.apiPermissions?.isFull() == true -> rh.gs(info.nightscout.shared.R.string.connected) nsAndroidClient?.lastStatus?.apiPermissions?.isRead() == true -> rh.gs(R.string.read_only) else -> rh.gs(info.nightscout.core.ui.R.string.unknown) } + var lastOperationError: String? = null internal var nsAndroidClient: NSAndroidClient? = null @@ -315,6 +317,7 @@ class NSClientV3Plugin @Inject constructor( NsClient.Collection.ENTRIES -> lastLoadedSrvModified.collections.entries == 0L NsClient.Collection.TREATMENTS -> lastLoadedSrvModified.collections.treatments == 0L NsClient.Collection.FOODS -> lastLoadedSrvModified.collections.foods == 0L + NsClient.Collection.PROFILE -> lastLoadedSrvModified.collections.profile == 0L } override fun updateLatestBgReceivedIfNewer(latestReceived: Long) { diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt index 8246cbf3fb2..2b42ea36fdb 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadBgWorker.kt @@ -119,9 +119,11 @@ class LoadBgWorker( } catch (error: Exception) { aapsLogger.error("Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt index a54663f17b2..437f2acae2a 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadDeviceStatusWorker.kt @@ -52,9 +52,11 @@ class LoadDeviceStatusWorker( } catch (error: Exception) { aapsLogger.error("Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt index f993f2e9f35..8a92a1f7037 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadFoodsWorker.kt @@ -64,9 +64,11 @@ class LoadFoodsWorker( } catch (error: Exception) { aapsLogger.error("Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt index bfcb3b0292e..c4333ab28b3 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadLastModificationWorker.kt @@ -28,8 +28,10 @@ class LoadLastModificationWorker( } catch (error: Exception) { aapsLogger.error(LTag.NSCLIENT, "Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt index 953eac93517..0acae4ffc40 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadProfileStoreWorker.kt @@ -8,6 +8,7 @@ import androidx.work.WorkerParameters import androidx.work.workDataOf import info.nightscout.core.utils.receivers.DataWorkerStorage import info.nightscout.core.utils.worker.LoggingWorker +import info.nightscout.interfaces.sync.NsClient import info.nightscout.interfaces.utils.JsonHelper import info.nightscout.interfaces.workflow.WorkerClasses import info.nightscout.plugins.sync.nsclientV3.NSClientV3Plugin @@ -37,13 +38,23 @@ class LoadProfileStoreWorker( val nsAndroidClient = nsClientV3Plugin.nsAndroidClient ?: return Result.failure(workDataOf("Error" to "AndroidClient is null")) try { - val lastLoaded = max(nsClientV3Plugin.lastLoadedSrvModified.collections.profile, 0) + val isFirstLoad = nsClientV3Plugin.isFirstLoad(NsClient.Collection.PROFILE) + val lastLoaded = max(nsClientV3Plugin.lastLoadedSrvModified.collections.profile, dateUtil.now() - nsClientV3Plugin.maxAge) if ((nsClientV3Plugin.newestDataOnServer?.collections?.profile ?: Long.MAX_VALUE) > lastLoaded) { - val response: NSAndroidClient.ReadResponse> = nsAndroidClient.getLastProfileStore() + val response: NSAndroidClient.ReadResponse> = + if (isFirstLoad) nsAndroidClient.getLastProfileStore() + else nsAndroidClient.getProfileModifiedSince(lastLoaded) val profiles = response.values - if (profiles.size == 1) { - val profile = profiles[0] - JsonHelper.safeGetLongAllowNull(profile, "srvModified")?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = it } + if (profiles.isNotEmpty()) { + val profile = profiles[profiles.size - 1] + // if srvModified found in response + response.lastServerModified?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = it } ?: + // if srvModified found in record + JsonHelper.safeGetLongAllowNull(profile, "srvModified")?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = it } ?: + // if created_at found in record + JsonHelper.safeGetStringAllowNull(profile, "created_at", null)?.let { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = dateUtil.fromISODateString(it) } ?: + // if not found reset to now + { nsClientV3Plugin.lastLoadedSrvModified.collections.profile = dateUtil.now() } nsClientV3Plugin.storeLastLoadedSrvModified() aapsLogger.debug(LTag.NSCLIENT, "PROFILE: $profile") rxBus.send(EventNSClientNewLog("RCV", "1 PROFILE from ${dateUtil.dateAndTimeAndSecondsString(lastLoaded)}")) diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt index 74488391faa..d51a2863067 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadStatusWorker.kt @@ -27,8 +27,10 @@ class LoadStatusWorker( } catch (error: Exception) { aapsLogger.error("Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt index 596e20b022c..750ad438e51 100644 --- a/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt +++ b/plugins/sync/src/main/java/info/nightscout/plugins/sync/nsclientV3/workers/LoadTreatmentsWorker.kt @@ -104,9 +104,11 @@ class LoadTreatmentsWorker( } catch (error: Exception) { aapsLogger.error("Error: ", error) rxBus.send(EventNSClientNewLog("ERROR", error.localizedMessage)) + nsClientV3Plugin.lastOperationError = error.localizedMessage return Result.failure(workDataOf("Error" to error.localizedMessage)) } + nsClientV3Plugin.lastOperationError = null return Result.success() } } \ No newline at end of file diff --git a/ui/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt b/ui/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt index 6eb1d28002a..ec8a67eab5b 100644 --- a/ui/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt +++ b/ui/src/test/java/info/nightscout/androidaps/ProfileStoreObject.kt @@ -45,7 +45,7 @@ class ProfileStoreObject(val injector: HasAndroidInjector, override val data: JS } override fun getStartDate(): Long { - val iso = JsonHelper.safeGetString(data, "startDate") ?: return 0 + val iso = JsonHelper.safeGetString(data, "created_at") ?: JsonHelper.safeGetString(data, "startDate") ?: return 0 return try { dateUtil.fromISODateString(iso) } catch (e: Exception) {