utils -> kt

This commit is contained in:
Milos Kozak 2022-05-03 23:05:17 +02:00
parent 37cee04926
commit 3c292e711f
10 changed files with 231 additions and 279 deletions

View file

@ -1,14 +0,0 @@
package info.nightscout.androidaps.interaction.utils;
public class Constants {
public static final long SECOND_IN_MS = 1000;
public static final long MINUTE_IN_MS = 60000;
public static final long HOUR_IN_MS = 3600000;
public static final long DAY_IN_MS = 86400000;
public static final long WEEK_IN_MS = DAY_IN_MS * 7;
public static final long MONTH_IN_MS = DAY_IN_MS * 30;
public static final long STALE_MS = Constants.MINUTE_IN_MS * 12;
}

View file

@ -0,0 +1,13 @@
package info.nightscout.androidaps.interaction.utils
@Suppress("unused")
object Constants {
const val SECOND_IN_MS: Long = 1000
const val MINUTE_IN_MS: Long = 60000
const val HOUR_IN_MS: Long = 3600000
const val DAY_IN_MS: Long = 86400000
const val WEEK_IN_MS = DAY_IN_MS * 7
const val MONTH_IN_MS = DAY_IN_MS * 30
const val STALE_MS = MINUTE_IN_MS * 12
}

View file

@ -12,7 +12,8 @@ public class DisplayFormat {
@Inject SP sp; @Inject SP sp;
@Inject WearUtil wearUtil; @Inject WearUtil wearUtil;
@Inject DisplayFormat() {} @Inject DisplayFormat() {
}
/** /**
* Maximal and minimal lengths of fields/labels shown in complications, in characters * Maximal and minimal lengths of fields/labels shown in complications, in characters
@ -74,12 +75,12 @@ public class DisplayFormat {
} }
// that only optimizes obvious things like 0 before . or at end, + at beginning // that only optimizes obvious things like 0 before . or at end, + at beginning
String delta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT -1); String delta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT - 1);
if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) { if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) {
return minutes + " " + deltaSymbol() + delta; return minutes + " " + deltaSymbol() + delta;
} }
String shortDelta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT -(1+minutes.length())); String shortDelta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT - (1 + minutes.length()));
return minutes + " " + shortDelta; return minutes + " " + shortDelta;
} }
@ -96,7 +97,7 @@ public class DisplayFormat {
final String SEP_MIN = " "; final String SEP_MIN = " ";
String line = String line =
raw.getStatus().getCob() + SEP_LONG + raw.getStatus().getIobSum() + SEP_LONG + basalRateSymbol()+raw.getStatus().getCurrentBasal(); raw.getStatus().getCob() + SEP_LONG + raw.getStatus().getIobSum() + SEP_LONG + basalRateSymbol() + raw.getStatus().getCurrentBasal();
if (line.length() <= MAX_FIELD_LEN_LONG) { if (line.length() <= MAX_FIELD_LEN_LONG) {
return line; return line;
} }
@ -105,14 +106,14 @@ public class DisplayFormat {
return line; return line;
} }
int remainingMax = MAX_FIELD_LEN_LONG - (raw.getStatus().getCob().length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN*2); int remainingMax = MAX_FIELD_LEN_LONG - (raw.getStatus().getCob().length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN * 2);
final String smallestIoB = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_IOB, remainingMax)); final String smallestIoB = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_IOB, remainingMax));
line = raw.getStatus().getCob() + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal(); line = raw.getStatus().getCob() + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal();
if (line.length() <= MAX_FIELD_LEN_LONG) { if (line.length() <= MAX_FIELD_LEN_LONG) {
return line; return line;
} }
remainingMax = MAX_FIELD_LEN_LONG - (smallestIoB.length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN*2); remainingMax = MAX_FIELD_LEN_LONG - (smallestIoB.length() + raw.getStatus().getCurrentBasal().length() + SEP_SHORT_LEN * 2);
final String simplifiedCob = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_COB, remainingMax)); final String simplifiedCob = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE).minimise(Math.max(MIN_FIELD_LEN_COB, remainingMax));
line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal(); line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal();
@ -135,13 +136,13 @@ public class DisplayFormat {
if (iobBolus.trim().length() == 0) { if (iobBolus.trim().length() == 0) {
iobBolus = "--"; iobBolus = "--";
} }
String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_FIELD_LEN_SHORT -1) - Math.max(MIN_FIELD_LEN_IOB, iobBolus.length())); String iobBasal = new SmallestDoubleString(iobs[1]).minimise((MAX_FIELD_LEN_SHORT - 1) - Math.max(MIN_FIELD_LEN_IOB, iobBolus.length()));
if (iobBasal.trim().length() == 0) { if (iobBasal.trim().length() == 0) {
iobBasal = "--"; iobBasal = "--";
} }
iob2 = iobBolus+" "+iobBasal; iob2 = iobBolus + " " + iobBasal;
} }
return Pair.create(iob1, iob2); return new Pair(iob1, iob2);
} }
public Pair<String, String> detailedCob(final RawDisplayData raw) { public Pair<String, String> detailedCob(final RawDisplayData raw) {
@ -152,6 +153,6 @@ public class DisplayFormat {
cob2 = cobMini.getExtra() + cobMini.getUnits(); cob2 = cobMini.getExtra() + cobMini.getUnits();
} }
final String cob1 = cobMini.minimise(MAX_FIELD_LEN_SHORT); final String cob1 = cobMini.minimise(MAX_FIELD_LEN_SHORT);
return Pair.create(cob1, cob2); return new Pair(cob1, cob2);
} }
} }

View file

@ -1,115 +0,0 @@
package info.nightscout.androidaps.interaction.utils;
import android.os.PowerManager;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.androidaps.BuildConfig;
import info.nightscout.androidaps.utils.DateUtil;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
/**
* Created for xDrip by jamorham on 07/03/2018
* Adapted for AAPS by dlvoy on 2019-11-11
*
* Tasks which are fired from events can be scheduled here and only execute when they become idle
* and are not being rescheduled within their wait window.
*
*/
@Singleton
public class Inevitable {
@Inject WearUtil wearUtil;
@Inject AAPSLogger aapsLogger;
@Inject DateUtil dateUtil;
@Inject Inevitable() {}
private static final int MAX_QUEUE_TIME = (int) Constants.MINUTE_IN_MS * 6;
private static final boolean debug = BuildConfig.DEBUG;
private final ConcurrentHashMap<String, Task> tasks = new ConcurrentHashMap<>();
public void task(final String id, long idle_for, Runnable runnable) {
if (idle_for > MAX_QUEUE_TIME) {
throw new RuntimeException(id + " Requested time: " + idle_for + " beyond max queue time");
}
final Task task = tasks.get(id);
if (task != null) {
// if it already exists then extend the time
task.extendTime(idle_for);
if (debug)
aapsLogger.debug(LTag.WEAR, "Extending time for: " + id + " to " + dateUtil.dateAndTimeAndSecondsString(task.when));
} else {
// otherwise create new task
if (runnable == null) return; // extension only if already exists
tasks.put(id, new Task(id, idle_for, runnable));
if (debug) {
aapsLogger.debug(LTag.WEAR,
"Creating task: " + id + " due: " + dateUtil.dateAndTimeAndSecondsString(tasks.get(id).when));
}
// create a thread to wait and execute in background
final Thread t = new Thread(() -> {
final PowerManager.WakeLock wl = wearUtil.getWakeLock(id, MAX_QUEUE_TIME + 5000);
try {
boolean running = true;
// wait for task to be due or killed
while (running) {
wearUtil.threadSleep(500);
final Task thisTask = tasks.get(id);
running = thisTask != null && !thisTask.poll();
}
} finally {
wearUtil.releaseWakeLock(wl);
}
});
t.setPriority(Thread.MIN_PRIORITY);
t.start();
}
}
public void kill(final String id) {
tasks.remove(id);
}
private class Task {
private long when;
private final Runnable what;
private final String id;
Task(String id, long offset, Runnable what) {
this.what = what;
this.id = id;
extendTime(offset);
}
public void extendTime(long offset) {
this.when = wearUtil.timestamp() + offset;
}
public boolean poll() {
final long till = wearUtil.msTill(when);
if (till < 1) {
if (debug) aapsLogger.debug(LTag.WEAR, "Executing task! " + this.id);
tasks.remove(this.id); // early remove to allow overlapping scheduling
what.run();
return true;
} else if (till > MAX_QUEUE_TIME) {
aapsLogger.debug(LTag.WEAR, "Task: " + this.id + " In queue too long: " + till);
tasks.remove(this.id);
return true;
}
return false;
}
}
}

View file

@ -0,0 +1,103 @@
package info.nightscout.androidaps.interaction.utils
import info.nightscout.androidaps.BuildConfig
import info.nightscout.androidaps.utils.DateUtil
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import javax.inject.Singleton
/**
* Created for xDrip by jamorham on 07/03/2018
* Adapted for AAPS by dlvoy on 2019-11-11
*
* Tasks which are fired from events can be scheduled here and only execute when they become idle
* and are not being rescheduled within their wait window.
*
*/
@Singleton
class Inevitable @Inject internal constructor() {
@Inject lateinit var wearUtil: WearUtil
@Inject lateinit var aapsLogger: AAPSLogger
@Inject lateinit var dateUtil: DateUtil
private val tasks = ConcurrentHashMap<String, Task>()
fun task(id: String, idle_for: Long, runnable: Runnable?) {
if (idle_for > MAX_QUEUE_TIME) {
throw RuntimeException("$id Requested time: $idle_for beyond max queue time")
}
val task = tasks[id]
if (task != null) {
// if it already exists then extend the time
task.extendTime(idle_for)
if (debug) aapsLogger.debug(LTag.WEAR, "Extending time for: " + id + " to " + dateUtil.dateAndTimeAndSecondsString(task.`when`))
} else {
// otherwise create new task
if (runnable == null) return // extension only if already exists
tasks[id] = Task(id, idle_for, runnable)
if (debug) {
aapsLogger.debug(
LTag.WEAR,
"Creating task: " + id + " due: " + dateUtil.dateAndTimeAndSecondsString(tasks[id]!!.`when`)
)
}
// create a thread to wait and execute in background
val t = Thread {
val wl = wearUtil.getWakeLock(id, MAX_QUEUE_TIME + 5000)
try {
var running = true
// wait for task to be due or killed
while (running) {
wearUtil.threadSleep(500)
val thisTask = tasks[id]
running = thisTask != null && !thisTask.poll()
}
} finally {
wearUtil.releaseWakeLock(wl)
}
}
t.priority = Thread.MIN_PRIORITY
t.start()
}
}
fun kill(id: String) {
tasks.remove(id)
}
private inner class Task(private val id: String, offset: Long, private val what: Runnable) {
var `when`: Long = 0
fun extendTime(offset: Long) {
`when` = wearUtil.timestamp() + offset
}
fun poll(): Boolean {
val till = wearUtil.msTill(`when`)
if (till < 1) {
if (debug) aapsLogger.debug(LTag.WEAR, "Executing task! $id")
tasks.remove(id) // early remove to allow overlapping scheduling
what.run()
return true
} else if (till > MAX_QUEUE_TIME) {
aapsLogger.debug(LTag.WEAR, "Task: $id In queue too long: $till")
tasks.remove(id)
return true
}
return false
}
init {
extendTime(offset)
}
}
companion object {
private const val MAX_QUEUE_TIME = Constants.MINUTE_IN_MS.toInt() * 6
private val debug = BuildConfig.DEBUG
}
}

View file

@ -1,43 +0,0 @@
package info.nightscout.androidaps.interaction.utils;
import java.util.Objects;
/**
* Same as android Pair, but clean room java class - does not require Android SDK for tests
*/
public class Pair<F, S> {
public final F first;
public final S second;
public Pair(F first, S second) {
this.first = first;
this.second = second;
}
public static <F, S> Pair<F, S> create(F f, S s) {
return new Pair<>(f, s);
}
@Override
public boolean equals(java.lang.Object o) {
if (o instanceof Pair) {
return ((Pair) o).first.equals(first) && ((Pair) o).second.equals(second);
} else {
return false;
}
}
@Override
public String toString() {
return "First: \""+first.toString()+"\" Second: \""+second.toString()+"\"";
}
@Override
public int hashCode() {
return Objects.hash(first, second);
}
}

View file

@ -0,0 +1,21 @@
package info.nightscout.androidaps.interaction.utils
import java.util.*
/**
* Same as android Pair, but clean room java class - does not require Android SDK for tests
*/
class Pair<F, S>(val first: F, val second: S) {
override fun equals(other: Any?): Boolean =
if (other is Pair<*, *>) other.first == first && other.second == second
else false
override fun toString(): String = "First: \"" + first.toString() + "\" Second: \"" + second.toString() + "\""
override fun hashCode(): Int = Objects.hash(first, second)
companion object {
fun <F, S> create(f: F, s: S): Pair<F, S> = Pair(f, s)
}
}

View file

@ -1,96 +0,0 @@
package info.nightscout.androidaps.interaction.utils;
import android.content.Context;
import android.os.Bundle;
import android.os.PowerManager;
import com.google.android.gms.wearable.DataMap;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import info.nightscout.shared.logging.AAPSLogger;
import info.nightscout.shared.logging.LTag;
/**
* Created by andy on 3/5/19.
* Adapted by dlvoy on 2019-11-06 using code from jamorham JoH class
*/
@Singleton
public class WearUtil {
@Inject public Context context;
@Inject public AAPSLogger aapsLogger;
@Inject public WearUtil() {
}
private final boolean debug_wakelocks = false;
private final Map<String, Long> rateLimits = new HashMap<>();
//==============================================================================================
// Time related util methods
//==============================================================================================
public long timestamp() {
return System.currentTimeMillis();
}
public long msSince(long when) {
return (timestamp() - when);
}
public long msTill(long when) {
return (when - timestamp());
}
//==============================================================================================
// Thread and power management utils
//==============================================================================================
// return true if below rate limit
public synchronized boolean isBelowRateLimit(String named, int onceForSeconds) {
// check if over limit
if ((rateLimits.containsKey(named)) && (timestamp() - rateLimits.get(named) < (onceForSeconds * 1000))) {
aapsLogger.debug(LTag.WEAR, named + " rate limited to one for " + onceForSeconds + " seconds");
return false;
}
// not over limit
rateLimits.put(named, timestamp());
return true;
}
public PowerManager.WakeLock getWakeLock(final String name, int millis) {
final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AAPS::" + name);
wl.acquire(millis);
if (debug_wakelocks)
aapsLogger.debug(LTag.WEAR, "getWakeLock: " + name + " " + wl);
return wl;
}
public void releaseWakeLock(PowerManager.WakeLock wl) {
if (debug_wakelocks) aapsLogger.debug(LTag.WEAR, "releaseWakeLock: " + wl.toString());
if (wl == null) return;
if (wl.isHeld()) wl.release();
}
public void threadSleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
// we simply ignore if sleep was interrupted
}
}
/**
* Taken out to helper method to allow testing
*/
public DataMap bundleToDataMap(Bundle bundle) {
return DataMap.fromBundle(bundle);
}
}

