chore: wear last java to kotlin

This commit is contained in:
Andries Smit 2022-06-01 14:29:01 +02:00
parent 8b8939fbb1
commit 3c4abe9378
28 changed files with 655 additions and 755 deletions

View file

@ -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"))
@ -39,4 +40,4 @@ class BrCobIobComplication : BaseComplicationProviderService() {
} }
override fun getProviderCanonicalName(): String = BrCobIobComplication::class.java.canonicalName!! override fun getProviderCanonicalName(): String = BrCobIobComplication::class.java.canonicalName!!
} }

View file

@ -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))
@ -31,4 +32,4 @@ class CobIobComplication : BaseComplicationProviderService() {
} }
override fun getProviderCanonicalName(): String = CobIobComplication::class.java.canonicalName!! override fun getProviderCanonicalName(): String = CobIobComplication::class.java.canonicalName!!
} }

View file

@ -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))
@ -34,4 +35,4 @@ class IobIconComplication : BaseComplicationProviderService() {
override fun getProviderCanonicalName(): String = IobIconComplication::class.java.canonicalName!! override fun getProviderCanonicalName(): String = IobIconComplication::class.java.canonicalName!!
override fun getComplicationAction(): ComplicationAction = ComplicationAction.BOLUS override fun getComplicationAction(): ComplicationAction = ComplicationAction.BOLUS
} }

View file

@ -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)))
@ -31,4 +31,4 @@ class FillMenuActivity : MenuListActivity() {
getString(R.string.action_free_amount) -> startActivity(Intent(this, FillActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) getString(R.string.action_free_amount) -> startActivity(Intent(this, FillActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
} }
} }
} }

View file

@ -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)))
@ -53,4 +53,4 @@ class MainMenuActivity : MenuListActivity() {
getString(R.string.menu_ecarb) -> startActivity(Intent(this, ECarbActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) }) getString(R.string.menu_ecarb) -> startActivity(Intent(this, ECarbActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) })
} }
} }
} }

View file

@ -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)))
@ -29,4 +29,4 @@ class StatusMenuActivity : MenuListActivity() {
getString(R.string.status_tdd) -> rxBus.send(EventWearToMobile(ActionTddStatus(System.currentTimeMillis()))) getString(R.string.status_tdd) -> rxBus.send(EventWearToMobile(ActionTddStatus(System.currentTimeMillis())))
} }
} }
} }

View file

@ -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);
}
}

View file

@ -0,0 +1,135 @@
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 { it <= ' ' }.isEmpty()) {
iobBolus = "--"
}
var iobBasal = SmallestDoubleString(iobs[1]).minimise(MAX_FIELD_LEN_SHORT - 1 - max(MIN_FIELD_LEN_IOB, iobBolus.length))
if (iobBasal.trim { it <= ' ' }.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)
}
}

View file

@ -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);
}
}
}

View file

@ -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
}
}
}

View file

@ -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
@ -182,4 +181,4 @@ class Persistence @Inject constructor(
aapsLogger.debug(LTag.WEAR, "TURNING OFF all active complications") aapsLogger.debug(LTag.WEAR, "TURNING OFF all active complications")
putString(KEY_COMPLICATIONS, "") putString(KEY_COMPLICATIONS, "")
} }
} }

View file

@ -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;
}
}

View file

@ -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()
}
}

View file

@ -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; }
}

View file

@ -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
}
}

View file

@ -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
@ -72,4 +70,4 @@ class WearUtil @Inject constructor() {
// we simply ignore if sleep was interrupted // we simply ignore if sleep was interrupted
} }
} }
} }

View file

@ -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() {

View file

@ -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() {

View file

@ -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() {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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 ""
@ -118,4 +120,4 @@ class BigChartWatchface : BaseWatchFace() {
setColorDark() setColorDark()
} }
} }
} }

View file

@ -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) {
@ -421,4 +421,4 @@ class CircleWatchface : WatchFace() {
override fun getWatchFaceStyle(): WatchFaceStyle { override fun getWatchFaceStyle(): WatchFaceStyle {
return WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build() return WatchFaceStyle.Builder(this).setAcceptsTapEvents(true).build()
} }
} }

View file

@ -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>

View file

@ -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>