Merge pull request #2857 from Philoul/wear/new_custom_watchface
Wear CWFNew customization capabilities with Dyn Datas
This commit is contained in:
commit
ed4c9792d6
2 changed files with 191 additions and 34 deletions
|
@ -238,7 +238,20 @@ enum class JsonKeys(val key: String) {
|
||||||
ALLCAPS("allCaps"),
|
ALLCAPS("allCaps"),
|
||||||
DAYNAMEFORMAT("dayNameFormat"),
|
DAYNAMEFORMAT("dayNameFormat"),
|
||||||
MONTHFORMAT("monthFormat"),
|
MONTHFORMAT("monthFormat"),
|
||||||
BACKGROUND("background")
|
BACKGROUND("background"), // Background image for textView
|
||||||
|
LEFTOFFSET("leftOffset"),
|
||||||
|
TOPOFFSET("topOffset"),
|
||||||
|
ROTATIONOFFSET("rotationOffset"),
|
||||||
|
DYNDATA("dynData"), //Bloc of DynDatas definition, and DynData keyValue within view
|
||||||
|
VALUEKEY("valueKey"), // Indentify which value (default is View Value)
|
||||||
|
MINDATA("minData"), // Min data Value (default defined for each value, note unit mg/dl for all bg, deltas)
|
||||||
|
MAXDATA("maxData"), // Max data idem min data (note all value below min or above max will be considered as equal min or mas)
|
||||||
|
MINVALUE("minValue"), // min returned value (when data value equals minData
|
||||||
|
MAXVALUE("maxValue"), //
|
||||||
|
INVALIDVALUE("invalidValue"),
|
||||||
|
IMAGE("image"),
|
||||||
|
INVALIDIMAGE("invalidImage"),
|
||||||
|
INVALIDCOLOR("invalidColor")
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class JsonKeyValues(val key: String) {
|
enum class JsonKeyValues(val key: String) {
|
||||||
|
@ -260,7 +273,8 @@ enum class JsonKeyValues(val key: String) {
|
||||||
BOLD("bold"),
|
BOLD("bold"),
|
||||||
BOLD_ITALIC("bold_italic"),
|
BOLD_ITALIC("bold_italic"),
|
||||||
ITALIC("italic"),
|
ITALIC("italic"),
|
||||||
BGCOLOR("bgColor")
|
BGCOLOR("bgColor"),
|
||||||
|
SGVLEVEL("sgvLevel")
|
||||||
}
|
}
|
||||||
|
|
||||||
class ZipWatchfaceFormat {
|
class ZipWatchfaceFormat {
|
||||||
|
|
|
@ -48,10 +48,12 @@ import app.aaps.core.interfaces.rx.weardata.isEquals
|
||||||
import app.aaps.wear.R
|
import app.aaps.wear.R
|
||||||
import app.aaps.wear.databinding.ActivityCustomBinding
|
import app.aaps.wear.databinding.ActivityCustomBinding
|
||||||
import app.aaps.wear.watchfaces.utils.BaseWatchFace
|
import app.aaps.wear.watchfaces.utils.BaseWatchFace
|
||||||
|
import org.joda.time.DateTime
|
||||||
import org.joda.time.TimeOfDay
|
import org.joda.time.TimeOfDay
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
@SuppressLint("UseCompatLoadingForDrawables")
|
@SuppressLint("UseCompatLoadingForDrawables")
|
||||||
class CustomWatchface : BaseWatchFace() {
|
class CustomWatchface : BaseWatchFace() {
|
||||||
|
@ -63,6 +65,7 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
private val TEMPLATE_RESOLUTION = 400
|
private val TEMPLATE_RESOLUTION = 400
|
||||||
private var lowBatColor = Color.RED
|
private var lowBatColor = Color.RED
|
||||||
private var resDataMap: CwfResDataMap = mutableMapOf()
|
private var resDataMap: CwfResDataMap = mutableMapOf()
|
||||||
|
private var jsonString = ""
|
||||||
private val bgColor: Int
|
private val bgColor: Int
|
||||||
get() = when (singleBg.sgvLevel) {
|
get() = when (singleBg.sgvLevel) {
|
||||||
1L -> highColor
|
1L -> highColor
|
||||||
|
@ -102,18 +105,20 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
|
|
||||||
override fun setColorDark() {
|
override fun setColorDark() {
|
||||||
setWatchfaceStyle()
|
setWatchfaceStyle()
|
||||||
binding.sgv.setTextColor(bgColor)
|
if ((ViewMap.SGV.dynData?.stepColor ?: 0) == 0)
|
||||||
binding.direction2.colorFilter = changeDrawableColor(bgColor)
|
binding.sgv.setTextColor(bgColor)
|
||||||
|
if ((ViewMap.DIRECTION.dynData?.stepColor ?: 0) == 0)
|
||||||
if (ageLevel != 1)
|
binding.direction2.colorFilter = changeDrawableColor(bgColor)
|
||||||
|
if (ageLevel != 1 && (ViewMap.TIMESTAMP.dynData?.stepColor ?: 0) == 0)
|
||||||
binding.timestamp.setTextColor(ContextCompat.getColor(this, R.color.dark_TimestampOld))
|
binding.timestamp.setTextColor(ContextCompat.getColor(this, R.color.dark_TimestampOld))
|
||||||
if (status.batteryLevel != 1)
|
if (status.batteryLevel != 1 && (ViewMap.UPLOADER_BATTERY.dynData?.stepColor ?: 0) == 0)
|
||||||
binding.uploaderBattery.setTextColor(lowBatColor)
|
binding.uploaderBattery.setTextColor(lowBatColor)
|
||||||
when (loopLevel) {
|
if ((ViewMap.LOOP.dynData?.stepDraw ?: 0) == 0) // Apply automatic background image only if no dynData or no step images
|
||||||
-1 -> binding.loop.setBackgroundResource(R.drawable.loop_grey_25)
|
when (loopLevel) {
|
||||||
1 -> binding.loop.setBackgroundResource(R.drawable.loop_green_25)
|
-1 -> binding.loop.setBackgroundResource(R.drawable.loop_grey_25)
|
||||||
else -> binding.loop.setBackgroundResource(R.drawable.loop_red_25)
|
1 -> binding.loop.setBackgroundResource(R.drawable.loop_green_25)
|
||||||
}
|
else -> binding.loop.setBackgroundResource(R.drawable.loop_red_25)
|
||||||
|
}
|
||||||
|
|
||||||
setupCharts()
|
setupCharts()
|
||||||
}
|
}
|
||||||
|
@ -147,11 +152,13 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
updatePref(it.customWatchfaceData.metadata)
|
updatePref(it.customWatchfaceData.metadata)
|
||||||
try {
|
try {
|
||||||
val json = JSONObject(it.customWatchfaceData.json)
|
val json = JSONObject(it.customWatchfaceData.json)
|
||||||
if (!resDataMap.isEquals(it.customWatchfaceData.resDatas)) {
|
if (!resDataMap.isEquals(it.customWatchfaceData.resDatas) || jsonString != it.customWatchfaceData.json) {
|
||||||
resDataMap = it.customWatchfaceData.resDatas
|
resDataMap = it.customWatchfaceData.resDatas
|
||||||
|
jsonString = it.customWatchfaceData.json
|
||||||
FontMap.init(this)
|
FontMap.init(this)
|
||||||
ViewMap.init(this)
|
ViewMap.init(this)
|
||||||
TrendArrowMap.init(this)
|
TrendArrowMap.init(this)
|
||||||
|
DynProvider.init(json.optJSONObject(DYNDATA.key))
|
||||||
}
|
}
|
||||||
enableSecond = json.optBoolean(ENABLESECOND.key) && sp.getBoolean(R.string.key_show_seconds, true)
|
enableSecond = json.optBoolean(ENABLESECOND.key) && sp.getBoolean(R.string.key_show_seconds, true)
|
||||||
highColor = getColor(json.optString(HIGHCOLOR.key), ContextCompat.getColor(this, R.color.dark_highColor))
|
highColor = getColor(json.optString(HIGHCOLOR.key), ContextCompat.getColor(this, R.color.dark_highColor))
|
||||||
|
@ -174,6 +181,7 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
when (view) {
|
when (view) {
|
||||||
is TextView -> viewMap.customizeTextView(view, viewJson)
|
is TextView -> viewMap.customizeTextView(view, viewJson)
|
||||||
is ImageView -> viewMap.customizeImageView(view, viewJson)
|
is ImageView -> viewMap.customizeImageView(view, viewJson)
|
||||||
|
is lecho.lib.hellocharts.view.LineChartView -> viewMap.customizeGraphView(view, viewJson)
|
||||||
else -> viewMap.customizeViewCommon(view, viewJson)
|
else -> viewMap.customizeViewCommon(view, viewJson)
|
||||||
}
|
}
|
||||||
} ?: apply {
|
} ?: apply {
|
||||||
|
@ -446,6 +454,7 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
var height = 0
|
var height = 0
|
||||||
var left = 0
|
var left = 0
|
||||||
var top = 0
|
var top = 0
|
||||||
|
var dynData: DynProvider? = null
|
||||||
var rangeCustom: Drawable? = null
|
var rangeCustom: Drawable? = null
|
||||||
get() = field ?: customDrawable?.let { cd -> cwf.resDataMap[cd.fileName]?.toDrawable(cwf.resources).also { rangeCustom = it } }
|
get() = field ?: customDrawable?.let { cd -> cwf.resDataMap[cd.fileName]?.toDrawable(cwf.resources).also { rangeCustom = it } }
|
||||||
var highCustom: Drawable? = null
|
var highCustom: Drawable? = null
|
||||||
|
@ -454,7 +463,7 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
get() = field ?: customLow?.let { cd -> cwf.resDataMap[cd.fileName]?.toDrawable(cwf.resources).also { lowCustom = it } }
|
get() = field ?: customLow?.let { cd -> cwf.resDataMap[cd.fileName]?.toDrawable(cwf.resources).also { lowCustom = it } }
|
||||||
var textDrawable: Drawable? = null
|
var textDrawable: Drawable? = null
|
||||||
val drawable: Drawable?
|
val drawable: Drawable?
|
||||||
get() = when (cwf.singleBg.sgvLevel) {
|
get() = dynData?.getDrawable() ?: when (cwf.singleBg.sgvLevel) {
|
||||||
1L -> highCustom ?: rangeCustom
|
1L -> highCustom ?: rangeCustom
|
||||||
0L -> rangeCustom
|
0L -> rangeCustom
|
||||||
-1L -> lowCustom ?: rangeCustom
|
-1L -> lowCustom ?: rangeCustom
|
||||||
|
@ -472,58 +481,68 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
left = (viewJson.optInt(LEFTMARGIN.key) * cwf.zoomFactor).toInt()
|
left = (viewJson.optInt(LEFTMARGIN.key) * cwf.zoomFactor).toInt()
|
||||||
top = (viewJson.optInt(TOPMARGIN.key) * cwf.zoomFactor).toInt()
|
top = (viewJson.optInt(TOPMARGIN.key) * cwf.zoomFactor).toInt()
|
||||||
val params = FrameLayout.LayoutParams(width, height)
|
val params = FrameLayout.LayoutParams(width, height)
|
||||||
params.topMargin = top
|
dynData = DynProvider.getDyn(cwf, viewJson.optString(DYNDATA.key),width, height, key)
|
||||||
params.leftMargin = left
|
val topOffset = if (viewJson.optBoolean(TOPOFFSET.key, false)) dynData?.getTopOffset() ?:0 else 0
|
||||||
|
params.topMargin = top + topOffset
|
||||||
|
val leftOffset = if (viewJson.optBoolean(LEFTOFFSET.key, false)) dynData?.getLeftOffset() ?:0 else 0
|
||||||
|
params.leftMargin = left + leftOffset
|
||||||
view.layoutParams = params
|
view.layoutParams = params
|
||||||
view.visibility = cwf.setVisibility(viewJson.optString(VISIBILITY.key, JsonKeyValues.GONE.key), visibility())
|
view.visibility = cwf.setVisibility(viewJson.optString(VISIBILITY.key, JsonKeyValues.GONE.key), visibility())
|
||||||
|
val rotationOffset = if (viewJson.optBoolean(ROTATIONOFFSET.key, false)) dynData?.getRotationOffset()?.toFloat() ?:0F else 0F
|
||||||
|
view.rotation = viewJson.optInt(ROTATION.key).toFloat() + rotationOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
fun customizeTextView(view: TextView, viewJson: JSONObject) {
|
fun customizeTextView(view: TextView, viewJson: JSONObject) {
|
||||||
customizeViewCommon(view, viewJson)
|
customizeViewCommon(view, viewJson)
|
||||||
view.rotation = viewJson.optInt(ROTATION.key).toFloat()
|
|
||||||
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, (viewJson.optInt(TEXTSIZE.key, 22) * cwf.zoomFactor).toFloat())
|
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, (viewJson.optInt(TEXTSIZE.key, 22) * cwf.zoomFactor).toFloat())
|
||||||
view.gravity = GravityMap.gravity(viewJson.optString(GRAVITY.key, GravityMap.CENTER.key))
|
view.gravity = GravityMap.gravity(viewJson.optString(GRAVITY.key, GravityMap.CENTER.key))
|
||||||
view.setTypeface(
|
view.setTypeface(
|
||||||
FontMap.font(viewJson.optString(FONT.key, FontMap.DEFAULT.key)),
|
FontMap.font(viewJson.optString(FONT.key, FontMap.DEFAULT.key)),
|
||||||
StyleMap.style(viewJson.optString(FONTSTYLE.key, StyleMap.NORMAL.key))
|
StyleMap.style(viewJson.optString(FONTSTYLE.key, StyleMap.NORMAL.key))
|
||||||
)
|
)
|
||||||
view.setTextColor(cwf.getColor(viewJson.optString(FONTCOLOR.key)))
|
view.setTextColor(dynData?.getColor() ?: cwf.getColor(viewJson.optString(FONTCOLOR.key)))
|
||||||
view.isAllCaps = viewJson.optBoolean(ALLCAPS.key)
|
view.isAllCaps = viewJson.optBoolean(ALLCAPS.key)
|
||||||
if (viewJson.has(TEXTVALUE.key))
|
if (viewJson.has(TEXTVALUE.key))
|
||||||
view.text = viewJson.optString(TEXTVALUE.key)
|
view.text = viewJson.optString(TEXTVALUE.key)
|
||||||
view.background = textDrawable(viewJson)
|
view.background = dynData?.getDrawable() ?: textDrawable(viewJson)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun customizeImageView(view: ImageView, viewJson: JSONObject) {
|
fun customizeImageView(view: ImageView, viewJson: JSONObject) {
|
||||||
customizeViewCommon(view, viewJson)
|
customizeViewCommon(view, viewJson)
|
||||||
view.clearColorFilter()
|
view.clearColorFilter()
|
||||||
drawable?.let {
|
drawable?.let {
|
||||||
if (viewJson.has(COLOR.key)) // Note only works on bitmap (png or jpg) or xml included into res, not for svg files
|
if (viewJson.has(COLOR.key) || (dynData?.stepColor ?: 0) > 0) // Note only works on bitmap (png or jpg) or xml included into res, not for svg files
|
||||||
it.colorFilter = cwf.changeDrawableColor(cwf.getColor(viewJson.optString(COLOR.key)))
|
it.colorFilter = cwf.changeDrawableColor(dynData?.getColor() ?: cwf.getColor(viewJson.optString(COLOR.key)))
|
||||||
else
|
else
|
||||||
it.clearColorFilter()
|
it.clearColorFilter()
|
||||||
view.setImageDrawable(it)
|
view.setImageDrawable(it)
|
||||||
} ?: apply {
|
} ?: apply {
|
||||||
view.setImageDrawable(defaultDrawable?.let { cwf.resources.getDrawable(it) })
|
view.setImageDrawable(defaultDrawable?.let { cwf.resources.getDrawable(it) })
|
||||||
if (viewJson.has(COLOR.key))
|
if (viewJson.has(COLOR.key) || (dynData?.stepColor ?: 0) > 0)
|
||||||
view.setColorFilter(cwf.getColor(viewJson.optString(COLOR.key)))
|
view.setColorFilter(dynData?.getColor() ?: cwf.getColor(viewJson.optString(COLOR.key)))
|
||||||
else
|
else
|
||||||
view.clearColorFilter()
|
view.clearColorFilter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun customizeGraphView(view: lecho.lib.hellocharts.view.LineChartView, viewJson: JSONObject) {
|
||||||
|
customizeViewCommon(view, viewJson)
|
||||||
|
view.setBackgroundColor(dynData?.getColor() ?: cwf.getColor(viewJson.optString(COLOR.key, "#0000000000"),Color.TRANSPARENT))
|
||||||
|
view.background = dynData?.getDrawable() ?: textDrawable(viewJson)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int, val customDrawable: ResFileMap?) {
|
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int, val customDrawable: ResFileMap?, val dynValue: Double) {
|
||||||
NONE("??", R.drawable.ic_invalid, ResFileMap.ARROW_NONE),
|
NONE("??", R.drawable.ic_invalid, ResFileMap.ARROW_NONE, 0.0),
|
||||||
TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
|
TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP, 7.0),
|
||||||
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
|
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP, 7.0),
|
||||||
SINGLE_UP("\u2191", R.drawable.ic_singleup, ResFileMap.ARROW_SINGLE_UP),
|
SINGLE_UP("\u2191", R.drawable.ic_singleup, ResFileMap.ARROW_SINGLE_UP, 6.0),
|
||||||
FORTY_FIVE_UP("\u2197", R.drawable.ic_fortyfiveup, ResFileMap.ARROW_FORTY_FIVE_UP),
|
FORTY_FIVE_UP("\u2197", R.drawable.ic_fortyfiveup, ResFileMap.ARROW_FORTY_FIVE_UP, 5.0),
|
||||||
FLAT("\u2192", R.drawable.ic_flat, ResFileMap.ARROW_FLAT),
|
FLAT("\u2192", R.drawable.ic_flat, ResFileMap.ARROW_FLAT, 4.0),
|
||||||
FORTY_FIVE_DOWN("\u2198", R.drawable.ic_fortyfivedown, ResFileMap.ARROW_FORTY_FIVE_DOWN),
|
FORTY_FIVE_DOWN("\u2198", R.drawable.ic_fortyfivedown, ResFileMap.ARROW_FORTY_FIVE_DOWN, 3.0),
|
||||||
SINGLE_DOWN("\u2193", R.drawable.ic_singledown, ResFileMap.ARROW_SINGLE_DOWN),
|
SINGLE_DOWN("\u2193", R.drawable.ic_singledown, ResFileMap.ARROW_SINGLE_DOWN, 2.0),
|
||||||
DOUBLE_DOWN("\u21ca", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN),
|
DOUBLE_DOWN("\u21ca", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN, 2.0),
|
||||||
TRIPLE_DOWN("X", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN);
|
TRIPLE_DOWN("X", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN, 1.0);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
@ -532,6 +551,7 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
it.arrowCustom = null
|
it.arrowCustom = null
|
||||||
}
|
}
|
||||||
fun drawable() = values().firstOrNull { it.symbol == it.cwf.singleBg.slopeArrow }?.arrowCustom ?: NONE.arrowCustom
|
fun drawable() = values().firstOrNull { it.symbol == it.cwf.singleBg.slopeArrow }?.arrowCustom ?: NONE.arrowCustom
|
||||||
|
fun value() = values().firstOrNull { it.symbol == it.cwf.singleBg.slopeArrow }?.dynValue ?: NONE.dynValue
|
||||||
}
|
}
|
||||||
|
|
||||||
lateinit var cwf: CustomWatchface
|
lateinit var cwf: CustomWatchface
|
||||||
|
@ -618,6 +638,129 @@ class CustomWatchface : BaseWatchFace() {
|
||||||
SHOW_LOOP_STATUS(CwfMetadataKey.CWF_PREF_WATCH_SHOW_LOOP_STATUS.key, R.string.key_show_external_status),
|
SHOW_LOOP_STATUS(CwfMetadataKey.CWF_PREF_WATCH_SHOW_LOOP_STATUS.key, R.string.key_show_external_status),
|
||||||
SHOW_WEEK_NUMBER(CwfMetadataKey.CWF_PREF_WATCH_SHOW_WEEK_NUMBER.key, R.string.key_show_week_number)
|
SHOW_WEEK_NUMBER(CwfMetadataKey.CWF_PREF_WATCH_SHOW_WEEK_NUMBER.key, R.string.key_show_week_number)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum class ValueMap(val key: String, val min: Double, val max: Double) {
|
||||||
|
SGV(ViewKeys.SGV.key, 39.0, 400.0),
|
||||||
|
SGVLEVEL(JsonKeyValues.SGVLEVEL.key, -1.0, 1.0),
|
||||||
|
DIRECTION(ViewKeys.DIRECTION.key, 1.0, 7.0),
|
||||||
|
DELTA(ViewKeys.DELTA.key, -25.0, 25.0),
|
||||||
|
AVG_DELTA(ViewKeys.AVG_DELTA.key, -25.0, 25.0),
|
||||||
|
UPLOADER_BATTERY(ViewKeys.UPLOADER_BATTERY.key, 0.0, 100.0),
|
||||||
|
RIG_BATTERY(ViewKeys.RIG_BATTERY.key, 0.0, 100.0),
|
||||||
|
TIMESTAMP(ViewKeys.TIMESTAMP.key, 0.0, 60.0),
|
||||||
|
LOOP(ViewKeys.LOOP.key, 0.0, 28.0),
|
||||||
|
DAY(ViewKeys.DAY.key, 1.0, 31.0),
|
||||||
|
DAY_NAME(ViewKeys.DAY_NAME.key, 1.0, 7.0),
|
||||||
|
MONTH(ViewKeys.MONTH.key, 1.0, 12.0),
|
||||||
|
WEEKNUMBER(ViewKeys.WEEKNUMBER.key, 1.0, 53.0);
|
||||||
|
|
||||||
|
fun dynValue(dataValue: Double, dataRange: DataRange, valueRange: DataRange): Int = when {
|
||||||
|
dataValue < dataRange.minData -> dataRange.minData
|
||||||
|
dataValue > dataRange.maxData -> dataRange.maxData
|
||||||
|
else -> dataValue
|
||||||
|
}.let {
|
||||||
|
if (dataRange.minData != dataRange.maxData)
|
||||||
|
(valueRange.minData + (it - dataRange.minData) * (valueRange.maxData - valueRange.minData) / (dataRange.maxData - dataRange.minData)).toInt()
|
||||||
|
else it.toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stepValue(dataValue: Double, range: DataRange, step: Int): Int = step(dataValue, range, step)
|
||||||
|
private fun step(dataValue: Double, dataRange: DataRange, step: Int): Int = when {
|
||||||
|
dataValue < dataRange.minData -> dataRange.minData
|
||||||
|
dataValue >= dataRange.maxData -> dataRange.maxData * 0.9999 // to avoid dataValue == maxData and be out of range
|
||||||
|
else -> dataValue
|
||||||
|
}.let { if (dataRange.minData != dataRange.maxData) (1 + ((it - dataRange.minData) * step) / (dataRange.maxData - dataRange.minData)).toInt() else 0 }
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun fromKey(key: String) = values().firstOrNull { it.key == key }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DynProvider(val cwf: CustomWatchface, val dataJson: JSONObject, val valueMap: ValueMap, val width: Int, val height: Int) {
|
||||||
|
|
||||||
|
private val dynDrawable = mutableMapOf<Int, Drawable?>()
|
||||||
|
private val dynColor = mutableMapOf<Int, Int>()
|
||||||
|
private var dataRange: DataRange? = null
|
||||||
|
private var topRange: DataRange? = null
|
||||||
|
private var leftRange: DataRange? = null
|
||||||
|
private var rotationRange: DataRange? = null
|
||||||
|
val stepDraw: Int
|
||||||
|
get() = dynDrawable.size - 1
|
||||||
|
val stepColor: Int
|
||||||
|
get() = dynColor.size - 1
|
||||||
|
|
||||||
|
val dataValue: Double?
|
||||||
|
get() = when (valueMap) {
|
||||||
|
ValueMap.SGV -> if (cwf.singleBg.sgvString != "---") cwf.singleBg.sgv else null
|
||||||
|
ValueMap.SGVLEVEL -> if (cwf.singleBg.sgvString != "---") cwf.singleBg.sgvLevel.toDouble() else null
|
||||||
|
ValueMap.DIRECTION -> TrendArrowMap.value()
|
||||||
|
ValueMap.DELTA -> cwf.singleBg.deltaMgdl
|
||||||
|
ValueMap.AVG_DELTA -> cwf.singleBg.avgDeltaMgdl
|
||||||
|
ValueMap.RIG_BATTERY -> cwf.status.rigBattery.replace("%", "").toDoubleOrNull()
|
||||||
|
ValueMap.UPLOADER_BATTERY -> cwf.status.battery.replace("%", "").toDoubleOrNull()
|
||||||
|
ValueMap.LOOP -> if (cwf.status.openApsStatus != -1L) ((System.currentTimeMillis() - cwf.status.openApsStatus) / 1000 / 60).toDouble() else null
|
||||||
|
ValueMap.TIMESTAMP -> if (cwf.singleBg.timeStamp != 0L) floor(cwf.timeSince() / (1000 * 60)) else null
|
||||||
|
ValueMap.DAY -> DateTime().dayOfMonth.toDouble()
|
||||||
|
ValueMap.DAY_NAME -> DateTime().dayOfWeek.toDouble()
|
||||||
|
ValueMap.MONTH -> DateTime().monthOfYear.toDouble()
|
||||||
|
ValueMap.WEEKNUMBER -> DateTime().weekOfWeekyear.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTopOffset(): Int = dataRange?.let { dataRange -> topRange?.let { topRange -> dataValue?.let { valueMap.dynValue(it, dataRange, topRange) } ?: topRange.invalidData } } ?: 0
|
||||||
|
fun getLeftOffset(): Int = dataRange?.let { dataRange -> leftRange?.let { leftRange -> dataValue?.let { valueMap.dynValue(it, dataRange, leftRange) } ?: leftRange.invalidData } } ?: 0
|
||||||
|
fun getRotationOffset(): Int = dataRange?.let { dataRange -> rotationRange?.let { rotRange -> dataValue?.let { valueMap.dynValue(it, dataRange, rotRange) } ?: rotRange.invalidData } } ?: 0
|
||||||
|
fun getDrawable() = dataRange?.let { dataRange -> dataValue?.let { dynDrawable[valueMap.stepValue(it, dataRange, stepDraw)] } ?: dynDrawable[0] }
|
||||||
|
fun getColor() = if (stepColor > 0) dataRange?.let { dataRange -> dataValue?.let { dynColor[valueMap.stepValue(it, dataRange, stepColor)] } ?: dynColor[0] } else null
|
||||||
|
private fun load() {
|
||||||
|
dynDrawable[0] = dataJson.optString(INVALIDIMAGE.key)?.let { cwf.resDataMap[it]?.toDrawable(cwf.resources, width, height) }
|
||||||
|
var idx = 1
|
||||||
|
while (dataJson.has("${IMAGE.key}$idx")) {
|
||||||
|
cwf.resDataMap[dataJson.optString("${IMAGE.key}$idx")]?.toDrawable(cwf.resources, width, height).also { dynDrawable[idx] = it }
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
dynColor[0] = cwf.getColor(dataJson.optString(INVALIDCOLOR.key))
|
||||||
|
idx = 1
|
||||||
|
while (dataJson.has("${COLOR.key}$idx")) {
|
||||||
|
dynColor[idx] = cwf.getColor(dataJson.optString("${COLOR.key}$idx"))
|
||||||
|
idx++
|
||||||
|
}
|
||||||
|
DataRange(dataJson.optDouble(MINDATA.key, valueMap.min), dataJson.optDouble(MAXDATA.key, valueMap.max)).let { defaultRange ->
|
||||||
|
dataRange = defaultRange
|
||||||
|
topRange = parseDataRange(dataJson.optJSONObject(TOPOFFSET.key), defaultRange)
|
||||||
|
leftRange = parseDataRange(dataJson.optJSONObject(LEFTOFFSET.key), defaultRange)
|
||||||
|
rotationRange = parseDataRange(dataJson.optJSONObject(ROTATIONOFFSET.key), defaultRange)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
val dynData = mutableMapOf<String, DynProvider>()
|
||||||
|
var dynJson: JSONObject? = null
|
||||||
|
fun init(dynJson: JSONObject?) {
|
||||||
|
this.dynJson = dynJson
|
||||||
|
dynData.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getDyn(cwf: CustomWatchface, key: String, width: Int, height: Int, defaultViewKey: String): DynProvider? = dynData["${defaultViewKey}_$key"]
|
||||||
|
?: dynJson?.optJSONObject(key)?.let { dataJson ->
|
||||||
|
ValueMap.fromKey(dataJson.optString(VALUEKEY.key, defaultViewKey))?.let { valueMap ->
|
||||||
|
DynProvider(cwf, dataJson, valueMap, width, height).also { it.load() }
|
||||||
|
}
|
||||||
|
}?.also { dynData["${defaultViewKey}_$key"] = it }
|
||||||
|
|
||||||
|
private fun parseDataRange(json: JSONObject?, defaultData: DataRange) =
|
||||||
|
json?.let {
|
||||||
|
DataRange(
|
||||||
|
minData = it.optDouble(MINVALUE.key, defaultData.minData),
|
||||||
|
maxData = it.optDouble(MAXVALUE.key, defaultData.maxData),
|
||||||
|
invalidData = it.optInt(INVALIDVALUE.key, defaultData.invalidData)
|
||||||
|
)
|
||||||
|
} ?: defaultData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataRange (val minData: Double, val maxData: Double, val invalidData: Int = 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue