Merge pull request #1799 from Andries-Smit/chore/wear-to-kt
Chore: Wear OS Java to Kotlin
This commit is contained in:
commit
2d8e67e591
|
@ -7,6 +7,7 @@ import android.support.wearable.complications.ComplicationData
|
||||||
import android.support.wearable.complications.ComplicationText
|
import android.support.wearable.complications.ComplicationText
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
import info.nightscout.androidaps.data.RawDisplayData
|
import info.nightscout.androidaps.data.RawDisplayData
|
||||||
|
import info.nightscout.androidaps.interaction.utils.DisplayFormat
|
||||||
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
@ -25,8 +26,8 @@ class BrCobIobComplication : BaseComplicationProviderService() {
|
||||||
override fun buildComplicationData(dataType: Int, raw: RawDisplayData, complicationPendingIntent: PendingIntent): ComplicationData? {
|
override fun buildComplicationData(dataType: Int, raw: RawDisplayData, complicationPendingIntent: PendingIntent): ComplicationData? {
|
||||||
var complicationData: ComplicationData? = null
|
var complicationData: ComplicationData? = null
|
||||||
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
||||||
val cob = SmallestDoubleString(raw.status.cob, SmallestDoubleString.Units.USE).minimise(displayFormat.MIN_FIELD_LEN_COB)
|
val cob = SmallestDoubleString(raw.status.cob, SmallestDoubleString.Units.USE).minimise(DisplayFormat.MIN_FIELD_LEN_COB)
|
||||||
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(max(displayFormat.MIN_FIELD_LEN_IOB, displayFormat.MAX_FIELD_LEN_SHORT - 1 - cob.length))
|
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(max(DisplayFormat.MIN_FIELD_LEN_IOB, DisplayFormat.MAX_FIELD_LEN_SHORT - 1 - cob.length))
|
||||||
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
||||||
.setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.status.currentBasal))
|
.setShortText(ComplicationText.plainText(displayFormat.basalRateSymbol() + raw.status.currentBasal))
|
||||||
.setShortTitle(ComplicationText.plainText("$cob $iob"))
|
.setShortTitle(ComplicationText.plainText("$cob $iob"))
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.app.PendingIntent
|
||||||
import android.support.wearable.complications.ComplicationData
|
import android.support.wearable.complications.ComplicationData
|
||||||
import android.support.wearable.complications.ComplicationText
|
import android.support.wearable.complications.ComplicationText
|
||||||
import info.nightscout.androidaps.data.RawDisplayData
|
import info.nightscout.androidaps.data.RawDisplayData
|
||||||
|
import info.nightscout.androidaps.interaction.utils.DisplayFormat
|
||||||
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class CobIobComplication : BaseComplicationProviderService() {
|
||||||
var complicationData: ComplicationData? = null
|
var complicationData: ComplicationData? = null
|
||||||
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
||||||
val cob = raw.status.cob
|
val cob = raw.status.cob
|
||||||
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT)
|
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(DisplayFormat.MAX_FIELD_LEN_SHORT)
|
||||||
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
||||||
.setShortText(ComplicationText.plainText(cob))
|
.setShortText(ComplicationText.plainText(cob))
|
||||||
.setShortTitle(ComplicationText.plainText(iob))
|
.setShortTitle(ComplicationText.plainText(iob))
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.support.wearable.complications.ComplicationData
|
||||||
import android.support.wearable.complications.ComplicationText
|
import android.support.wearable.complications.ComplicationText
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.data.RawDisplayData
|
import info.nightscout.androidaps.data.RawDisplayData
|
||||||
|
import info.nightscout.androidaps.interaction.utils.DisplayFormat
|
||||||
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
import info.nightscout.androidaps.interaction.utils.SmallestDoubleString
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ class IobIconComplication : BaseComplicationProviderService() {
|
||||||
override fun buildComplicationData(dataType: Int, raw: RawDisplayData, complicationPendingIntent: PendingIntent): ComplicationData? {
|
override fun buildComplicationData(dataType: Int, raw: RawDisplayData, complicationPendingIntent: PendingIntent): ComplicationData? {
|
||||||
var complicationData: ComplicationData? = null
|
var complicationData: ComplicationData? = null
|
||||||
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
if (dataType == ComplicationData.TYPE_SHORT_TEXT) {
|
||||||
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(displayFormat.MAX_FIELD_LEN_SHORT)
|
val iob = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(DisplayFormat.MAX_FIELD_LEN_SHORT)
|
||||||
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
val builder = ComplicationData.Builder(ComplicationData.TYPE_SHORT_TEXT)
|
||||||
.setShortText(ComplicationText.plainText(iob))
|
.setShortText(ComplicationText.plainText(iob))
|
||||||
.setIcon(Icon.createWithResource(this, R.drawable.ic_ins))
|
.setIcon(Icon.createWithResource(this, R.drawable.ic_ins))
|
||||||
|
|
|
@ -15,7 +15,7 @@ class FillMenuActivity : MenuListActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElements(): List<MenuItem> =
|
override fun provideElements(): List<MenuItem> =
|
||||||
ArrayList<MenuItem>().apply {
|
ArrayList<MenuItem>().apply {
|
||||||
add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_1)))
|
add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_1)))
|
||||||
add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_2)))
|
add(MenuItem(R.drawable.ic_canula, getString(R.string.action_preset_2)))
|
||||||
|
|
|
@ -21,7 +21,7 @@ class MainMenuActivity : MenuListActivity() {
|
||||||
rxBus.send(EventWearToMobile(ActionResendData("MainMenuListActivity")))
|
rxBus.send(EventWearToMobile(ActionResendData("MainMenuListActivity")))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElements(): List<MenuItem> =
|
override fun provideElements(): List<MenuItem> =
|
||||||
ArrayList<MenuItem>().apply {
|
ArrayList<MenuItem>().apply {
|
||||||
if (!sp.getBoolean(R.string.key_wear_control, false)) {
|
if (!sp.getBoolean(R.string.key_wear_control, false)) {
|
||||||
add(MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings)))
|
add(MenuItem(R.drawable.ic_settings, getString(R.string.menu_settings)))
|
||||||
|
|
|
@ -15,7 +15,7 @@ class StatusMenuActivity : MenuListActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getElements(): List<MenuItem> =
|
override fun provideElements(): List<MenuItem> =
|
||||||
ArrayList<MenuItem>().apply {
|
ArrayList<MenuItem>().apply {
|
||||||
add(MenuItem(R.drawable.ic_status, getString(R.string.status_pump)))
|
add(MenuItem(R.drawable.ic_status, getString(R.string.status_pump)))
|
||||||
add(MenuItem(R.drawable.ic_loop_closed, getString(R.string.status_loop)))
|
add(MenuItem(R.drawable.ic_loop_closed, getString(R.string.status_loop)))
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
package info.nightscout.androidaps.interaction.utils;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import info.nightscout.androidaps.data.RawDisplayData;
|
|
||||||
import info.nightscout.shared.sharedPreferences.SP;
|
|
||||||
|
|
||||||
@Singleton
|
|
||||||
public class DisplayFormat {
|
|
||||||
|
|
||||||
@Inject SP sp;
|
|
||||||
@Inject WearUtil wearUtil;
|
|
||||||
|
|
||||||
@Inject DisplayFormat() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximal and minimal lengths of fields/labels shown in complications, in characters
|
|
||||||
* For MAX values - above that WearOS and watch faces may start ellipsize (...) contents
|
|
||||||
* For MIN values - this is minimal length that can hold legible data
|
|
||||||
*/
|
|
||||||
public final int MAX_FIELD_LEN_LONG = 22; // this is found out empirical, for TYPE_LONG_TEXT
|
|
||||||
public final int MAX_FIELD_LEN_SHORT = 7; // according to Wear OS docs for TYPE_SHORT_TEXT
|
|
||||||
public final int MIN_FIELD_LEN_COB = 3; // since carbs are usually 0..99g
|
|
||||||
public final int MIN_FIELD_LEN_IOB = 3; // IoB can range from like .1U to 99U
|
|
||||||
|
|
||||||
private boolean areComplicationsUnicode() {
|
|
||||||
return sp.getBoolean("complication_unicode", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String deltaSymbol() {
|
|
||||||
return areComplicationsUnicode() ? "\u0394" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String verticalSeparatorSymbol() {
|
|
||||||
return areComplicationsUnicode() ? "\u205E" : "|";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String basalRateSymbol() {
|
|
||||||
return areComplicationsUnicode() ? "\u238D\u2006" : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String shortTimeSince(final long refTime) {
|
|
||||||
|
|
||||||
long deltaTimeMs = wearUtil.msSince(refTime);
|
|
||||||
|
|
||||||
if (deltaTimeMs < Constants.MINUTE_IN_MS) {
|
|
||||||
return "0'";
|
|
||||||
} else if (deltaTimeMs < Constants.HOUR_IN_MS) {
|
|
||||||
int minutes = (int) (deltaTimeMs / Constants.MINUTE_IN_MS);
|
|
||||||
return minutes + "'";
|
|
||||||
} else if (deltaTimeMs < Constants.DAY_IN_MS) {
|
|
||||||
int hours = (int) (deltaTimeMs / Constants.HOUR_IN_MS);
|
|
||||||
return hours + "h";
|
|
||||||
} else {
|
|
||||||
int days = (int) (deltaTimeMs / Constants.DAY_IN_MS);
|
|
||||||
if (days < 7) {
|
|
||||||
return days + "d";
|
|
||||||
} else {
|
|
||||||
int weeks = days / 7;
|
|
||||||
return weeks + "w";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String shortTrend(final RawDisplayData raw) {
|
|
||||||
String minutes = "--";
|
|
||||||
if (raw.getSingleBg().getTimeStamp() > 0) {
|
|
||||||
minutes = shortTimeSince(raw.getSingleBg().getTimeStamp());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (minutes.length() + raw.getSingleBg().getDelta().length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) {
|
|
||||||
return minutes + " " + deltaSymbol() + raw.getSingleBg().getDelta();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
if (minutes.length() + delta.length() + deltaSymbol().length() + 1 <= MAX_FIELD_LEN_SHORT) {
|
|
||||||
return minutes + " " + deltaSymbol() + delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
String shortDelta = (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(MAX_FIELD_LEN_SHORT - (1 + minutes.length()));
|
|
||||||
|
|
||||||
return minutes + " " + shortDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String longGlucoseLine(final RawDisplayData raw) {
|
|
||||||
return raw.getSingleBg().getSgvString() + raw.getSingleBg().getSlopeArrow() + " " + deltaSymbol() + (new SmallestDoubleString(raw.getSingleBg().getDelta())).minimise(8) + " (" + shortTimeSince(raw.getSingleBg().getTimeStamp()) + ")";
|
|
||||||
}
|
|
||||||
|
|
||||||
public String longDetailsLine(final RawDisplayData raw) {
|
|
||||||
|
|
||||||
final String SEP_LONG = " " + verticalSeparatorSymbol() + " ";
|
|
||||||
final String SEP_SHORT = " " + verticalSeparatorSymbol() + " ";
|
|
||||||
final int SEP_SHORT_LEN = SEP_SHORT.length();
|
|
||||||
final String SEP_MIN = " ";
|
|
||||||
|
|
||||||
String line =
|
|
||||||
raw.getStatus().getCob() + SEP_LONG + raw.getStatus().getIobSum() + SEP_LONG + basalRateSymbol() + raw.getStatus().getCurrentBasal();
|
|
||||||
if (line.length() <= MAX_FIELD_LEN_LONG) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
line = raw.getStatus().getCob() + SEP_SHORT + raw.getStatus().getIobSum() + SEP_SHORT + raw.getStatus().getCurrentBasal();
|
|
||||||
if (line.length() <= MAX_FIELD_LEN_LONG) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
line = raw.getStatus().getCob() + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal();
|
|
||||||
if (line.length() <= MAX_FIELD_LEN_LONG) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
line = simplifiedCob + SEP_SHORT + smallestIoB + SEP_SHORT + raw.getStatus().getCurrentBasal();
|
|
||||||
if (line.length() <= MAX_FIELD_LEN_LONG) {
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
line = simplifiedCob + SEP_MIN + smallestIoB + SEP_MIN + raw.getStatus().getCurrentBasal();
|
|
||||||
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pair<String, String> detailedIob(RawDisplayData raw) {
|
|
||||||
final String iob1 = new SmallestDoubleString(raw.getStatus().getIobSum(), SmallestDoubleString.Units.USE).minimise(MAX_FIELD_LEN_SHORT);
|
|
||||||
String iob2 = "";
|
|
||||||
if (raw.getStatus().getIobDetail().contains("|")) {
|
|
||||||
String[] iobs = raw.getStatus().getIobDetail().replace("(", "").replace(")", "").split("\\|");
|
|
||||||
|
|
||||||
String iobBolus = new SmallestDoubleString(iobs[0]).minimise(MIN_FIELD_LEN_IOB);
|
|
||||||
if (iobBolus.trim().length() == 0) {
|
|
||||||
iobBolus = "--";
|
|
||||||
}
|
|
||||||
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) {
|
|
||||||
iobBasal = "--";
|
|
||||||
}
|
|
||||||
iob2 = iobBolus + " " + iobBasal;
|
|
||||||
}
|
|
||||||
return Pair.Companion.create(iob1, iob2);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pair<String, String> detailedCob(final RawDisplayData raw) {
|
|
||||||
SmallestDoubleString cobMini = new SmallestDoubleString(raw.getStatus().getCob(), SmallestDoubleString.Units.USE);
|
|
||||||
|
|
||||||
String cob2 = "";
|
|
||||||
if (cobMini.getExtra().length() > 0) {
|
|
||||||
cob2 = cobMini.getExtra() + cobMini.getUnits();
|
|
||||||
}
|
|
||||||
final String cob1 = cobMini.minimise(MAX_FIELD_LEN_SHORT);
|
|
||||||
return Pair.Companion.create(cob1, cob2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
|
import info.nightscout.androidaps.interaction.utils.Pair.Companion.create
|
||||||
|
import javax.inject.Singleton
|
||||||
|
import javax.inject.Inject
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import info.nightscout.androidaps.data.RawDisplayData
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class DisplayFormat @Inject internal constructor() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
const val MAX_FIELD_LEN_LONG = 22 // this is found out empirical, for TYPE_LONG_TEXT
|
||||||
|
const val MAX_FIELD_LEN_SHORT = 7 // according to Wear OS docs for TYPE_SHORT_TEXT
|
||||||
|
const val MIN_FIELD_LEN_COB = 3 // since carbs are usually 0..99g
|
||||||
|
const val MIN_FIELD_LEN_IOB = 3 // IoB can range from like .1U to 99U
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var wearUtil: WearUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximal and minimal lengths of fields/labels shown in complications, in characters
|
||||||
|
* For MAX values - above that WearOS and watch faces may start ellipsize (...) contents
|
||||||
|
* For MIN values - this is minimal length that can hold legible data
|
||||||
|
*/
|
||||||
|
|
||||||
|
private fun areComplicationsUnicode() = sp.getBoolean("complication_unicode", true)
|
||||||
|
|
||||||
|
private fun deltaSymbol() = if (areComplicationsUnicode()) "\u0394" else ""
|
||||||
|
|
||||||
|
private fun verticalSeparatorSymbol() = if (areComplicationsUnicode()) "\u205E" else "|"
|
||||||
|
|
||||||
|
fun basalRateSymbol() = if (areComplicationsUnicode()) "\u238D\u2006" else ""
|
||||||
|
|
||||||
|
fun shortTimeSince(refTime: Long): String {
|
||||||
|
val deltaTimeMs = wearUtil.msSince(refTime)
|
||||||
|
return if (deltaTimeMs < Constants.MINUTE_IN_MS) {
|
||||||
|
"0'"
|
||||||
|
} else if (deltaTimeMs < Constants.HOUR_IN_MS) {
|
||||||
|
val minutes = (deltaTimeMs / Constants.MINUTE_IN_MS).toInt()
|
||||||
|
"$minutes'"
|
||||||
|
} else if (deltaTimeMs < Constants.DAY_IN_MS) {
|
||||||
|
val hours = (deltaTimeMs / Constants.HOUR_IN_MS).toInt()
|
||||||
|
hours.toString() + "h"
|
||||||
|
} else {
|
||||||
|
val days = (deltaTimeMs / Constants.DAY_IN_MS).toInt()
|
||||||
|
if (days < 7) {
|
||||||
|
days.toString() + "d"
|
||||||
|
} else {
|
||||||
|
val weeks = days / 7
|
||||||
|
weeks.toString() + "w"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shortTrend(raw: RawDisplayData): String {
|
||||||
|
var minutes = "--"
|
||||||
|
if (raw.singleBg.timeStamp > 0) {
|
||||||
|
minutes = shortTimeSince(raw.singleBg.timeStamp)
|
||||||
|
}
|
||||||
|
if (minutes.length + raw.singleBg.delta.length + deltaSymbol().length + 1 <= MAX_FIELD_LEN_SHORT) {
|
||||||
|
return minutes + " " + deltaSymbol() + raw.singleBg.delta
|
||||||
|
}
|
||||||
|
|
||||||
|
// that only optimizes obvious things like 0 before . or at end, + at beginning
|
||||||
|
val delta = SmallestDoubleString(raw.singleBg.delta).minimise(MAX_FIELD_LEN_SHORT - 1)
|
||||||
|
if (minutes.length + delta.length + deltaSymbol().length + 1 <= MAX_FIELD_LEN_SHORT) {
|
||||||
|
return minutes + " " + deltaSymbol() + delta
|
||||||
|
}
|
||||||
|
val shortDelta = SmallestDoubleString(raw.singleBg.delta).minimise(MAX_FIELD_LEN_SHORT - (1 + minutes.length))
|
||||||
|
return "$minutes $shortDelta"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun longGlucoseLine(raw: RawDisplayData): String {
|
||||||
|
return raw.singleBg.sgvString + raw.singleBg.slopeArrow + " " + deltaSymbol() + SmallestDoubleString(raw.singleBg.delta).minimise(8) + " (" + shortTimeSince(raw.singleBg.timeStamp) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun longDetailsLine(raw: RawDisplayData): String {
|
||||||
|
val sepLong = " " + verticalSeparatorSymbol() + " "
|
||||||
|
val sepShort = " " + verticalSeparatorSymbol() + " "
|
||||||
|
val sepShortLen = sepShort.length
|
||||||
|
val sepMin = " "
|
||||||
|
var line = raw.status.cob + sepLong + raw.status.iobSum + sepLong + basalRateSymbol() + raw.status.currentBasal
|
||||||
|
if (line.length <= MAX_FIELD_LEN_LONG) {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
line = raw.status.cob + sepShort + raw.status.iobSum + sepShort + raw.status.currentBasal
|
||||||
|
if (line.length <= MAX_FIELD_LEN_LONG) {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
var remainingMax = MAX_FIELD_LEN_LONG - (raw.status.cob.length + raw.status.currentBasal.length + sepShortLen * 2)
|
||||||
|
val smallestIoB = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(max(MIN_FIELD_LEN_IOB, remainingMax))
|
||||||
|
line = raw.status.cob + sepShort + smallestIoB + sepShort + raw.status.currentBasal
|
||||||
|
if (line.length <= MAX_FIELD_LEN_LONG) {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
remainingMax = MAX_FIELD_LEN_LONG - (smallestIoB.length + raw.status.currentBasal.length + sepShortLen * 2)
|
||||||
|
val simplifiedCob = SmallestDoubleString(raw.status.cob, SmallestDoubleString.Units.USE).minimise(max(MIN_FIELD_LEN_COB, remainingMax))
|
||||||
|
line = simplifiedCob + sepShort + smallestIoB + sepShort + raw.status.currentBasal
|
||||||
|
if (line.length <= MAX_FIELD_LEN_LONG) {
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
line = simplifiedCob + sepMin + smallestIoB + sepMin + raw.status.currentBasal
|
||||||
|
return line
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detailedIob(raw: RawDisplayData): Pair<String, String> {
|
||||||
|
val iob1 = SmallestDoubleString(raw.status.iobSum, SmallestDoubleString.Units.USE).minimise(MAX_FIELD_LEN_SHORT)
|
||||||
|
var iob2 = ""
|
||||||
|
if (raw.status.iobDetail.contains("|")) {
|
||||||
|
val iobs = raw.status.iobDetail.replace("(", "").replace(")", "").split("|").toTypedArray()
|
||||||
|
var iobBolus = SmallestDoubleString(iobs[0]).minimise(MIN_FIELD_LEN_IOB)
|
||||||
|
if (iobBolus.trim().isEmpty()) {
|
||||||
|
iobBolus = "--"
|
||||||
|
}
|
||||||
|
var iobBasal = SmallestDoubleString(iobs[1]).minimise(MAX_FIELD_LEN_SHORT - 1 - max(MIN_FIELD_LEN_IOB, iobBolus.length))
|
||||||
|
if (iobBasal.trim().isEmpty()) {
|
||||||
|
iobBasal = "--"
|
||||||
|
}
|
||||||
|
iob2 = "$iobBolus $iobBasal"
|
||||||
|
}
|
||||||
|
return create(iob1, iob2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun detailedCob(raw: RawDisplayData): Pair<String, String> {
|
||||||
|
val cobMini = SmallestDoubleString(raw.status.cob, SmallestDoubleString.Units.USE)
|
||||||
|
var cob2 = ""
|
||||||
|
if (cobMini.extra.isNotEmpty()) {
|
||||||
|
cob2 = cobMini.extra + cobMini.units
|
||||||
|
}
|
||||||
|
val cob1 = cobMini.minimise(MAX_FIELD_LEN_SHORT)
|
||||||
|
return create(cob1, cob2)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,160 +0,0 @@
|
||||||
package info.nightscout.androidaps.interaction.utils;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
import androidx.wear.widget.CurvedTextView;
|
|
||||||
import androidx.wear.widget.WearableLinearLayoutManager;
|
|
||||||
import androidx.wear.widget.WearableRecyclerView;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import dagger.android.DaggerActivity;
|
|
||||||
import info.nightscout.androidaps.R;
|
|
||||||
import info.nightscout.androidaps.plugins.bus.RxBus;
|
|
||||||
import info.nightscout.shared.sharedPreferences.SP;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by adrian on 08/02/17.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public abstract class MenuListActivity extends DaggerActivity {
|
|
||||||
|
|
||||||
@Inject public RxBus rxBus;
|
|
||||||
@Inject public SP sp;
|
|
||||||
|
|
||||||
List<MenuItem> elements;
|
|
||||||
|
|
||||||
protected abstract List<MenuItem> getElements();
|
|
||||||
|
|
||||||
protected abstract void doAction(String position);
|
|
||||||
|
|
||||||
public interface AdapterCallback {
|
|
||||||
void onItemClicked(MenuAdapter.ItemViewHolder v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.actions_list_activity);
|
|
||||||
setTitleBasedOnScreenShape(String.valueOf(getTitle()));
|
|
||||||
|
|
||||||
elements = getElements();
|
|
||||||
CustomScrollingLayoutCallback customScrollingLayoutCallback = new CustomScrollingLayoutCallback();
|
|
||||||
WearableLinearLayoutManager layoutManager = new WearableLinearLayoutManager(this);
|
|
||||||
WearableRecyclerView listView = findViewById(R.id.action_list);
|
|
||||||
boolean isScreenRound = this.getResources().getConfiguration().isScreenRound();
|
|
||||||
if (isScreenRound) {
|
|
||||||
layoutManager.setLayoutCallback(customScrollingLayoutCallback);
|
|
||||||
listView.setEdgeItemsCenteringEnabled(true);
|
|
||||||
} else {
|
|
||||||
// Bug in androidx.wear:wear:1.2.0
|
|
||||||
// WearableRecyclerView setEdgeItemsCenteringEnabled requires fix for square screen
|
|
||||||
listView.setPadding(0, 50, 0, 0);
|
|
||||||
}
|
|
||||||
listView.setHasFixedSize(true);
|
|
||||||
listView.setLayoutManager(layoutManager);
|
|
||||||
listView.setAdapter(new MenuAdapter(elements, v -> {
|
|
||||||
String tag = (String) v.itemView.getTag();
|
|
||||||
doAction(tag);
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setTitleBasedOnScreenShape(String title) {
|
|
||||||
CurvedTextView titleViewCurved = findViewById(R.id.title_curved);
|
|
||||||
TextView titleView = findViewById(R.id.title);
|
|
||||||
if (this.getResources().getConfiguration().isScreenRound()) {
|
|
||||||
titleViewCurved.setText(title);
|
|
||||||
titleViewCurved.setVisibility(View.VISIBLE);
|
|
||||||
titleView.setVisibility((View.GONE));
|
|
||||||
} else {
|
|
||||||
titleView.setText(title);
|
|
||||||
titleView.setVisibility(View.VISIBLE);
|
|
||||||
titleViewCurved.setVisibility((View.GONE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MenuAdapter extends RecyclerView.Adapter<MenuAdapter.ItemViewHolder> {
|
|
||||||
private final List<MenuItem> mDataset;
|
|
||||||
private final AdapterCallback callback;
|
|
||||||
|
|
||||||
public MenuAdapter(List<MenuItem> dataset, AdapterCallback callback) {
|
|
||||||
mDataset = dataset;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ItemViewHolder extends RecyclerView.ViewHolder {
|
|
||||||
protected final RelativeLayout menuContainer;
|
|
||||||
protected final TextView actionItem;
|
|
||||||
protected final ImageView actionIcon;
|
|
||||||
|
|
||||||
public ItemViewHolder(View itemView) {
|
|
||||||
super(itemView);
|
|
||||||
menuContainer = itemView.findViewById(R.id.menu_container);
|
|
||||||
actionItem = itemView.findViewById(R.id.menuItemText);
|
|
||||||
actionIcon = itemView.findViewById(R.id.menuItemIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull @Override
|
|
||||||
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
|
|
||||||
|
|
||||||
return new ItemViewHolder(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(ItemViewHolder holder, final int position) {
|
|
||||||
MenuItem item = mDataset.get(position);
|
|
||||||
holder.actionItem.setText(item.actionItem);
|
|
||||||
holder.actionIcon.setImageResource(item.actionIcon);
|
|
||||||
holder.itemView.setTag(item.actionItem);
|
|
||||||
holder.menuContainer.setOnClickListener(v -> callback.onItemClicked(holder));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemCount() {
|
|
||||||
return mDataset.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class MenuItem {
|
|
||||||
public MenuItem(int actionIcon, String actionItem) {
|
|
||||||
this.actionIcon = actionIcon;
|
|
||||||
this.actionItem = actionItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int actionIcon;
|
|
||||||
public String actionItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class CustomScrollingLayoutCallback extends WearableLinearLayoutManager.LayoutCallback {
|
|
||||||
// How much should we scale the icon at most.
|
|
||||||
private static final float MAX_ICON_PROGRESS = 0.65f;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onLayoutFinished(View child, RecyclerView parent) {
|
|
||||||
// Figure out % progress from top to bottom
|
|
||||||
float centerOffset = ((float) child.getHeight() / 2.0f) / (float) parent.getHeight();
|
|
||||||
float yRelativeToCenterOffset = (child.getY() / parent.getHeight()) + centerOffset;
|
|
||||||
|
|
||||||
// Normalize for center
|
|
||||||
float progressToCenter = Math.abs(0.5f - yRelativeToCenterOffset);
|
|
||||||
// Adjust to the maximum scale
|
|
||||||
progressToCenter = Math.min(progressToCenter, MAX_ICON_PROGRESS);
|
|
||||||
|
|
||||||
child.setScaleX(1 - progressToCenter);
|
|
||||||
child.setScaleY(1 - progressToCenter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
|
import dagger.android.DaggerActivity
|
||||||
|
import javax.inject.Inject
|
||||||
|
import info.nightscout.androidaps.plugins.bus.RxBus
|
||||||
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
|
import info.nightscout.androidaps.interaction.utils.MenuListActivity.MenuAdapter.ItemViewHolder
|
||||||
|
import android.os.Bundle
|
||||||
|
import info.nightscout.androidaps.R
|
||||||
|
import androidx.wear.widget.WearableLinearLayoutManager
|
||||||
|
import androidx.wear.widget.CurvedTextView
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.wear.widget.WearableLinearLayoutManager.LayoutCallback
|
||||||
|
import androidx.wear.widget.WearableRecyclerView
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by adrian on 08/02/17.
|
||||||
|
*/
|
||||||
|
abstract class MenuListActivity : DaggerActivity() {
|
||||||
|
|
||||||
|
@Inject lateinit var sp: SP
|
||||||
|
@Inject lateinit var rxBus: RxBus
|
||||||
|
|
||||||
|
private var elements: List<MenuItem> = listOf()
|
||||||
|
protected abstract fun provideElements(): List<MenuItem>
|
||||||
|
protected abstract fun doAction(position: String)
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.actions_list_activity)
|
||||||
|
setTitleBasedOnScreenShape(title.toString())
|
||||||
|
elements = provideElements()
|
||||||
|
val customScrollingLayoutCallback = CustomScrollingLayoutCallback()
|
||||||
|
val layoutManager = WearableLinearLayoutManager(this)
|
||||||
|
val listView = findViewById<WearableRecyclerView>(R.id.action_list)
|
||||||
|
val isScreenRound = this.resources.configuration.isScreenRound
|
||||||
|
if (isScreenRound) {
|
||||||
|
layoutManager.layoutCallback = customScrollingLayoutCallback
|
||||||
|
listView.isEdgeItemsCenteringEnabled = true
|
||||||
|
} else {
|
||||||
|
// Bug in androidx.wear:wear:1.2.0
|
||||||
|
// WearableRecyclerView setEdgeItemsCenteringEnabled requires fix for square screen
|
||||||
|
listView.setPadding(0, 50, 0, 0)
|
||||||
|
}
|
||||||
|
listView.setHasFixedSize(true)
|
||||||
|
listView.layoutManager = layoutManager
|
||||||
|
listView.adapter = MenuAdapter(elements) { v: ItemViewHolder ->
|
||||||
|
val tag = v.itemView.tag as String
|
||||||
|
doAction(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTitleBasedOnScreenShape(title: String) {
|
||||||
|
val titleViewCurved = findViewById<CurvedTextView>(R.id.title_curved)
|
||||||
|
val titleView = findViewById<TextView>(R.id.title)
|
||||||
|
if (this.resources.configuration.isScreenRound) {
|
||||||
|
titleViewCurved.text = title
|
||||||
|
titleViewCurved.visibility = View.VISIBLE
|
||||||
|
titleView.visibility = View.GONE
|
||||||
|
} else {
|
||||||
|
titleView.text = title
|
||||||
|
titleView.visibility = View.VISIBLE
|
||||||
|
titleViewCurved.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MenuAdapter(private val mDataset: List<MenuItem>, private val callback: (ItemViewHolder) -> Unit) : RecyclerView.Adapter<ItemViewHolder>() {
|
||||||
|
class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
|
val menuContainer: RelativeLayout
|
||||||
|
val actionItem: TextView
|
||||||
|
val actionIcon: ImageView
|
||||||
|
|
||||||
|
init {
|
||||||
|
menuContainer = itemView.findViewById(R.id.menu_container)
|
||||||
|
actionItem = itemView.findViewById(R.id.menuItemText)
|
||||||
|
actionIcon = itemView.findViewById(R.id.menuItemIcon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
|
||||||
|
val view = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
|
||||||
|
return ItemViewHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
|
||||||
|
val item = mDataset[position]
|
||||||
|
holder.actionItem.text = item.actionItem
|
||||||
|
holder.actionIcon.setImageResource(item.actionIcon)
|
||||||
|
holder.itemView.tag = item.actionItem
|
||||||
|
holder.menuContainer.setOnClickListener { callback(holder) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int {
|
||||||
|
return mDataset.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class MenuItem(var actionIcon: Int, var actionItem: String)
|
||||||
|
class CustomScrollingLayoutCallback : LayoutCallback() {
|
||||||
|
|
||||||
|
override fun onLayoutFinished(child: View, parent: RecyclerView) {
|
||||||
|
// Figure out % progress from top to bottom
|
||||||
|
val centerOffset = child.height.toFloat() / 2.0f / parent.height.toFloat()
|
||||||
|
val yRelativeToCenterOffset = child.y / parent.height + centerOffset
|
||||||
|
|
||||||
|
// Normalize for center
|
||||||
|
var progressToCenter = abs(0.5f - yRelativeToCenterOffset)
|
||||||
|
// Adjust to the maximum scale
|
||||||
|
progressToCenter = min(progressToCenter, MAX_ICON_PROGRESS)
|
||||||
|
child.scaleX = 1 - progressToCenter
|
||||||
|
child.scaleY = 1 - progressToCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
// How much should we scale the icon at most.
|
||||||
|
private const val MAX_ICON_PROGRESS = 0.65f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
package info.nightscout.androidaps.interaction.utils
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import info.nightscout.androidaps.utils.DateUtil
|
import info.nightscout.androidaps.utils.DateUtil
|
||||||
import info.nightscout.shared.logging.AAPSLogger
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
|
|
|
@ -1,235 +0,0 @@
|
||||||
package info.nightscout.androidaps.interaction.utils;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import androidx.core.view.InputDeviceCompat;
|
|
||||||
import androidx.core.view.MotionEventCompat;
|
|
||||||
|
|
||||||
import java.text.NumberFormat;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by mike on 28.06.2016.
|
|
||||||
*/
|
|
||||||
public class PlusMinusEditText implements View.OnKeyListener,
|
|
||||||
View.OnTouchListener, View.OnClickListener, View.OnGenericMotionListener {
|
|
||||||
|
|
||||||
public TextView editText;
|
|
||||||
ImageView minusImage;
|
|
||||||
ImageView plusImage;
|
|
||||||
|
|
||||||
Double value;
|
|
||||||
Double minValue;
|
|
||||||
Double maxValue;
|
|
||||||
Double step;
|
|
||||||
NumberFormat formatter;
|
|
||||||
boolean allowZero;
|
|
||||||
boolean roundRobin;
|
|
||||||
|
|
||||||
private int mChangeCounter = 0;
|
|
||||||
private long mLastChange = 0;
|
|
||||||
private final static int THRESHOLD_COUNTER = 5;
|
|
||||||
private final static int THRESHOLD_COUNTER_LONG = 10;
|
|
||||||
private final static int THRESHOLD_TIME = 100;
|
|
||||||
|
|
||||||
private final Handler mHandler;
|
|
||||||
private ScheduledExecutorService mUpdater;
|
|
||||||
|
|
||||||
private class UpdateCounterTask implements Runnable {
|
|
||||||
private final boolean mInc;
|
|
||||||
private int repeated = 0;
|
|
||||||
private int multiplier = 1;
|
|
||||||
|
|
||||||
public UpdateCounterTask(boolean inc) {
|
|
||||||
mInc = inc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
Message msg = new Message();
|
|
||||||
int doubleLimit = 5;
|
|
||||||
if (repeated % doubleLimit == 0) multiplier *= 2;
|
|
||||||
repeated++;
|
|
||||||
msg.arg1 = multiplier;
|
|
||||||
msg.arg2 = repeated;
|
|
||||||
if (mInc) {
|
|
||||||
msg.what = MSG_INC;
|
|
||||||
} else {
|
|
||||||
msg.what = MSG_DEC;
|
|
||||||
}
|
|
||||||
mHandler.sendMessage(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int MSG_INC = 0;
|
|
||||||
private static final int MSG_DEC = 1;
|
|
||||||
|
|
||||||
public PlusMinusEditText(View view, int editTextID, int plusID, int minusID, Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero) {
|
|
||||||
this(view, editTextID, plusID, minusID, initValue, minValue, maxValue, step, formatter, allowZero, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlusMinusEditText(View view, int editTextID, int plusID, int minusID, Double initValue, Double minValue, Double maxValue, Double step, NumberFormat formatter, boolean allowZero, boolean roundRobin) {
|
|
||||||
editText = view.findViewById(editTextID);
|
|
||||||
minusImage = view.findViewById(minusID);
|
|
||||||
plusImage = view.findViewById(plusID);
|
|
||||||
|
|
||||||
this.value = initValue;
|
|
||||||
this.minValue = minValue;
|
|
||||||
this.maxValue = maxValue;
|
|
||||||
this.step = step;
|
|
||||||
this.formatter = formatter;
|
|
||||||
this.allowZero = allowZero;
|
|
||||||
this.roundRobin = roundRobin;
|
|
||||||
|
|
||||||
mHandler = new Handler(Looper.getMainLooper()) {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case MSG_INC:
|
|
||||||
inc(msg.arg1);
|
|
||||||
return;
|
|
||||||
case MSG_DEC:
|
|
||||||
dec(msg.arg1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.handleMessage(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
minusImage.setOnTouchListener(this);
|
|
||||||
minusImage.setOnKeyListener(this);
|
|
||||||
minusImage.setOnClickListener(this);
|
|
||||||
plusImage.setOnTouchListener(this);
|
|
||||||
plusImage.setOnKeyListener(this);
|
|
||||||
plusImage.setOnClickListener(this);
|
|
||||||
editText.setOnGenericMotionListener(this);
|
|
||||||
updateEditText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(Double value) {
|
|
||||||
this.value = value;
|
|
||||||
updateEditText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void inc(int multiplier) {
|
|
||||||
value += step * multiplier;
|
|
||||||
if (value > maxValue) {
|
|
||||||
if (roundRobin) {
|
|
||||||
value = minValue;
|
|
||||||
} else {
|
|
||||||
value = maxValue;
|
|
||||||
stopUpdating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateEditText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dec(int multiplier) {
|
|
||||||
value -= step * multiplier;
|
|
||||||
if (value < minValue) {
|
|
||||||
if (roundRobin) {
|
|
||||||
value = maxValue;
|
|
||||||
} else {
|
|
||||||
value = minValue;
|
|
||||||
stopUpdating();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateEditText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateEditText() {
|
|
||||||
if (value == 0d && !allowZero)
|
|
||||||
editText.setText("");
|
|
||||||
else
|
|
||||||
editText.setText(formatter.format(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startUpdating(boolean inc) {
|
|
||||||
if (mUpdater != null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mUpdater = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
mUpdater.scheduleAtFixedRate(new UpdateCounterTask(inc), 200, 200,
|
|
||||||
TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopUpdating() {
|
|
||||||
if (mUpdater != null) {
|
|
||||||
mUpdater.shutdownNow();
|
|
||||||
mUpdater = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (mUpdater == null) {
|
|
||||||
if (v == plusImage) {
|
|
||||||
inc(1);
|
|
||||||
} else {
|
|
||||||
dec(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
||||||
boolean isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER;
|
|
||||||
boolean isReleased = event.getAction() == KeyEvent.ACTION_UP;
|
|
||||||
boolean isPressed = event.getAction() == KeyEvent.ACTION_DOWN;
|
|
||||||
|
|
||||||
if (isKeyOfInterest && isReleased) {
|
|
||||||
stopUpdating();
|
|
||||||
} else if (isKeyOfInterest && isPressed) {
|
|
||||||
startUpdating(v == plusImage);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
|
||||||
boolean isReleased = event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL;
|
|
||||||
boolean isPressed = event.getAction() == MotionEvent.ACTION_DOWN;
|
|
||||||
|
|
||||||
if (isReleased) {
|
|
||||||
stopUpdating();
|
|
||||||
} else if (isPressed) {
|
|
||||||
startUpdating(v == plusImage);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onGenericMotion(View v, MotionEvent ev) {
|
|
||||||
if (ev.getAction() == MotionEvent.ACTION_SCROLL && ev.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER)) {
|
|
||||||
long now = System.currentTimeMillis();
|
|
||||||
if (now - mLastChange > THRESHOLD_TIME) mChangeCounter = 0;
|
|
||||||
|
|
||||||
int dynamicMultiplier = mChangeCounter < THRESHOLD_COUNTER ? 1 :
|
|
||||||
mChangeCounter < THRESHOLD_COUNTER_LONG ? 2 : 4;
|
|
||||||
|
|
||||||
float delta = -ev.getAxisValue(MotionEventCompat.AXIS_SCROLL);
|
|
||||||
if (delta > 0) {
|
|
||||||
inc(dynamicMultiplier);
|
|
||||||
} else {
|
|
||||||
dec(dynamicMultiplier);
|
|
||||||
}
|
|
||||||
mLastChange = System.currentTimeMillis();
|
|
||||||
mChangeCounter++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,217 @@
|
||||||
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
|
import android.os.Handler
|
||||||
|
import kotlin.jvm.JvmOverloads
|
||||||
|
import android.view.View.OnTouchListener
|
||||||
|
import android.view.View.OnGenericMotionListener
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.os.Looper
|
||||||
|
import android.os.Message
|
||||||
|
import android.view.KeyEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.core.view.InputDeviceCompat
|
||||||
|
import androidx.core.view.MotionEventCompat
|
||||||
|
import java.text.NumberFormat
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.ScheduledExecutorService
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by mike on 28.06.2016.
|
||||||
|
*/
|
||||||
|
class PlusMinusEditText @JvmOverloads constructor(
|
||||||
|
view: View,
|
||||||
|
editTextID: Int,
|
||||||
|
plusID: Int,
|
||||||
|
minusID: Int,
|
||||||
|
initValue: Double,
|
||||||
|
minValue: Double,
|
||||||
|
maxValue: Double,
|
||||||
|
step: Double,
|
||||||
|
formatter: NumberFormat,
|
||||||
|
allowZero: Boolean,
|
||||||
|
roundRobin: Boolean = false
|
||||||
|
) : View.OnKeyListener, OnTouchListener, View.OnClickListener, OnGenericMotionListener {
|
||||||
|
|
||||||
|
var editText: TextView
|
||||||
|
private set
|
||||||
|
private var minusImage: ImageView
|
||||||
|
private var plusImage: ImageView
|
||||||
|
private var value: Double
|
||||||
|
private var minValue: Double
|
||||||
|
private var maxValue: Double
|
||||||
|
private var step: Double
|
||||||
|
private var formatter: NumberFormat
|
||||||
|
private var allowZero: Boolean
|
||||||
|
private var roundRobin: Boolean
|
||||||
|
private var mChangeCounter = 0
|
||||||
|
private var mLastChange: Long = 0
|
||||||
|
private val mHandler: Handler
|
||||||
|
private var mUpdater: ScheduledExecutorService? = null
|
||||||
|
|
||||||
|
private inner class UpdateCounterTask(private val mInc: Boolean) : Runnable {
|
||||||
|
|
||||||
|
private var repeated = 0
|
||||||
|
private var multiplier = 1
|
||||||
|
override fun run() {
|
||||||
|
val msg = Message()
|
||||||
|
val doubleLimit = 5
|
||||||
|
if (repeated % doubleLimit == 0) multiplier *= 2
|
||||||
|
repeated++
|
||||||
|
msg.arg1 = multiplier
|
||||||
|
msg.arg2 = repeated
|
||||||
|
if (mInc) {
|
||||||
|
msg.what = MSG_INC
|
||||||
|
} else {
|
||||||
|
msg.what = MSG_DEC
|
||||||
|
}
|
||||||
|
mHandler.sendMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun inc(multiplier: Int) {
|
||||||
|
value += step * multiplier
|
||||||
|
if (value > maxValue) {
|
||||||
|
if (roundRobin) {
|
||||||
|
value = minValue
|
||||||
|
} else {
|
||||||
|
value = maxValue
|
||||||
|
stopUpdating()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateEditText()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun dec(multiplier: Int) {
|
||||||
|
value -= step * multiplier
|
||||||
|
if (value < minValue) {
|
||||||
|
if (roundRobin) {
|
||||||
|
value = maxValue
|
||||||
|
} else {
|
||||||
|
value = minValue
|
||||||
|
stopUpdating()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateEditText()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateEditText() {
|
||||||
|
if (value == 0.0 && !allowZero) editText.text = "" else editText.text = formatter.format(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun startUpdating(inc: Boolean) {
|
||||||
|
if (mUpdater != null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mUpdater = Executors.newSingleThreadScheduledExecutor()
|
||||||
|
mUpdater?.scheduleAtFixedRate(
|
||||||
|
UpdateCounterTask(inc), 200, 200,
|
||||||
|
TimeUnit.MILLISECONDS
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopUpdating() {
|
||||||
|
mUpdater?.shutdownNow()
|
||||||
|
mUpdater = null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
if (mUpdater == null) {
|
||||||
|
if (v === plusImage) {
|
||||||
|
inc(1)
|
||||||
|
} else {
|
||||||
|
dec(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
|
||||||
|
val isKeyOfInterest = keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER
|
||||||
|
val isReleased = event.action == KeyEvent.ACTION_UP
|
||||||
|
val isPressed = event.action == KeyEvent.ACTION_DOWN
|
||||||
|
if (isKeyOfInterest && isReleased) {
|
||||||
|
stopUpdating()
|
||||||
|
} else if (isKeyOfInterest && isPressed) {
|
||||||
|
startUpdating(v === plusImage)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||||
|
val isReleased = event.action == MotionEvent.ACTION_UP || event.action == MotionEvent.ACTION_CANCEL
|
||||||
|
val isPressed = event.action == MotionEvent.ACTION_DOWN
|
||||||
|
if (isReleased) {
|
||||||
|
stopUpdating()
|
||||||
|
} else if (isPressed) {
|
||||||
|
startUpdating(v === plusImage)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onGenericMotion(v: View, ev: MotionEvent): Boolean {
|
||||||
|
if (ev.action == MotionEvent.ACTION_SCROLL && ev.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER)) {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - mLastChange > THRESHOLD_TIME) mChangeCounter = 0
|
||||||
|
val dynamicMultiplier = if (mChangeCounter < THRESHOLD_COUNTER) 1 else if (mChangeCounter < THRESHOLD_COUNTER_LONG) 2 else 4
|
||||||
|
val delta = -ev.getAxisValue(MotionEventCompat.AXIS_SCROLL)
|
||||||
|
if (delta > 0) {
|
||||||
|
inc(dynamicMultiplier)
|
||||||
|
} else {
|
||||||
|
dec(dynamicMultiplier)
|
||||||
|
}
|
||||||
|
mLastChange = System.currentTimeMillis()
|
||||||
|
mChangeCounter++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val THRESHOLD_COUNTER = 5
|
||||||
|
private const val THRESHOLD_COUNTER_LONG = 10
|
||||||
|
private const val THRESHOLD_TIME = 100
|
||||||
|
private const val MSG_INC = 0
|
||||||
|
private const val MSG_DEC = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
editText = view.findViewById(editTextID)
|
||||||
|
minusImage = view.findViewById(minusID)
|
||||||
|
plusImage = view.findViewById(plusID)
|
||||||
|
value = initValue
|
||||||
|
this.minValue = minValue
|
||||||
|
this.maxValue = maxValue
|
||||||
|
this.step = step
|
||||||
|
this.formatter = formatter
|
||||||
|
this.allowZero = allowZero
|
||||||
|
this.roundRobin = roundRobin
|
||||||
|
mHandler = object : Handler(Looper.getMainLooper()) {
|
||||||
|
override fun handleMessage(msg: Message) {
|
||||||
|
when (msg.what) {
|
||||||
|
MSG_INC -> {
|
||||||
|
inc(msg.arg1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
MSG_DEC -> {
|
||||||
|
dec(msg.arg1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.handleMessage(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
minusImage.setOnTouchListener(this)
|
||||||
|
minusImage.setOnKeyListener(this)
|
||||||
|
minusImage.setOnClickListener(this)
|
||||||
|
plusImage.setOnTouchListener(this)
|
||||||
|
plusImage.setOnKeyListener(this)
|
||||||
|
plusImage.setOnClickListener(this)
|
||||||
|
editText.setOnGenericMotionListener(this)
|
||||||
|
updateEditText()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,135 +0,0 @@
|
||||||
package info.nightscout.androidaps.interaction.utils;
|
|
||||||
|
|
||||||
import java.math.RoundingMode;
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to minimise various floating point values, with or without unit, to fit into specified
|
|
||||||
* and limited size, scarifying precision (rounding up) and extra characters like leading zero,
|
|
||||||
* following zero(s) in fractional part, extra plus sign etc.
|
|
||||||
*
|
|
||||||
* Created by dlvoy on 2019-11-12
|
|
||||||
*/
|
|
||||||
public class SmallestDoubleString {
|
|
||||||
|
|
||||||
private String sign = "";
|
|
||||||
private String decimal = "";
|
|
||||||
private String separator = "";
|
|
||||||
private String fractional = "";
|
|
||||||
private String extra = "";
|
|
||||||
private String units = "";
|
|
||||||
|
|
||||||
private final Units withUnits;
|
|
||||||
|
|
||||||
public enum Units {
|
|
||||||
SKIP,
|
|
||||||
USE
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Pattern pattern = Pattern.compile("^([+-]?)([0-9]*)([,.]?)([0-9]*)(\\([^)]*\\))?(.*?)$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE );
|
|
||||||
|
|
||||||
public SmallestDoubleString(String inputString) {
|
|
||||||
this(inputString, Units.SKIP);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SmallestDoubleString(String inputString, Units withUnits) {
|
|
||||||
Matcher matcher = pattern.matcher(inputString);
|
|
||||||
matcher.matches();
|
|
||||||
|
|
||||||
sign = matcher.group(1);
|
|
||||||
decimal = matcher.group(2);
|
|
||||||
separator = matcher.group(3);
|
|
||||||
fractional = matcher.group(4);
|
|
||||||
units = matcher.group(6);
|
|
||||||
|
|
||||||
if (fractional == null || fractional.length() == 0) {
|
|
||||||
separator = "";
|
|
||||||
fractional = "";
|
|
||||||
}
|
|
||||||
if (decimal == null || decimal.length() == 0) {
|
|
||||||
decimal = "";
|
|
||||||
}
|
|
||||||
if (separator == null || separator.length() == 0) {
|
|
||||||
separator = "";
|
|
||||||
}
|
|
||||||
if (sign == null || sign.length() == 0) {
|
|
||||||
sign = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
final String extraCandidate = matcher.group(5);
|
|
||||||
if (extraCandidate != null && extraCandidate.length() > 2) {
|
|
||||||
extra = extraCandidate.substring(1, extraCandidate.length()-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (units != null) {
|
|
||||||
units = units.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.withUnits = withUnits;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String minimise(int maxSize) {
|
|
||||||
final String originalSeparator = separator;
|
|
||||||
|
|
||||||
if (Integer.parseInt("0"+fractional) == 0) {
|
|
||||||
separator = "";
|
|
||||||
fractional = "";
|
|
||||||
}
|
|
||||||
if (Integer.parseInt("0"+decimal) == 0 && (fractional.length() >0)) {
|
|
||||||
decimal = "";
|
|
||||||
}
|
|
||||||
if (currentLen() <= maxSize)
|
|
||||||
return toString();
|
|
||||||
|
|
||||||
if (sign.equals("+")) {
|
|
||||||
sign = "";
|
|
||||||
}
|
|
||||||
if (currentLen() <= maxSize) {
|
|
||||||
return toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((fractional.length() > 1)&&(fractional.charAt(fractional.length()-1) == '0')) {
|
|
||||||
fractional = fractional.substring(0, fractional.length()-1);
|
|
||||||
}
|
|
||||||
if (currentLen() <= maxSize) {
|
|
||||||
return toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fractional.length() > 0) {
|
|
||||||
int remainingForFraction = maxSize-currentLen()+fractional.length();
|
|
||||||
String formatCandidate = "#";
|
|
||||||
if (remainingForFraction>=1) {
|
|
||||||
formatCandidate = "#."+("#######".substring(0, remainingForFraction));
|
|
||||||
}
|
|
||||||
DecimalFormat df = new DecimalFormat(formatCandidate);
|
|
||||||
df.setRoundingMode(RoundingMode.HALF_UP);
|
|
||||||
|
|
||||||
final String decimalSup = (decimal.length() > 0) ? decimal : "0";
|
|
||||||
String result = sign + df.format(Double.parseDouble(decimalSup+"."+fractional)).replace(",", originalSeparator).replace(".", originalSeparator) +
|
|
||||||
((withUnits == Units.USE) ? units : "");
|
|
||||||
return (decimal.length() > 0) ? result : result.substring(1);
|
|
||||||
}
|
|
||||||
return toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int currentLen() {
|
|
||||||
return sign.length() + decimal.length() + separator.length() + fractional.length() +
|
|
||||||
((withUnits == Units.USE) ? units.length() : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return sign+decimal+separator+fractional +
|
|
||||||
((withUnits == Units.USE) ? units : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getExtra() {
|
|
||||||
return extra;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUnits() { return units; }
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
|
import kotlin.jvm.JvmOverloads
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to minimise various floating point values, with or without unit, to fit into specified
|
||||||
|
* and limited size, scarifying precision (rounding up) and extra characters like leading zero,
|
||||||
|
* following zero(s) in fractional part, extra plus sign etc.
|
||||||
|
*
|
||||||
|
* Created by dlvoy on 2019-11-12
|
||||||
|
*/
|
||||||
|
class SmallestDoubleString @JvmOverloads constructor(inputString: String, withUnits: Units = Units.SKIP) {
|
||||||
|
|
||||||
|
private var sign: String
|
||||||
|
private var decimal: String
|
||||||
|
private var separator: String
|
||||||
|
private var fractional: String
|
||||||
|
var extra = ""
|
||||||
|
var units: String
|
||||||
|
private val withUnits: Units
|
||||||
|
|
||||||
|
enum class Units {
|
||||||
|
SKIP, USE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun minimise(maxSize: Int): String {
|
||||||
|
val originalSeparator = separator
|
||||||
|
if ("0$fractional".toInt() == 0) {
|
||||||
|
separator = ""
|
||||||
|
fractional = ""
|
||||||
|
}
|
||||||
|
if ("0$decimal".toInt() == 0 && fractional.isNotEmpty()) {
|
||||||
|
decimal = ""
|
||||||
|
}
|
||||||
|
if (currentLen() <= maxSize) return toString()
|
||||||
|
if (sign == "+") {
|
||||||
|
sign = ""
|
||||||
|
}
|
||||||
|
if (currentLen() <= maxSize) {
|
||||||
|
return toString()
|
||||||
|
}
|
||||||
|
while (fractional.length > 1 && fractional[fractional.length - 1] == '0') {
|
||||||
|
fractional = fractional.substring(0, fractional.length - 1)
|
||||||
|
}
|
||||||
|
if (currentLen() <= maxSize) {
|
||||||
|
return toString()
|
||||||
|
}
|
||||||
|
if (fractional.isNotEmpty()) {
|
||||||
|
val remainingForFraction = maxSize - currentLen() + fractional.length
|
||||||
|
var formatCandidate = "#"
|
||||||
|
if (remainingForFraction >= 1) {
|
||||||
|
formatCandidate = "#." + "#######".substring(0, remainingForFraction)
|
||||||
|
}
|
||||||
|
val df = DecimalFormat(formatCandidate)
|
||||||
|
df.roundingMode = RoundingMode.HALF_UP
|
||||||
|
val decimalSup = decimal.ifEmpty { "0" }
|
||||||
|
val result = sign + df.format("$decimalSup.$fractional".toDouble()).replace(",", originalSeparator).replace(".", originalSeparator) +
|
||||||
|
if (withUnits == Units.USE) units else ""
|
||||||
|
return if (decimal.isNotEmpty()) result else result.substring(1)
|
||||||
|
}
|
||||||
|
return toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun currentLen(): Int {
|
||||||
|
return sign.length + decimal.length + separator.length + fractional.length +
|
||||||
|
if (withUnits == Units.USE) units.length else 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return sign + decimal + separator + fractional +
|
||||||
|
if (withUnits == Units.USE) units else ""
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val pattern = Pattern.compile("^([+-]?)([0-9]*)([,.]?)([0-9]*)(\\([^)]*\\))?(.*?)$", Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val matcher = pattern.matcher(inputString)
|
||||||
|
matcher.matches()
|
||||||
|
sign = matcher.group(1) ?: ""
|
||||||
|
decimal = matcher.group(2) ?: ""
|
||||||
|
separator = matcher.group(3) ?: ""
|
||||||
|
fractional = matcher.group(4) ?: ""
|
||||||
|
units = matcher.group(6) ?: ""
|
||||||
|
if (fractional.isEmpty()) {
|
||||||
|
separator = ""
|
||||||
|
fractional = ""
|
||||||
|
}
|
||||||
|
val extraCandidate = matcher.group(5) ?: ""
|
||||||
|
if (extraCandidate.length > 2) {
|
||||||
|
extra = extraCandidate.substring(1, extraCandidate.length - 1)
|
||||||
|
}
|
||||||
|
units = units.trim()
|
||||||
|
|
||||||
|
this.withUnits = withUnits
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
package info.nightscout.androidaps.interaction.utils
|
package info.nightscout.androidaps.interaction.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
import com.google.android.gms.wearable.DataMap
|
|
||||||
import info.nightscout.shared.logging.AAPSLogger
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile
|
||||||
|
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
|
import info.nightscout.androidaps.tile.source.ActionSource
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class ActionsTileService : TileBase() {
|
class ActionsTileService : TileBase() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile
|
||||||
|
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
|
import info.nightscout.androidaps.tile.source.QuickWizardSource
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class QuickWizardTileService : TileBase() {
|
class QuickWizardTileService : TileBase() {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile
|
||||||
|
|
||||||
import dagger.android.AndroidInjection
|
import dagger.android.AndroidInjection
|
||||||
|
import info.nightscout.androidaps.tile.source.TempTargetSource
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class TempTargetTileService : TileBase() {
|
class TempTargetTileService : TileBase() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
|
@ -1,9 +1,11 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import info.nightscout.androidaps.R
|
import info.nightscout.androidaps.R
|
||||||
import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity
|
import info.nightscout.androidaps.interaction.actions.BackgroundActionActivity
|
||||||
|
import info.nightscout.androidaps.tile.Action
|
||||||
|
import info.nightscout.androidaps.tile.TileSource
|
||||||
import info.nightscout.shared.logging.AAPSLogger
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
import info.nightscout.shared.logging.LTag
|
import info.nightscout.shared.logging.LTag
|
||||||
import info.nightscout.shared.sharedPreferences.SP
|
import info.nightscout.shared.sharedPreferences.SP
|
|
@ -1,9 +1,10 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import info.nightscout.androidaps.tile.Action
|
||||||
|
import info.nightscout.androidaps.tile.TileSource
|
||||||
import info.nightscout.shared.logging.AAPSLogger
|
import info.nightscout.shared.logging.AAPSLogger
|
||||||
import info.nightscout.shared.sharedPreferences.SP
|
import info.nightscout.shared.sharedPreferences.SP
|
||||||
import info.nightscout.shared.weardata.EventData
|
import info.nightscout.shared.weardata.EventData
|
|
@ -1,4 +1,4 @@
|
||||||
package info.nightscout.androidaps.tile
|
package info.nightscout.androidaps.tile.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.res.Resources
|
import android.content.res.Resources
|
|
@ -66,7 +66,6 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
@Inject lateinit var dateUtil: DateUtil
|
@Inject lateinit var dateUtil: DateUtil
|
||||||
|
|
||||||
private var disposable = CompositeDisposable()
|
private var disposable = CompositeDisposable()
|
||||||
|
|
||||||
private val rawData = RawDisplayData()
|
private val rawData = RawDisplayData()
|
||||||
|
|
||||||
protected val singleBg get() = rawData.singleBg
|
protected val singleBg get() = rawData.singleBg
|
||||||
|
@ -96,11 +95,11 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
var mCOB2: TextView? = null
|
var mCOB2: TextView? = null
|
||||||
var mBgi: TextView? = null
|
var mBgi: TextView? = null
|
||||||
var mLoop: TextView? = null
|
var mLoop: TextView? = null
|
||||||
var mTimePeriod: TextView? = null
|
private var mTimePeriod: TextView? = null
|
||||||
var mDay: TextView? = null
|
var mDay: TextView? = null
|
||||||
var mDayName: TextView? = null
|
private var mDayName: TextView? = null
|
||||||
var mMonth: TextView? = null
|
var mMonth: TextView? = null
|
||||||
var isAAPSv2: View? = null
|
private var isAAPSv2: View? = null
|
||||||
var mHighLight: TextView? = null
|
var mHighLight: TextView? = null
|
||||||
var mLowLight: TextView? = null
|
var mLowLight: TextView? = null
|
||||||
var mGlucoseDial: ImageView? = null
|
var mGlucoseDial: ImageView? = null
|
||||||
|
@ -110,9 +109,9 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
var mRelativeLayout: ViewGroup? = null
|
var mRelativeLayout: ViewGroup? = null
|
||||||
var mLinearLayout: LinearLayout? = null
|
var mLinearLayout: LinearLayout? = null
|
||||||
var mLinearLayout2: LinearLayout? = null
|
var mLinearLayout2: LinearLayout? = null
|
||||||
var mDate: LinearLayout? = null
|
private var mDate: LinearLayout? = null
|
||||||
var mChartTap: LinearLayout? = null // Steampunk only
|
private var mChartTap: LinearLayout? = null // Steampunk only
|
||||||
var mMainMenuTap: LinearLayout? = null // Steampunk,Digital only
|
private var mMainMenuTap: LinearLayout? = null // Steampunk,Digital only
|
||||||
var chart: LineChartView? = null
|
var chart: LineChartView? = null
|
||||||
|
|
||||||
var ageLevel = 1
|
var ageLevel = 1
|
||||||
|
@ -123,7 +122,7 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
var gridColor = Color.WHITE
|
var gridColor = Color.WHITE
|
||||||
var basalBackgroundColor = Color.BLUE
|
var basalBackgroundColor = Color.BLUE
|
||||||
var basalCenterColor = Color.BLUE
|
var basalCenterColor = Color.BLUE
|
||||||
var bolusColor = Color.MAGENTA
|
private var bolusColor = Color.MAGENTA
|
||||||
private var lowResMode = false
|
private var lowResMode = false
|
||||||
private var layoutSet = false
|
private var layoutSet = false
|
||||||
var bIsRound = false
|
var bIsRound = false
|
||||||
|
@ -137,8 +136,8 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
|
|
||||||
// related endTime manual layout
|
// related endTime manual layout
|
||||||
var layoutView: View? = null
|
var layoutView: View? = null
|
||||||
var specW = 0
|
private var specW = 0
|
||||||
var specH = 0
|
private var specH = 0
|
||||||
var forceSquareCanvas = false // Set to true by the Steampunk watch face.
|
var forceSquareCanvas = false // Set to true by the Steampunk watch face.
|
||||||
private var batteryReceiver: BroadcastReceiver? = null
|
private var batteryReceiver: BroadcastReceiver? = null
|
||||||
private var colorDarkHigh = 0
|
private var colorDarkHigh = 0
|
||||||
|
@ -338,7 +337,7 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
return (System.currentTimeMillis() - singleBg.timeStamp).toDouble()
|
return (System.currentTimeMillis() - singleBg.timeStamp).toDouble()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun readingAge(shortString: Boolean): String {
|
private fun readingAge(shortString: Boolean): String {
|
||||||
if (singleBg.timeStamp == 0L) {
|
if (singleBg.timeStamp == 0L) {
|
||||||
return if (shortString) "--" else "-- Minute ago"
|
return if (shortString) "--" else "-- Minute ago"
|
||||||
}
|
}
|
||||||
|
@ -491,7 +490,7 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun setDateAndTime() {
|
private fun setDateAndTime() {
|
||||||
mTime?.text = dateUtil.timeString()
|
mTime?.text = dateUtil.timeString()
|
||||||
mHour?.text = dateUtil.hourString()
|
mHour?.text = dateUtil.hourString()
|
||||||
mMinute?.text = dateUtil.minuteString()
|
mMinute?.text = dateUtil.minuteString()
|
||||||
|
@ -512,7 +511,7 @@ abstract class BaseWatchFace : WatchFace() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun strikeThroughSgvIfNeeded() {
|
private fun strikeThroughSgvIfNeeded() {
|
||||||
mSgv?.let { mSgv ->
|
mSgv?.let { mSgv ->
|
||||||
if (ageLevel() <= 0 && singleBg.timeStamp > 0) mSgv.paintFlags = mSgv.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
if (ageLevel() <= 0 && singleBg.timeStamp > 0) mSgv.paintFlags = mSgv.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||||
else mSgv.paintFlags = mSgv.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
else mSgv.paintFlags = mSgv.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
package info.nightscout.androidaps.watchfaces
|
package info.nightscout.androidaps.watchfaces
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.ustwo.clockwise.common.WatchMode
|
import com.ustwo.clockwise.common.WatchMode
|
||||||
|
@ -13,6 +14,7 @@ class BigChartWatchface : BaseWatchFace() {
|
||||||
if (resources.displayMetrics.widthPixels < SCREEN_SIZE_SMALL || resources.displayMetrics.heightPixels < SCREEN_SIZE_SMALL) R.layout.activity_bigchart_small
|
if (resources.displayMetrics.widthPixels < SCREEN_SIZE_SMALL || resources.displayMetrics.heightPixels < SCREEN_SIZE_SMALL) R.layout.activity_bigchart_small
|
||||||
else R.layout.activity_bigchart
|
else R.layout.activity_bigchart
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
override fun setDataFields() {
|
override fun setDataFields() {
|
||||||
super.setDataFields()
|
super.setDataFields()
|
||||||
mStatus?.text = status.externalStatus + if (sp.getBoolean(R.string.key_show_cob, true)) (" " + this.status.cob) else ""
|
mStatus?.text = status.externalStatus + if (sp.getBoolean(R.string.key_show_cob, true)) (" " + this.status.cob) else ""
|
||||||
|
|
|
@ -197,7 +197,7 @@ class CircleWatchface : WatchFace() {
|
||||||
myLayout?.layout(0, 0, myLayout?.measuredWidth ?: 0, myLayout?.measuredHeight ?: 0)
|
myLayout?.layout(0, 0, myLayout?.measuredWidth ?: 0, myLayout?.measuredHeight ?: 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
val minutes: String
|
private val minutes: String
|
||||||
get() {
|
get() {
|
||||||
var minutes = "--'"
|
var minutes = "--'"
|
||||||
if (singleBg.timeStamp != 0L) {
|
if (singleBg.timeStamp != 0L) {
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_action_1"
|
|
||||||
android:title="Action 1"
|
|
||||||
android:defaultValue="wizard"
|
android:defaultValue="wizard"
|
||||||
android:entries="@array/tile_action_names"
|
android:entries="@array/tile_action_names"
|
||||||
android:entryValues="@array/tile_action_values"/>
|
android:entryValues="@array/tile_action_values"
|
||||||
|
android:key="tile_action_1"
|
||||||
|
android:title="Action 1" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_action_2"
|
|
||||||
android:title="Action 2"
|
|
||||||
android:defaultValue="treatment"
|
android:defaultValue="treatment"
|
||||||
android:entries="@array/tile_action_names"
|
android:entries="@array/tile_action_names"
|
||||||
android:entryValues="@array/tile_action_values"/>
|
android:entryValues="@array/tile_action_values"
|
||||||
|
android:key="tile_action_2"
|
||||||
|
android:title="Action 2" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_action_3"
|
|
||||||
android:title="Action 3"
|
|
||||||
android:defaultValue="carbs"
|
android:defaultValue="carbs"
|
||||||
android:entries="@array/tile_action_names"
|
android:entries="@array/tile_action_names"
|
||||||
android:entryValues="@array/tile_action_values"/>
|
android:entryValues="@array/tile_action_values"
|
||||||
|
android:key="tile_action_3"
|
||||||
|
android:title="Action 3" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_action_4"
|
|
||||||
android:title="Action 4"
|
|
||||||
android:defaultValue="temp_target"
|
android:defaultValue="temp_target"
|
||||||
android:entries="@array/tile_action_names"
|
android:entries="@array/tile_action_names"
|
||||||
android:entryValues="@array/tile_action_values"/>
|
android:entryValues="@array/tile_action_values"
|
||||||
|
android:key="tile_action_4"
|
||||||
|
android:title="Action 4" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_tempt_1"
|
|
||||||
android:title="Target 1"
|
|
||||||
android:defaultValue="activity"
|
android:defaultValue="activity"
|
||||||
android:entries="@array/tile_tempt_names"
|
android:entries="@array/tile_tempt_names"
|
||||||
android:entryValues="@array/tile_tempt_values"/>
|
android:entryValues="@array/tile_tempt_values"
|
||||||
|
android:key="tile_tempt_1"
|
||||||
|
android:title="Target 1" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_tempt_2"
|
|
||||||
android:title="Target 2"
|
|
||||||
android:defaultValue="eating_soon"
|
android:defaultValue="eating_soon"
|
||||||
android:entries="@array/tile_tempt_names"
|
android:entries="@array/tile_tempt_names"
|
||||||
android:entryValues="@array/tile_tempt_values"/>
|
android:entryValues="@array/tile_tempt_values"
|
||||||
|
android:key="tile_tempt_2"
|
||||||
|
android:title="Target 2" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_tempt_3"
|
|
||||||
android:title="Target 3"
|
|
||||||
android:defaultValue="hypo"
|
android:defaultValue="hypo"
|
||||||
android:entries="@array/tile_tempt_names"
|
android:entries="@array/tile_tempt_names"
|
||||||
android:entryValues="@array/tile_tempt_values"/>
|
android:entryValues="@array/tile_tempt_values"
|
||||||
|
android:key="tile_tempt_3"
|
||||||
|
android:title="Target 3" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
android:key="tile_tempt_4"
|
|
||||||
android:title="Target 4"
|
|
||||||
android:defaultValue="manual"
|
android:defaultValue="manual"
|
||||||
android:entries="@array/tile_tempt_names"
|
android:entries="@array/tile_tempt_names"
|
||||||
android:entryValues="@array/tile_tempt_values"/>
|
android:entryValues="@array/tile_tempt_values"
|
||||||
|
android:key="tile_tempt_4"
|
||||||
|
android:title="Target 4" />
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
|
Loading…
Reference in a new issue