Tidepool test UI

This commit is contained in:
Milos Kozak 2019-06-03 00:03:06 +02:00
parent e2364561ed
commit 048ea4d489
9 changed files with 202 additions and 31 deletions

View file

@ -104,7 +104,7 @@ android {
targetSdkVersion 25 targetSdkVersion 25
multiDexEnabled true multiDexEnabled true
versionCode 1500 versionCode 1500
version "2.3.1-dev" version "2.3.1-tidepool"
buildConfigField "String", "VERSION", '"' + version + '"' buildConfigField "String", "VERSION", '"' + version + '"'
buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"' buildConfigField "String", "BUILDVERSION", '"' + generateGitBuild() + '-' + generateDate() + '"'
buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"' buildConfigField "String", "REMOTE", '"' + generateGitRemote() + '"'

View file

@ -0,0 +1,41 @@
package info.nightscout.androidaps.plugins.general.tidepool
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.squareup.otto.Subscribe
import info.nightscout.androidaps.R
import info.nightscout.androidaps.plugins.common.SubscriberFragment
import info.nightscout.androidaps.plugins.general.nsclient.events.EventNSClientUpdateGUI
import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader
import kotlinx.android.synthetic.main.tidepool_fragment.*
class TidepoolFragment : SubscriberFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.tidepool_fragment, container, false)
tidepool_login.setOnClickListener {
TidepoolUploader.doLogin()
}
tidepool_removeall.setOnClickListener { }
tidepool_uploadnow.setOnClickListener { }
return view
}
@Subscribe
fun onStatusEvent(ev: EventNSClientUpdateGUI) {
updateGUI()
}
override fun updateGUI() {
val activity = activity
activity?.runOnUiThread {
// TidepoolPlugin.updateLog()
// tidepool_log.text = TidepoolPlugin.textLog
}
}
}

View file

@ -0,0 +1,36 @@
package info.nightscout.androidaps.plugins.general.tidepool;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import info.nightscout.androidaps.MainApp;
import info.nightscout.androidaps.R;
import info.nightscout.androidaps.plugins.common.SubscriberFragment;
import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader;
import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData;
public class TidepoolJavaFragment extends SubscriberFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.tidepool_fragment, container, false);
Button login = view.findViewById(R.id.tidepool_login);
login.setOnClickListener(v -> {
TidepoolUploader.INSTANCE.doLogin();
});
Button removeall = view.findViewById(R.id.tidepool_removeall);
removeall.setOnClickListener(v -> {
MainApp.bus().post(new EventTidepoolResetData());
});
return view;
}
@Override
protected void updateGUI() {
}
}

View file