View file

@ -0,0 +1,82 @@
package info.nightscout.androidaps.interaction.utils
import android.content.Context
import android.os.Bundle
import android.os.PowerManager
import com.google.android.gms.wearable.DataMap
import info.nightscout.shared.logging.AAPSLogger
import info.nightscout.shared.logging.LTag
import javax.inject.Inject
import javax.inject.Singleton
/**
* Created by andy on 3/5/19.
* Adapted by dlvoy on 2019-11-06 using code from jamorham JoH class
*/
@Singleton
class WearUtil @Inject constructor() {
@Inject lateinit var context: Context
@Inject lateinit var aapsLogger: AAPSLogger
private val debugWakelocks = false
private val rateLimits: MutableMap<String, Long> = HashMap()
//==============================================================================================
// Time related util methods
//==============================================================================================
fun timestamp(): Long {
return System.currentTimeMillis()
}
fun msSince(`when`: Long): Long {
return timestamp() - `when`
}
fun msTill(`when`: Long): Long {
return `when` - timestamp()
}
//==============================================================================================
// Thread and power management utils
//==============================================================================================
// return true if below rate limit
@Synchronized fun isBelowRateLimit(named: String, onceForSeconds: Int): Boolean {
// check if over limit
if (rateLimits.containsKey(named) && timestamp() - rateLimits[named]!! < onceForSeconds * 1000) {
aapsLogger.debug(LTag.WEAR, "$named rate limited to one for $onceForSeconds seconds")
return false
}
// not over limit
rateLimits[named] = timestamp()
return true
}
fun getWakeLock(name: String, millis: Int): PowerManager.WakeLock {
val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
val wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AAPS::$name")
wl.acquire(millis.toLong())
if (debugWakelocks) aapsLogger.debug(LTag.WEAR, "getWakeLock: $name $wl")
return wl
}
fun releaseWakeLock(wl: PowerManager.WakeLock?) {
if (debugWakelocks) aapsLogger.debug(LTag.WEAR, "releaseWakeLock: " + wl.toString())
if (wl?.isHeld == true) wl.release()
}
fun threadSleep(millis: Long) {
try {
Thread.sleep(millis)
} catch (e: InterruptedException) {
// we simply ignore if sleep was interrupted
}
}
/**
* Taken out to helper method to allow testing
*/
fun bundleToDataMap(bundle: Bundle?): DataMap {
return DataMap.fromBundle(bundle)
}
}

View file

@ -64,7 +64,7 @@ class WearUtilMocker(private val wearUtil: WearUtil) {
if (v is String) map.putString(key, v) if (v is String) map.putString(key, v)
if (v is Array<*>) map.putStringArray(key, v as Array<String>) if (v is Array<*>) map.putStringArray(key, v as Array<String>)
if (v is ArrayList<*>) { if (v is ArrayList<*>) {
if (!v.isEmpty()) { if (v.isNotEmpty()) {
if (v[0] is Int) { if (v[0] is Int) {
map.putIntegerArrayList(key, v as ArrayList<Int>) map.putIntegerArrayList(key, v as ArrayList<Int>)
} }