@ -9,7 +9,10 @@ import info.nightscout.androidaps.interfaces.PluginBase
import info.nightscout.androidaps.interfaces.PluginDescription import info.nightscout.androidaps.interfaces.PluginDescription
import info.nightscout.androidaps.interfaces.PluginType import info.nightscout.androidaps.interfaces.PluginType
import info.nightscout.androidaps.logging.L import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.general.tidepool.comm.Session
import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader import info.nightscout.androidaps.plugins.general.tidepool.comm.TidepoolUploader
import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolDoUpload
import info.nightscout.androidaps.plugins.general.tidepool.events.EventTidepoolResetData
import info.nightscout.androidaps.plugins.general.tidepool.utils.RateLimit import info.nightscout.androidaps.plugins.general.tidepool.utils.RateLimit
import info.nightscout.androidaps.receivers.ChargingStateReceiver import info.nightscout.androidaps.receivers.ChargingStateReceiver
import info.nightscout.androidaps.utils.SP import info.nightscout.androidaps.utils.SP
@ -19,12 +22,15 @@ object TidepoolPlugin : PluginBase(PluginDescription()
.mainType(PluginType.GENERAL) .mainType(PluginType.GENERAL)
.pluginName(R.string.tidepool) .pluginName(R.string.tidepool)
.shortName(R.string.tidepool_shortname) .shortName(R.string.tidepool_shortname)
.fragmentClass(TidepoolJavaFragment::class.java.name)
.preferencesId(R.xml.pref_tidepool) .preferencesId(R.xml.pref_tidepool)
.description(R.string.description_tidepool) .description(R.string.description_tidepool)
) { ) {
private val log = LoggerFactory.getLogger(L.TIDEPOOL) private val log = LoggerFactory.getLogger(L.TIDEPOOL)
private var wifiConnected = false private var wifiConnected = false
var session: Session? = null
override fun onStart() { override fun onStart() {
MainApp.bus().register(this) MainApp.bus().register(this)
super.onStart() super.onStart()
@ -35,6 +41,12 @@ object TidepoolPlugin : PluginBase(PluginDescription()
super.onStop() super.onStop()
} }
fun doUpload() {
if (session == null)
session = TidepoolUploader.doLogin()
else TidepoolUploader.doUpload(session!!)
}
@Suppress("UNUSED_PARAMETER") @Suppress("UNUSED_PARAMETER")
@Subscribe @Subscribe
fun onStatusEvent(ev: EventNewBG) { fun onStatusEvent(ev: EventNewBG) {
@ -42,7 +54,24 @@ object TidepoolPlugin : PluginBase(PluginDescription()
&& (!SP.getBoolean(R.string.key_tidepool_only_while_charging, false) || ChargingStateReceiver.isCharging()) && (!SP.getBoolean(R.string.key_tidepool_only_while_charging, false) || ChargingStateReceiver.isCharging())
&& (!SP.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || wifiConnected) && (!SP.getBoolean(R.string.key_tidepool_only_while_unmetered, false) || wifiConnected)
&& RateLimit.ratelimit("tidepool-new-data-upload", 1200)) && RateLimit.ratelimit("tidepool-new-data-upload", 1200))
TidepoolUploader.doLogin() doUpload()
}
@Suppress("UNUSED_PARAMETER")
@Subscribe
fun onEventTidepoolDoUpload(ev: EventTidepoolDoUpload) {
doUpload()
}
@Suppress("UNUSED_PARAMETER")
@Subscribe
fun onEventTidepoolResetData(ev: EventTidepoolResetData) {
if (session == null)
session = TidepoolUploader.doLogin()
if (session != null) {
TidepoolUploader.deleteDataSet(session!!)
TidepoolUploader.startSession(session!!)
}
} }
@Subscribe @Subscribe

View file

@ -59,10 +59,10 @@ object TidepoolUploader {
} }
@Synchronized @Synchronized
fun doLogin() { fun doLogin(): Session? {
if (!SP.getBoolean(R.string.key_cloud_storage_tidepool_enable, false)) { if (!SP.getBoolean(R.string.key_cloud_storage_tidepool_enable, false)) {
log.debug("Cannot login as disabled by preference") log.debug("Cannot login as disabled by preference")
return return null
} }
// TODO failure backoff // TODO failure backoff
extendWakeLock(30000) extendWakeLock(30000)
@ -72,10 +72,12 @@ object TidepoolUploader {
status("Connecting") status("Connecting")
call?.enqueue(TidepoolCallback<AuthReplyMessage>(session, "Login", { startSession(session) }, { loginFailed() })) call?.enqueue(TidepoolCallback<AuthReplyMessage>(session, "Login", { startSession(session) }, { loginFailed() }))
return session
} else { } else {
if (L.isEnabled(L.TIDEPOOL)) log.debug("Cannot do login as user credentials have not been set correctly") if (L.isEnabled(L.TIDEPOOL)) log.debug("Cannot do login as user credentials have not been set correctly")
status("Invalid credentials") status("Invalid credentials")
releaseWakeLock() releaseWakeLock()
return null
} }
} }
@ -103,7 +105,7 @@ object TidepoolUploader {
releaseWakeLock() releaseWakeLock()
} }
private fun startSession(session: Session) { fun startSession(session: Session) {
extendWakeLock(30000) extendWakeLock(30000)
if (session.authReply?.userid != null) { if (session.authReply?.userid != null) {
// See if we already have an open data set to write to // See if we already have an open data set to write to
@ -130,7 +132,7 @@ object TidepoolUploader {
} }
} }
private fun doUpload(session: Session) { fun doUpload(session: Session) {
if (!TidepoolPlugin.enabled()) { if (!TidepoolPlugin.enabled()) {
if (L.isEnabled(L.TIDEPOOL)) if (L.isEnabled(L.TIDEPOOL))
log.debug("Cannot upload - preference disabled") log.debug("Cannot upload - preference disabled")
@ -164,6 +166,7 @@ object TidepoolUploader {
} }
private fun status(status: String) { private fun status(status: String) {
log.debug("New status: $status")
MainApp.bus().post(EventTidepoolStatus(status)) MainApp.bus().post(EventTidepoolStatus(status))
} }
@ -186,23 +189,18 @@ object TidepoolUploader {
releaseWakeLock() releaseWakeLock()
} }
private fun deleteData(session: Session) {
if (session.authReply!!.userid != null) {
val call = session.service!!.deleteAllData(session.token!!, session.authReply!!.userid!!)
call.enqueue(TidepoolCallback(session, "Delete Data", {}, {}))
} else {
log.error("Got login response but cannot determine userid - cannot proceed")
}
}
private fun getDataSet(session: Session) { private fun getDataSet(session: Session) {
val call = session.service!!.getDataSet(session.token!!, "bogus") val call = session.service!!.getDataSet(session.token!!, "bogus")
call.enqueue(TidepoolCallback(session, "Get Data", {}, {})) call.enqueue(TidepoolCallback(session, "Get Data", {}, {}))
} }
private fun deleteDataSet(session: Session) { fun deleteDataSet(session: Session) {
val call = session.service!!.deleteDataSet(session.token!!, "bogus") if (session.datasetReply?.id != null) {
call.enqueue(TidepoolCallback(session, "Delete Data", {}, {})) val call = session.service?.deleteDataSet(session.token!!, session.datasetReply!!.id!!)
call?.enqueue(TidepoolCallback(session, "Delete Dataset", {}, {}))
} else {
log.error("Got login response but cannot determine dataseId - cannot proceed")
}
} }
@Synchronized @Synchronized

View file

@ -1,8 +1,8 @@
package info.nightscout.androidaps.plugins.general.tidepool.comm package info.nightscout.androidaps.plugins.general.tidepool.comm
import android.util.Log
import info.nightscout.androidaps.MainApp import info.nightscout.androidaps.MainApp
import info.nightscout.androidaps.R import info.nightscout.androidaps.R
import info.nightscout.androidaps.logging.L
import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions import info.nightscout.androidaps.plugins.configBuilder.ProfileFunctions
import info.nightscout.androidaps.plugins.general.tidepool.elements.* import info.nightscout.androidaps.plugins.general.tidepool.elements.*
import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance import info.nightscout.androidaps.plugins.general.tidepool.utils.GsonInstance
@ -11,6 +11,7 @@ import info.nightscout.androidaps.plugins.treatments.TreatmentsPlugin
import info.nightscout.androidaps.utils.DateUtil import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.androidaps.utils.SP import info.nightscout.androidaps.utils.SP
import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.T
import org.slf4j.LoggerFactory
import java.util.* import java.util.*
object UploadChunk { object UploadChunk {
@ -21,6 +22,7 @@ object UploadChunk {
private val DEFAULT_WINDOW_OFFSET = T.mins(15).msecs() private val DEFAULT_WINDOW_OFFSET = T.mins(15).msecs()
private val MAX_LATENCY_THRESHOLD_MINUTES: Long = 1440 // minutes per day private val MAX_LATENCY_THRESHOLD_MINUTES: Long = 1440 // minutes per day
private val log = LoggerFactory.getLogger(L.TIDEPOOL)
fun getNext(session: Session): String? { fun getNext(session: Session): String? {
session.start = getLastEnd() session.start = getLastEnd()
@ -28,7 +30,7 @@ object UploadChunk {
val result = get(session.start, session.end) val result = get(session.start, session.end)
if (result != null && result.length < 3) { if (result != null && result.length < 3) {
Log.d(TAG, "No records in this time period, setting start to best end time") if (L.isEnabled(L.TIDEPOOL)) log.debug("No records in this time period, setting start to best end time")
setLastEnd(Math.max(session.end, getOldestRecordTimeStamp())) setLastEnd(Math.max(session.end, getOldestRecordTimeStamp()))
} }
return result return result
@ -36,13 +38,13 @@ object UploadChunk {
operator fun get(start: Long, end: Long): String? { operator fun get(start: Long, end: Long): String? {
Log.e(TAG, "Syncing data between: " + DateUtil.dateAndTimeFullString(start) + " -> " + DateUtil.dateAndTimeFullString(end)) if (L.isEnabled(L.TIDEPOOL)) log.debug("Syncing data between: " + DateUtil.dateAndTimeFullString(start) + " -> " + DateUtil.dateAndTimeFullString(end))
if (end <= start) { if (end <= start) {
Log.e(TAG, "End is <= start: " + DateUtil.dateAndTimeFullString(start) + " " + DateUtil.dateAndTimeFullString(end)) if (L.isEnabled(L.TIDEPOOL)) log.debug("End is <= start: " + DateUtil.dateAndTimeFullString(start) + " " + DateUtil.dateAndTimeFullString(end))
return null return null
} }
if (end - start > MAX_UPLOAD_SIZE) { if (end - start > MAX_UPLOAD_SIZE) {
Log.e(TAG, "More than max range - rejecting") if (L.isEnabled(L.TIDEPOOL)) log.debug("More than max range - rejecting")
return null return null
} }
@ -61,7 +63,7 @@ object UploadChunk {
val value = getLatencySliderValue(SP.getInt(R.string.key_tidepool_window_latency, 0)).toLong() val value = getLatencySliderValue(SP.getInt(R.string.key_tidepool_window_latency, 0)).toLong()
return Math.max(T.mins(value).msecs(), DEFAULT_WINDOW_OFFSET) return Math.max(T.mins(value).msecs(), DEFAULT_WINDOW_OFFSET)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Reverting to default of 15 minutes due to Window Size exception: $e") if (L.isEnabled(L.TIDEPOOL)) log.debug("Reverting to default of 15 minutes due to Window Size exception: $e")
return DEFAULT_WINDOW_OFFSET // default return DEFAULT_WINDOW_OFFSET // default
} }
@ -79,10 +81,11 @@ object UploadChunk {
fun setLastEnd(time: Long) { fun setLastEnd(time: Long) {
if (time > getLastEnd()) { if (time > getLastEnd()) {
SP.putLong(R.string.key_tidepool_last_end, time) //TODO SP.putLong(R.string.key_tidepool_last_end, time)
Log.d(TAG, "Updating last end to: " + DateUtil.dateAndTimeFullString(time)) SP.putLong(R.string.key_tidepool_last_end, 0)
if (L.isEnabled(L.TIDEPOOL)) log.debug("Updating last end to: " + DateUtil.dateAndTimeFullString(time))
} else { } else {
Log.e(TAG, "Cannot set last end to: " + DateUtil.dateAndTimeFullString(time) + " vs " + DateUtil.dateAndTimeFullString(getLastEnd())) if (L.isEnabled(L.TIDEPOOL)) log.debug("Cannot set last end to: " + DateUtil.dateAndTimeFullString(time) + " vs " + DateUtil.dateAndTimeFullString(getLastEnd()))
} }
} }
@ -123,7 +126,10 @@ object UploadChunk {
} }
internal fun getBgReadings(start: Long, end: Long): List<SensorGlucoseElement> { internal fun getBgReadings(start: Long, end: Long): List<SensorGlucoseElement> {
return SensorGlucoseElement.fromBgReadings(MainApp.getDbHelper().getBgreadingsDataFromTime(start, end, true)) val readings = MainApp.getDbHelper().getBgreadingsDataFromTime(start, end, true)
if (L.isEnabled(L.TIDEPOOL))
log.debug("${readings.size} selected for upload")
return SensorGlucoseElement.fromBgReadings(readings)
} }
internal fun getBasals(start: Long, end: Long): List<BasalElement> { internal fun getBasals(start: Long, end: Long): List<BasalElement> {
@ -136,15 +142,15 @@ object UploadChunk {
if (current != null) { if (current != null) {
if (this_rate != current.rate) { if (this_rate != current.rate) {
current.duration = temporaryBasal.date - current.timestamp current.duration = temporaryBasal.date - current.timestamp
Log.d(TAG, "Adding current: " + current.toS()) if (L.isEnabled(L.TIDEPOOL)) log.debug("Adding current: " + current.toS())
if (current.isValid()) { if (current.isValid()) {
basals.add(current) basals.add(current)
} else { } else {
Log.e(TAG, "Current basal is invalid: " + current.toS()) if (L.isEnabled(L.TIDEPOOL)) log.debug("Current basal is invalid: " + current.toS())
} }
current = null current = null
} else { } else {
Log.d(TAG, "Same rate as previous basal record: " + current.rate + " " + temporaryBasal.toStringFull()) if (L.isEnabled(L.TIDEPOOL)) log.debug("Same rate as previous basal record: " + current.rate + " " + temporaryBasal.toStringFull())
} }
} }
if (current == null) { if (current == null) {

View file

@ -0,0 +1,4 @@
package info.nightscout.androidaps.plugins.general.tidepool.events
class EventTidepoolDoUpload {
}

View file

@ -0,0 +1,6 @@
package info.nightscout.androidaps.plugins.general.tidepool.events
import info.nightscout.androidaps.events.Event
class EventTidepoolResetData :Event() {
}

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tidepool_status"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="-"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="32dp" />
<Button
android:id="@+id/tidepool_uploadnow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="36dp"
android:text="Upload now"
app:layout_constraintStart_toEndOf="@+id/tidepool_login"
tools:layout_editor_absoluteY="66dp" />
<Button
android:id="@+id/tidepool_removeall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:text="Remove all"
app:layout_constraintStart_toEndOf="@+id/tidepool_uploadnow"
tools:layout_editor_absoluteY="66dp" />
<TextView
android:id="@+id/tidepool_log"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="-- logs --"
tools:layout_editor_absoluteX="1dp"
tools:layout_editor_absoluteY="128dp" />
<Button
android:id="@+id/tidepool_login"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="1dp"
android:text="Login"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="66dp" />
</android.support.constraint.ConstraintLayout>