Merge pull request #2730 from Philoul/wear/new_custom_watchface

Wear CWF : Reduce complexity and add new customized capabilities
This commit is contained in:
Milos Kozak 2023-09-04 10:13:39 +02:00 committed by GitHub
commit c50bdd0f78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 324 additions and 187 deletions

View file

@ -2,10 +2,10 @@ package info.nightscout.rx.weardata
import android.content.res.Resources import android.content.res.Resources
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.graphics.Typeface
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.graphics.drawable.PictureDrawable import android.graphics.drawable.PictureDrawable
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.caverock.androidsvg.SVG import com.caverock.androidsvg.SVG
import info.nightscout.shared.R import info.nightscout.shared.R
@ -22,53 +22,73 @@ import java.util.zip.ZipOutputStream
val CUSTOM_VERSION = "1.0" val CUSTOM_VERSION = "1.0"
enum class CwfDrawableFileMap(val key: String, @DrawableRes val icon: Int?, val fileName: String) { enum class ResFileMap(val fileName: String) {
UNKNOWN("unknown", null, "Unknown"), UNKNOWN("Unknown"),
CUSTOM_WATCHFACE("customWatchface", R.drawable.watchface_custom, "CustomWatchface"), CUSTOM_WATCHFACE("CustomWatchface"),
BACKGROUND(ViewKeys.BACKGROUND.key, R.drawable.background, "Background"), BACKGROUND("Background"),
BACKGROUND_HIGH(ViewKeys.BACKGROUND.key, R.drawable.background, "BackgroundHigh"), BACKGROUND_HIGH("BackgroundHigh"),
BACKGROUND_LOW(ViewKeys.BACKGROUND.key, R.drawable.background, "BackgroundLow"), BACKGROUND_LOW("BackgroundLow"),
COVER_CHART(ViewKeys.COVER_CHART.key, null, "CoverChart"), COVER_CHART("CoverChart"),
COVER_PLATE(ViewKeys.COVER_PLATE.key, R.drawable.simplified_dial, "CoverPlate"), COVER_CHART_HIGH("CoverChartHigh"),
HOUR_HAND(ViewKeys.HOUR_HAND.key, R.drawable.hour_hand, "HourHand"), COVER_CHART_LOW("CoverChartLow"),
MINUTE_HAND(ViewKeys.MINUTE_HAND.key, R.drawable.minute_hand, "MinuteHand"), COVER_PLATE("CoverPlate"),
SECOND_HAND(ViewKeys.SECOND_HAND.key, R.drawable.second_hand, "SecondHand"); COVER_PLATE_HIGH("CoverPlateHigh"),
COVER_PLATE_LOW("CoverPlateLow"),
HOUR_HAND("HourHand"),
HOUR_HAND_HIGH("HourHandHigh"),
HOUR_HAND_LOW("HourHandLow"),
MINUTE_HAND("MinuteHand"),
MINUTE_HAND_HIGH("MinuteHandHigh"),
MINUTE_HAND_LOW("MinuteHandLow"),
SECOND_HAND("SecondHand"),
SECOND_HAND_HIGH("SecondHandHigh"),
SECOND_HAND_LOW("SecondHandLow"),
ARROW_NONE("ArrowNone"),
ARROW_DOUBLE_UP("ArrowDoubleUp"),
ARROW_SINGLE_UP("ArrowSingleUp"),
ARROW_FORTY_FIVE_UP("Arrow45Up"),
ARROW_FLAT("ArrowFlat"),
ARROW_FORTY_FIVE_DOWN("Arrow45Down"),
ARROW_SINGLE_DOWN("ArrowSingleDown"),
ARROW_DOUBLE_DOWN("ArrowDoubleDown"),
FONT1("Font1"),
FONT2("Font2"),
FONT3("Font3"),
FONT4("Font4");
companion object { companion object {
fun fromKey(key: String): CwfDrawableFileMap = fun fromFileName(file: String): ResFileMap = values().firstOrNull { it.fileName == file.substringBeforeLast(".") } ?: UNKNOWN
values().firstOrNull { it.key == key } ?: UNKNOWN
fun fromFileName(file: String): CwfDrawableFileMap = values().firstOrNull { it.fileName == file.substringBeforeLast(".") } ?: UNKNOWN
} }
} }
enum class DrawableFormat(val extension: String) { enum class ResFormat(val extension: String) {
UNKNOWN(""), UNKNOWN(""),
SVG("svg"), SVG("svg"),
JPG("jpg"), JPG("jpg"),
PNG("png"); PNG("png"),
TTF("ttf");
companion object { companion object {
fun fromFileName(fileName: String): DrawableFormat = fun fromFileName(fileName: String): ResFormat =
values().firstOrNull { it.extension == fileName.substringAfterLast(".") } ?: UNKNOWN values().firstOrNull { it.extension == fileName.substringAfterLast(".").lowercase() } ?: UNKNOWN
} }
} }
@Serializable @Serializable
data class DrawableData(val value: ByteArray, val format: DrawableFormat) { data class ResData(val value: ByteArray, val format: ResFormat) {
fun toDrawable(resources: Resources): Drawable? { fun toDrawable(resources: Resources): Drawable? {
try { try {
return when (format) { return when (format) {
DrawableFormat.PNG, DrawableFormat.JPG -> { ResFormat.PNG, ResFormat.JPG -> {
val bitmap = BitmapFactory.decodeByteArray(value, 0, value.size) val bitmap = BitmapFactory.decodeByteArray(value, 0, value.size)
BitmapDrawable(resources, bitmap) BitmapDrawable(resources, bitmap)
} }
DrawableFormat.SVG -> { ResFormat.SVG -> {
val svg = SVG.getFromInputStream(ByteArrayInputStream(value)) val svg = SVG.getFromInputStream(ByteArrayInputStream(value))
val picture = svg.renderToPicture() val picture = svg.renderToPicture()
PictureDrawable(picture).apply { PictureDrawable(picture).apply {
@ -76,7 +96,37 @@ data class DrawableData(val value: ByteArray, val format: DrawableFormat) {
} }
} }
else -> null else -> null
}
} catch (e: Exception) {
return null
}
}
fun toTypeface(): Typeface? {
try {
return when (format) {
ResFormat.TTF -> {
// Workaround with temporary File, Typeface.createFromFileDescriptor(null, value, 0, value.size) more simple not available
File.createTempFile("temp", ".ttf").let { tempFile ->
FileOutputStream(tempFile).let { fileOutputStream ->
fileOutputStream.write(value)
fileOutputStream.close()
}
Typeface.createFromFile(tempFile).let {
if (!tempFile.delete()) {
// delete tempfile after usage
}
it
}
}
}
else -> {
null
}
} }
} catch (e: Exception) { } catch (e: Exception) {
return null return null
@ -84,11 +134,11 @@ data class DrawableData(val value: ByteArray, val format: DrawableFormat) {
} }
} }
typealias CwfDrawableDataMap = MutableMap<CwfDrawableFileMap, DrawableData> typealias CwfResDataMap = MutableMap<ResFileMap, ResData>
typealias CwfMetadataMap = MutableMap<CwfMetadataKey, String> typealias CwfMetadataMap = MutableMap<CwfMetadataKey, String>
@Serializable @Serializable
data class CwfData(val json: String, var metadata: CwfMetadataMap, val drawableDatas: CwfDrawableDataMap) data class CwfData(val json: String, var metadata: CwfMetadataMap, val resDatas: CwfResDataMap)
enum class CwfMetadataKey(val key: String, @StringRes val label: Int, val isPref: Boolean) { enum class CwfMetadataKey(val key: String, @StringRes val label: Int, val isPref: Boolean) {
@ -98,7 +148,7 @@ enum class CwfMetadataKey(val key: String, @StringRes val label: Int, val isPref
CWF_CREATED_AT("created_at", R.string.metadata_label_watchface_created_at, false), CWF_CREATED_AT("created_at", R.string.metadata_label_watchface_created_at, false),
CWF_VERSION("cwf_version", R.string.metadata_label_plugin_version, false), CWF_VERSION("cwf_version", R.string.metadata_label_plugin_version, false),
CWF_AUTHOR_VERSION("author_version", R.string.metadata_label_watchface_name_version, false), CWF_AUTHOR_VERSION("author_version", R.string.metadata_label_watchface_name_version, false),
CWF_COMMENT("comment", R.string.metadata_label_watchface_comment, false), // label not planed to be used for CWF_COMMENT CWF_COMMENT("comment", R.string.metadata_label_watchface_infos, false), // label not planed to be used for CWF_COMMENT
CWF_AUTHORIZATION("cwf_authorization", R.string.metadata_label_watchface_authorization, false), CWF_AUTHORIZATION("cwf_authorization", R.string.metadata_label_watchface_authorization, false),
CWF_PREF_WATCH_SHOW_DETAILED_IOB("key_show_detailed_iob", R.string.pref_show_detailed_iob, true), CWF_PREF_WATCH_SHOW_DETAILED_IOB("key_show_detailed_iob", R.string.pref_show_detailed_iob, true),
CWF_PREF_WATCH_SHOW_DETAILED_DELTA("key_show_detailed_delta", R.string.pref_show_detailed_delta, true), CWF_PREF_WATCH_SHOW_DETAILED_DELTA("key_show_detailed_delta", R.string.pref_show_detailed_delta, true),
@ -210,7 +260,10 @@ enum class JsonKeyValues(val key: String, val jsonKey: JsonKeys) {
BOLD_ITALIC("bold_italic", JsonKeys.FONTSTYLE), BOLD_ITALIC("bold_italic", JsonKeys.FONTSTYLE),
ITALIC("italic", JsonKeys.FONTSTYLE), ITALIC("italic", JsonKeys.FONTSTYLE),
BGCOLOR("bgColor", JsonKeys.COLOR), BGCOLOR("bgColor", JsonKeys.COLOR),
BGCOLOR1("bgColor", JsonKeys.FONTCOLOR) FONT1("font1", JsonKeys.FONTCOLOR),
FONT2("font2", JsonKeys.FONTCOLOR),
FONT3("font3", JsonKeys.FONTCOLOR),
FONT4("font4", JsonKeys.FONTCOLOR)
} }
enum class ViewType(@StringRes val comment: Int?) { enum class ViewType(@StringRes val comment: Int?) {
@ -229,7 +282,7 @@ class ZipWatchfaceFormat {
fun loadCustomWatchface(cwfFile: File, authorization: Boolean): CwfData? { fun loadCustomWatchface(cwfFile: File, authorization: Boolean): CwfData? {
var json = JSONObject() var json = JSONObject()
var metadata: CwfMetadataMap = mutableMapOf() var metadata: CwfMetadataMap = mutableMapOf()
val drawableDatas: CwfDrawableDataMap = mutableMapOf() val resDatas: CwfResDataMap = mutableMapOf()
try { try {
val zipInputStream = ZipInputStream(cwfFile.inputStream()) val zipInputStream = ZipInputStream(cwfFile.inputStream())
@ -253,18 +306,18 @@ class ZipWatchfaceFormat {
metadata[CwfMetadataKey.CWF_FILENAME] = cwfFile.name metadata[CwfMetadataKey.CWF_FILENAME] = cwfFile.name
metadata[CwfMetadataKey.CWF_AUTHORIZATION] = authorization.toString() metadata[CwfMetadataKey.CWF_AUTHORIZATION] = authorization.toString()
} else { } else {
val cwfDrawableFileMap = CwfDrawableFileMap.fromFileName(entryName) val cwfResFileMap = ResFileMap.fromFileName(entryName)
val drawableFormat = DrawableFormat.fromFileName(entryName) val drawableFormat = ResFormat.fromFileName(entryName)
if (cwfDrawableFileMap != CwfDrawableFileMap.UNKNOWN && drawableFormat != DrawableFormat.UNKNOWN) { if (cwfResFileMap != ResFileMap.UNKNOWN && drawableFormat != ResFormat.UNKNOWN) {
drawableDatas[cwfDrawableFileMap] = DrawableData(byteArrayOutputStream.toByteArray(), drawableFormat) resDatas[cwfResFileMap] = ResData(byteArrayOutputStream.toByteArray(), drawableFormat)
} }
} }
zipEntry = zipInputStream.nextEntry zipEntry = zipInputStream.nextEntry
} }
// Valid CWF file must contains a valid json file with a name within metadata and a custom watchface image // Valid CWF file must contains a valid json file with a name within metadata and a custom watchface image
return if (metadata.containsKey(CwfMetadataKey.CWF_NAME) && drawableDatas.containsKey(CwfDrawableFileMap.CUSTOM_WATCHFACE)) return if (metadata.containsKey(CwfMetadataKey.CWF_NAME) && resDatas.containsKey(ResFileMap.CUSTOM_WATCHFACE))
CwfData(json.toString(4), metadata, drawableDatas) CwfData(json.toString(4), metadata, resDatas)
else else
null null
@ -286,10 +339,10 @@ class ZipWatchfaceFormat {
zipOutputStream.closeEntry() zipOutputStream.closeEntry()
// Ajouter les fichiers divers au ZIP // Ajouter les fichiers divers au ZIP
for (drawableData in customWatchface.drawableDatas) { for (resData in customWatchface.resDatas) {
val fileEntry = ZipEntry("${drawableData.key.fileName}.${drawableData.value.format.extension}") val fileEntry = ZipEntry("${resData.key.fileName}.${resData.value.format.extension}")
zipOutputStream.putNextEntry(fileEntry) zipOutputStream.putNextEntry(fileEntry)
zipOutputStream.write(drawableData.value.value) zipOutputStream.write(resData.value.value)
zipOutputStream.closeEntry() zipOutputStream.closeEntry()
} }
zipOutputStream.close() zipOutputStream.close()

View file

@ -40,7 +40,7 @@
<string name="metadata_wear_import_filename">Failo pavadinimas: %1$s</string> <string name="metadata_wear_import_filename">Failo pavadinimas: %1$s</string>
<string name="metadata_label_plugin_version">Įskiepio versija: %1$s</string> <string name="metadata_label_plugin_version">Įskiepio versija: %1$s</string>
<string name="metadata_label_watchface_name_version">Pavadinimas: %1$s (%2$s)</string> <string name="metadata_label_watchface_name_version">Pavadinimas: %1$s (%2$s)</string>
<string name="metadata_label_watchface_comment">Komentaras: %1$s</string> <string name="metadata_label_watchface_info">Komentaras: %1$s</string>
<string name="pref_show_iob">Rodyti AIO</string> <string name="pref_show_iob">Rodyti AIO</string>
<string name="pref_show_detailed_iob">Rodyti detalų AIO</string> <string name="pref_show_detailed_iob">Rodyti detalų AIO</string>
<string name="pref_show_cob">Rodyti AAO</string> <string name="pref_show_cob">Rodyti AAO</string>

View file

@ -46,7 +46,7 @@
<string name="metadata_wear_import_filename">File name: %1$s</string> <string name="metadata_wear_import_filename">File name: %1$s</string>
<string name="metadata_label_plugin_version">Plugin version: %1$s</string> <string name="metadata_label_plugin_version">Plugin version: %1$s</string>
<string name="metadata_label_watchface_name_version">Name: %1$s (%2$s)</string> <string name="metadata_label_watchface_name_version">Name: %1$s (%2$s)</string>
<string name="metadata_label_watchface_comment">Comment: %1$s</string> <string name="metadata_label_watchface_infos">Info: %1$s</string>
<string name="metadata_label_watchface_authorization" translatable="false">%1$s</string> <string name="metadata_label_watchface_authorization" translatable="false">%1$s</string>
<string name="pref_show_iob">Show IOB</string> <string name="pref_show_iob">Show IOB</string>
<string name="pref_show_detailed_iob">Show detailed IOB</string> <string name="pref_show_detailed_iob">Show detailed IOB</string>

View file

@ -18,7 +18,7 @@ import info.nightscout.rx.events.EventMobileDataToWear
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.weardata.CUSTOM_VERSION import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfData import info.nightscout.rx.weardata.CwfData
import info.nightscout.rx.weardata.CwfDrawableFileMap import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfMetadataKey.CWF_AUTHOR import info.nightscout.rx.weardata.CwfMetadataKey.CWF_AUTHOR
import info.nightscout.rx.weardata.CwfMetadataKey.CWF_AUTHOR_VERSION import info.nightscout.rx.weardata.CwfMetadataKey.CWF_AUTHOR_VERSION
import info.nightscout.rx.weardata.CwfMetadataKey.CWF_CREATED_AT import info.nightscout.rx.weardata.CwfMetadataKey.CWF_CREATED_AT
@ -89,7 +89,7 @@ class CustomWatchfaceImportListActivity: TranslatedDaggerAppCompatActivity() {
override fun onBindViewHolder(holder: CwfFileViewHolder, position: Int) { override fun onBindViewHolder(holder: CwfFileViewHolder, position: Int) {
val customWatchfaceFile = customWatchfaceFileList[position] val customWatchfaceFile = customWatchfaceFileList[position]
val metadata = customWatchfaceFile.metadata val metadata = customWatchfaceFile.metadata
val drawable = customWatchfaceFile.drawableDatas[CwfDrawableFileMap val drawable = customWatchfaceFile.resDatas[ResFileMap
.CUSTOM_WATCHFACE]?.toDrawable(resources) .CUSTOM_WATCHFACE]?.toDrawable(resources)
with(holder.customWatchfaceImportListItemBinding) { with(holder.customWatchfaceImportListItemBinding) {
filelistName.text = rh.gs(info.nightscout.shared.R.string.metadata_wear_import_filename, metadata[CWF_FILENAME]) filelistName.text = rh.gs(info.nightscout.shared.R.string.metadata_wear_import_filename, metadata[CWF_FILENAME])

View file

@ -18,7 +18,7 @@ import info.nightscout.rx.events.EventMobileToWear
import info.nightscout.rx.events.EventWearUpdateGui import info.nightscout.rx.events.EventWearUpdateGui
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.weardata.CwfData import info.nightscout.rx.weardata.CwfData
import info.nightscout.rx.weardata.CwfDrawableFileMap import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfMetadataKey import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.EventData import info.nightscout.rx.weardata.EventData
import info.nightscout.shared.extensions.toVisibility import info.nightscout.shared.extensions.toVisibility
@ -111,7 +111,7 @@ class WearFragment : DaggerFragment() {
wearPlugin.savedCustomWatchface?.let { wearPlugin.savedCustomWatchface?.let {
wearPlugin.checkCustomWatchfacePreferences() wearPlugin.checkCustomWatchfacePreferences()
binding.customName.text = rh.gs(R.string.wear_custom_watchface, it.metadata[CwfMetadataKey.CWF_NAME]) binding.customName.text = rh.gs(R.string.wear_custom_watchface, it.metadata[CwfMetadataKey.CWF_NAME])
binding.coverChart.setImageDrawable(it.drawableDatas[CwfDrawableFileMap.CUSTOM_WATCHFACE]?.toDrawable(resources)) binding.coverChart.setImageDrawable(it.resDatas[ResFileMap.CUSTOM_WATCHFACE]?.toDrawable(resources))
binding.infosCustom.visibility = View.VISIBLE binding.infosCustom.visibility = View.VISIBLE
} ?:apply { } ?:apply {
binding.customName.text = rh.gs(R.string.wear_custom_watchface, "") binding.customName.text = rh.gs(R.string.wear_custom_watchface, "")

View file

@ -20,7 +20,7 @@ import info.nightscout.rx.events.EventWearUpdateGui
import info.nightscout.rx.logging.AAPSLogger import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.rx.weardata.CUSTOM_VERSION import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfDrawableFileMap import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfMetadataKey import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.JsonKeyValues import info.nightscout.rx.weardata.JsonKeyValues
@ -86,7 +86,7 @@ class CwfInfosActivity : TranslatedDaggerAppCompatActivity() {
wearPlugin.savedCustomWatchface?.let { wearPlugin.savedCustomWatchface?.let {
val cwfAuthorization = sp.getBoolean(info.nightscout.core.utils.R.string.key_wear_custom_watchface_autorization, false) val cwfAuthorization = sp.getBoolean(info.nightscout.core.utils.R.string.key_wear_custom_watchface_autorization, false)
val metadata = it.metadata val metadata = it.metadata
val drawable = it.drawableDatas[CwfDrawableFileMap.CUSTOM_WATCHFACE]?.toDrawable(resources) val drawable = it.resDatas[ResFileMap.CUSTOM_WATCHFACE]?.toDrawable(resources)
binding.customWatchface.setImageDrawable(drawable) binding.customWatchface.setImageDrawable(drawable)
title = rh.gs(CwfMetadataKey.CWF_NAME.label, metadata[CwfMetadataKey.CWF_NAME]) title = rh.gs(CwfMetadataKey.CWF_NAME.label, metadata[CwfMetadataKey.CWF_NAME])
metadata[CwfMetadataKey.CWF_AUTHOR_VERSION]?.let { authorVersion -> metadata[CwfMetadataKey.CWF_AUTHOR_VERSION]?.let { authorVersion ->

View file

@ -4,12 +4,14 @@ package info.nightscout.androidaps.watchfaces
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Resources
import android.graphics.Color import android.graphics.Color
import android.graphics.ColorFilter import android.graphics.ColorFilter
import android.graphics.ColorMatrix import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter import android.graphics.ColorMatrixColorFilter
import android.graphics.Point import android.graphics.Point
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.Drawable
import android.support.wearable.watchface.WatchFaceStyle import android.support.wearable.watchface.WatchFaceStyle
import android.util.TypedValue import android.util.TypedValue
import android.view.Gravity import android.view.Gravity
@ -33,12 +35,12 @@ import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
import info.nightscout.rx.logging.LTag import info.nightscout.rx.logging.LTag
import info.nightscout.rx.weardata.CUSTOM_VERSION import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfData import info.nightscout.rx.weardata.CwfData
import info.nightscout.rx.weardata.CwfDrawableFileMap import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfDrawableDataMap import info.nightscout.rx.weardata.CwfResDataMap
import info.nightscout.rx.weardata.CwfMetadataKey import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.DrawableData import info.nightscout.rx.weardata.ResData
import info.nightscout.rx.weardata.DrawableFormat import info.nightscout.rx.weardata.ResFormat
import info.nightscout.rx.weardata.EventData import info.nightscout.rx.weardata.EventData
import info.nightscout.rx.weardata.JsonKeyValues import info.nightscout.rx.weardata.JsonKeyValues
import info.nightscout.rx.weardata.JsonKeys.* import info.nightscout.rx.weardata.JsonKeys.*
@ -61,10 +63,11 @@ 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 bgColor = Color.WHITE private var bgColor = Color.WHITE
private var resDataMap: CwfResDataMap = mutableMapOf()
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
FontMap.init(context) FontMap.init(context, resDataMap)
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -88,7 +91,7 @@ class CustomWatchface : BaseWatchFace() {
@SuppressLint("UseCompatLoadingForDrawables") @SuppressLint("UseCompatLoadingForDrawables")
override fun setDataFields() { override fun setDataFields() {
super.setDataFields() super.setDataFields()
binding.direction2.setImageDrawable(this.resources.getDrawable(TrendArrowMap.icon(singleBg.slopeArrow))) binding.direction2.setImageDrawable(TrendArrowMap.drawable(singleBg.slopeArrow, resources, resDataMap))
// rotate the second hand. // rotate the second hand.
binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f
// rotate the minute hand. // rotate the minute hand.
@ -100,7 +103,7 @@ class CustomWatchface : BaseWatchFace() {
override fun setColorDark() { override fun setColorDark() {
setWatchfaceStyle() setWatchfaceStyle()
binding.sgv.setTextColor(bgColor) binding.sgv.setTextColor(bgColor)
binding.direction2.colorFilter = changeDrawableColor(bgColor) binding.direction2.setColorFilter(changeDrawableColor(bgColor))
if (ageLevel != 1) if (ageLevel != 1)
binding.timestamp.setTextColor(ContextCompat.getColor(this, R.color.dark_TimestampOld)) binding.timestamp.setTextColor(ContextCompat.getColor(this, R.color.dark_TimestampOld))
@ -128,7 +131,6 @@ class CustomWatchface : BaseWatchFace() {
getString(R.string.hour_minute_second, dateUtil.hourString(), dateUtil.minuteString(), dateUtil.secondString()) getString(R.string.hour_minute_second, dateUtil.hourString(), dateUtil.minuteString(), dateUtil.secondString())
else else
getString(R.string.hour_minute, dateUtil.hourString(), dateUtil.minuteString()) getString(R.string.hour_minute, dateUtil.hourString(), dateUtil.minuteString())
//binding.time.text = "${dateUtil.hourString()}:${dateUtil.minuteString()}" + if (showSecond) ":${dateUtil.secondString()}" else ""
binding.second.text = dateUtil.secondString() binding.second.text = dateUtil.secondString()
// rotate the second hand. // rotate the second hand.
binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f
@ -146,7 +148,8 @@ 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)
val drawableDataMap = it.customWatchfaceData.drawableDatas resDataMap = it.customWatchfaceData.resDatas
FontMap.init(context, resDataMap)
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))
midColor = getColor(json.optString(MIDCOLOR.key), ContextCompat.getColor(this, R.color.inrange)) midColor = getColor(json.optString(MIDCOLOR.key), ContextCompat.getColor(this, R.color.inrange))
@ -167,17 +170,10 @@ class CustomWatchface : BaseWatchFace() {
-1L -> lowColor -1L -> lowColor
else -> midColor else -> midColor
} }
val backGroundDrawable = when (singleBg.sgvLevel) {
1L -> drawableDataMap[CwfDrawableFileMap.BACKGROUND_HIGH]?.toDrawable(resources) ?: drawableDataMap[CwfDrawableFileMap.BACKGROUND]?.toDrawable(resources)
0L -> drawableDataMap[CwfDrawableFileMap.BACKGROUND]?.toDrawable(resources)
-1L -> drawableDataMap[CwfDrawableFileMap.BACKGROUND_LOW]?.toDrawable(resources) ?: drawableDataMap[CwfDrawableFileMap.BACKGROUND]?.toDrawable(resources)
else -> drawableDataMap[CwfDrawableFileMap.BACKGROUND]?.toDrawable(resources)
}
binding.mainLayout.forEach { view -> binding.mainLayout.forEach { view ->
ViewMap.fromId(view.id)?.let { id -> ViewMap.fromId(view.id)?.let { id ->
if (json.has(id.key)) { json.optJSONObject(id.key)?.also { viewJson ->
val viewJson = json.getJSONObject(id.key)
val width = (viewJson.optInt(WIDTH.key) * zoomFactor).toInt() val width = (viewJson.optInt(WIDTH.key) * zoomFactor).toInt()
val height = (viewJson.optInt(HEIGHT.key) * zoomFactor).toInt() val height = (viewJson.optInt(HEIGHT.key) * zoomFactor).toInt()
val params = FrameLayout.LayoutParams(width, height) val params = FrameLayout.LayoutParams(width, height)
@ -202,18 +198,14 @@ class CustomWatchface : BaseWatchFace() {
is ImageView -> { is ImageView -> {
view.clearColorFilter() view.clearColorFilter()
val drawable = if (id.key == CwfDrawableFileMap.BACKGROUND.key) id.drawable(resources, resDataMap, singleBg.sgvLevel)?.let {
backGroundDrawable if (viewJson.has(COLOR.key)) // Note only works on bitmap (png or jpg) or xml included into res, not for svg files
else
drawableDataMap[CwfDrawableFileMap.fromKey(id.key)]?.toDrawable(resources)
drawable?.let {
if (viewJson.has(COLOR.key))
it.colorFilter = changeDrawableColor(getColor(viewJson.optString(COLOR.key))) it.colorFilter = changeDrawableColor(getColor(viewJson.optString(COLOR.key)))
else else
it.clearColorFilter() it.clearColorFilter()
view.setImageDrawable(it) view.setImageDrawable(it)
} ?: apply { } ?: apply {
view.setImageDrawable(CwfDrawableFileMap.fromKey(id.key).icon?.let { context.getDrawable(it) }) view.setImageDrawable(id.defaultDrawable?.let {resources.getDrawable(it)})
if (viewJson.has(COLOR.key)) if (viewJson.has(COLOR.key))
view.setColorFilter(getColor(viewJson.optString(COLOR.key))) view.setColorFilter(getColor(viewJson.optString(COLOR.key)))
else else
@ -222,7 +214,7 @@ class CustomWatchface : BaseWatchFace() {
} }
} }
} else { } ?:apply {
view.visibility = View.GONE view.visibility = View.GONE
if (view is TextView) { if (view is TextView) {
view.text = "" view.text = ""
@ -305,9 +297,9 @@ class CustomWatchface : BaseWatchFace() {
} }
} }
val metadataMap = ZipWatchfaceFormat.loadMetadata(json) val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
val drawableDataMap: CwfDrawableDataMap = mutableMapOf() val drawableDataMap: CwfResDataMap = mutableMapOf()
getResourceByteArray(info.nightscout.shared.R.drawable.watchface_custom)?.let { getResourceByteArray(info.nightscout.shared.R.drawable.watchface_custom)?.let {
drawableDataMap[CwfDrawableFileMap.CUSTOM_WATCHFACE] = DrawableData(it, DrawableFormat.PNG) drawableDataMap[ResFileMap.CUSTOM_WATCHFACE] = ResData(it, ResFormat.PNG)
} }
return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap)) return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap))
} }
@ -327,7 +319,7 @@ class CustomWatchface : BaseWatchFace() {
JsonKeyValues.VISIBLE.key -> pref.toVisibility() JsonKeyValues.VISIBLE.key -> pref.toVisibility()
JsonKeyValues.INVISIBLE.key -> pref.toVisibilityKeepSpace() JsonKeyValues.INVISIBLE.key -> pref.toVisibilityKeepSpace()
JsonKeyValues.GONE.key -> View.GONE JsonKeyValues.GONE.key -> View.GONE
else -> View.GONE else -> View.GONE
} }
private fun getVisibility(visibility: Int): String = when (visibility) { private fun getVisibility(visibility: Int): String = when (visibility) {
@ -373,7 +365,11 @@ class CustomWatchface : BaseWatchFace() {
if (color == JsonKeyValues.BGCOLOR.key) if (color == JsonKeyValues.BGCOLOR.key)
bgColor bgColor
else else
try { Color.parseColor(color) } catch (e: Exception) { defaultColor } try {
Color.parseColor(color)
} catch (e: Exception) {
defaultColor
}
private fun manageSpecificViews() { private fun manageSpecificViews() {
//Background should fill all the watchface and must be visible //Background should fill all the watchface and must be visible
@ -388,41 +384,98 @@ class CustomWatchface : BaseWatchFace() {
// Update timePeriod visibility // Update timePeriod visibility
binding.timePeriod.visibility = (binding.timePeriod.visibility == View.VISIBLE && android.text.format.DateFormat.is24HourFormat(this).not()).toVisibility() binding.timePeriod.visibility = (binding.timePeriod.visibility == View.VISIBLE && android.text.format.DateFormat.is24HourFormat(this).not()).toVisibility()
} }
private enum class ViewMap(val key: String, @IdRes val id: Int, @StringRes val pref: Int?) {
BACKGROUND(ViewKeys.BACKGROUND.key, R.id.background, null), private enum class ViewMap(
CHART(ViewKeys.CHART.key, R.id.chart, null), val key: String,
COVER_CHART(ViewKeys.COVER_CHART.key, R.id.cover_chart, null), @IdRes val id: Int,
FREETEXT1(ViewKeys.FREETEXT1.key, R.id.freetext1, null), @StringRes val pref: Int?,
FREETEXT2(ViewKeys.FREETEXT2.key, R.id.freetext2, null), @IdRes val defaultDrawable: Int?,
FREETEXT3(ViewKeys.FREETEXT3.key, R.id.freetext3, null), val customDrawable: ResFileMap?,
FREETEXT4(ViewKeys.FREETEXT4.key, R.id.freetext4, null), val customHigh:ResFileMap?,
IOB1(ViewKeys.IOB1.key, R.id.iob1, R.string.key_show_iob), val customLow: ResFileMap?
IOB2(ViewKeys.IOB2.key, R.id.iob2, R.string.key_show_iob), ) {
COB1(ViewKeys.COB1.key, R.id.cob1, R.string.key_show_cob),
COB2(ViewKeys.COB2.key, R.id.cob2, R.string.key_show_cob), BACKGROUND(
DELTA(ViewKeys.DELTA.key, R.id.delta, R.string.key_show_delta), ViewKeys.BACKGROUND.key,
AVG_DELTA(ViewKeys.AVG_DELTA.key, R.id.avg_delta, R.string.key_show_avg_delta), R.id.background,
UPLOADER_BATTERY(ViewKeys.UPLOADER_BATTERY.key, R.id.uploader_battery, R.string.key_show_uploader_battery), null,
RIG_BATTERY(ViewKeys.RIG_BATTERY.key, R.id.rig_battery, R.string.key_show_rig_battery), info.nightscout.shared.R.drawable.background,
BASALRATE(ViewKeys.BASALRATE.key, R.id.basalRate, R.string.key_show_temp_basal), ResFileMap.BACKGROUND,
BGI(ViewKeys.BGI.key, R.id.bgi, R.string.key_show_bgi), ResFileMap.BACKGROUND_HIGH,
TIME(ViewKeys.TIME.key, R.id.time, null), ResFileMap.BACKGROUND_LOW
HOUR(ViewKeys.HOUR.key, R.id.hour, null), ),
MINUTE(ViewKeys.MINUTE.key, R.id.minute, null), CHART(ViewKeys.CHART.key, R.id.chart, null, null, null, null, null),
SECOND(ViewKeys.SECOND.key, R.id.second, R.string.key_show_seconds), COVER_CHART(
TIMEPERIOD(ViewKeys.TIMEPERIOD.key, R.id.timePeriod, null), ViewKeys.COVER_CHART.key,
DAY_NAME(ViewKeys.DAY_NAME.key, R.id.day_name, null), R.id.cover_chart,
DAY(ViewKeys.DAY.key, R.id.day, null), null,
MONTH(ViewKeys.MONTH.key, R.id.month, null), null,
LOOP(ViewKeys.LOOP.key, R.id.loop, R.string.key_show_external_status), ResFileMap.COVER_CHART,
DIRECTION(ViewKeys.DIRECTION.key, R.id.direction2, R.string.key_show_direction), ResFileMap.COVER_CHART_HIGH,
TIMESTAMP(ViewKeys.TIMESTAMP.key, R.id.timestamp, R.string.key_show_ago), ResFileMap.COVER_CHART_LOW
SGV(ViewKeys.SGV.key, R.id.sgv, R.string.key_show_bg), ),
COVER_PLATE(ViewKeys.COVER_PLATE.key, R.id.cover_plate, null), FREETEXT1(ViewKeys.FREETEXT1.key, R.id.freetext1, null, null, null, null, null),
HOUR_HAND(ViewKeys.HOUR_HAND.key, R.id.hour_hand, null), FREETEXT2(ViewKeys.FREETEXT2.key, R.id.freetext2, null, null, null, null, null),
MINUTE_HAND(ViewKeys.MINUTE_HAND.key, R.id.minute_hand, null), FREETEXT3(ViewKeys.FREETEXT3.key, R.id.freetext3, null, null, null, null, null),
SECOND_HAND(ViewKeys.SECOND_HAND.key, R.id.second_hand, R.string.key_show_seconds); FREETEXT4(ViewKeys.FREETEXT4.key, R.id.freetext4, null, null, null, null, null),
IOB1(ViewKeys.IOB1.key, R.id.iob1, R.string.key_show_iob, null, null, null, null),
IOB2(ViewKeys.IOB2.key, R.id.iob2, R.string.key_show_iob, null, null, null, null),
COB1(ViewKeys.COB1.key, R.id.cob1, R.string.key_show_cob, null, null, null, null),
COB2(ViewKeys.COB2.key, R.id.cob2, R.string.key_show_cob, null, null, null, null),
DELTA(ViewKeys.DELTA.key, R.id.delta, R.string.key_show_delta, null, null, null, null),
AVG_DELTA(ViewKeys.AVG_DELTA.key, R.id.avg_delta, R.string.key_show_avg_delta, null, null, null, null),
UPLOADER_BATTERY(ViewKeys.UPLOADER_BATTERY.key, R.id.uploader_battery, R.string.key_show_uploader_battery, null, null, null, null),
RIG_BATTERY(ViewKeys.RIG_BATTERY.key, R.id.rig_battery, R.string.key_show_rig_battery, null, null, null, null),
BASALRATE(ViewKeys.BASALRATE.key, R.id.basalRate, R.string.key_show_temp_basal, null, null, null, null),
BGI(ViewKeys.BGI.key, R.id.bgi, R.string.key_show_bgi, null, null, null, null),
TIME(ViewKeys.TIME.key, R.id.time, null, null, null, null, null),
HOUR(ViewKeys.HOUR.key, R.id.hour, null, null, null, null, null),
MINUTE(ViewKeys.MINUTE.key, R.id.minute, null, null, null, null, null),
SECOND(ViewKeys.SECOND.key, R.id.second, R.string.key_show_seconds, null, null, null, null),
TIMEPERIOD(ViewKeys.TIMEPERIOD.key, R.id.timePeriod, null, null, null, null, null),
DAY_NAME(ViewKeys.DAY_NAME.key, R.id.day_name, null, null, null, null, null),
DAY(ViewKeys.DAY.key, R.id.day, null, null, null, null, null),
MONTH(ViewKeys.MONTH.key, R.id.month, null, null, null, null, null),
LOOP(ViewKeys.LOOP.key, R.id.loop, R.string.key_show_external_status, null, null, null, null),
DIRECTION(ViewKeys.DIRECTION.key, R.id.direction2, R.string.key_show_direction, null, null, null, null),
TIMESTAMP(ViewKeys.TIMESTAMP.key, R.id.timestamp, R.string.key_show_ago, null, null, null, null),
SGV(ViewKeys.SGV.key, R.id.sgv, R.string.key_show_bg, null, null, null, null),
COVER_PLATE(
ViewKeys.COVER_PLATE.key,
R.id.cover_plate,
null,
null,
ResFileMap.COVER_PLATE,
ResFileMap.COVER_PLATE_HIGH,
ResFileMap.COVER_PLATE_LOW
),
HOUR_HAND(
ViewKeys.HOUR_HAND.key,
R.id.hour_hand,
null,
info.nightscout.shared.R.drawable.hour_hand,
ResFileMap.HOUR_HAND,
ResFileMap.HOUR_HAND_HIGH,
ResFileMap.HOUR_HAND_LOW
),
MINUTE_HAND(
ViewKeys.MINUTE_HAND.key,
R.id.minute_hand,
null,
info.nightscout.shared.R.drawable.minute_hand,
ResFileMap.MINUTE_HAND,
ResFileMap.MINUTE_HAND_HIGH,
ResFileMap.MINUTE_HAND_LOW
),
SECOND_HAND(
ViewKeys.SECOND_HAND.key,
R.id.second_hand,
R.string.key_show_seconds,
info.nightscout.shared.R.drawable.second_hand,
ResFileMap.SECOND_HAND,
ResFileMap.SECOND_HAND_HIGH,
ResFileMap.SECOND_HAND_LOW
);
companion object { companion object {
@ -431,85 +484,116 @@ class CustomWatchface : BaseWatchFace() {
fun visibility(sp: SP): Boolean = this.pref?.let { sp.getBoolean(it, true) } fun visibility(sp: SP): Boolean = this.pref?.let { sp.getBoolean(it, true) }
?: true ?: true
}
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int) { fun drawable(resources: Resources, drawableDataMap: CwfResDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd ->
NONE("??", R.drawable.ic_invalid), when (sgvLevel) {
TRIPLE_UP("X", R.drawable.ic_doubleup), 1L -> { drawableDataMap[customHigh]?.toDrawable(resources) ?: drawableDataMap[cd]?.toDrawable(resources) }
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup), 0L -> { drawableDataMap[cd]?.toDrawable(resources) }
SINGLE_UP("\u2191", R.drawable.ic_singleup), -1L -> { drawableDataMap[customLow]?.toDrawable(resources) ?: drawableDataMap[cd]?.toDrawable(resources) }
FORTY_FIVE_UP("\u2197", R.drawable.ic_fortyfiveup), else -> drawableDataMap[cd]?.toDrawable(resources)
FLAT("\u2192", R.drawable.ic_flat), }
FORTY_FIVE_DOWN("\u2198", R.drawable.ic_fortyfivedown),
SINGLE_DOWN("\u2193", R.drawable.ic_singledown),
DOUBLE_DOWN("\u21ca", R.drawable.ic_doubledown),
TRIPLE_DOWN("X", R.drawable.ic_doubledown);
companion object {
fun icon(direction: String?) = values().firstOrNull { it.symbol == direction }?.icon ?: NONE.icon
} }
} }
private enum class GravityMap(val key: String, val gravity: Int) {
CENTER(JsonKeyValues.CENTER.key, Gravity.CENTER),
LEFT(JsonKeyValues.LEFT.key, Gravity.LEFT),
RIGHT(JsonKeyValues.RIGHT.key, Gravity.RIGHT);
companion object {
fun gravity(key: String?) = values().firstOrNull { it.key == key }?.gravity ?: CENTER.gravity
fun key(gravity: Int) = values().firstOrNull { it.gravity == gravity }?.key ?: CENTER.key
}
}
private enum class FontMap(val key: String, var font: Typeface, @FontRes val fontRessources: Int?) {
SANS_SERIF(JsonKeyValues.SANS_SERIF.key, Typeface.SANS_SERIF, null),
DEFAULT(JsonKeyValues.DEFAULT.key, Typeface.DEFAULT, null),
DEFAULT_BOLD(JsonKeyValues.DEFAULT_BOLD.key, Typeface.DEFAULT_BOLD, null),
MONOSPACE(JsonKeyValues.MONOSPACE.key, Typeface.MONOSPACE, null),
SERIF(JsonKeyValues.SERIF.key, Typeface.SERIF, null),
ROBOTO_CONDENSED_BOLD(JsonKeyValues.ROBOTO_CONDENSED_BOLD.key, Typeface.DEFAULT, R.font.roboto_condensed_bold),
ROBOTO_CONDENSED_LIGHT(JsonKeyValues.ROBOTO_CONDENSED_LIGHT.key, Typeface.DEFAULT, R.font.roboto_condensed_light),
ROBOTO_CONDENSED_REGULAR(JsonKeyValues.ROBOTO_CONDENSED_REGULAR.key, Typeface.DEFAULT, R.font.roboto_condensed_regular),
ROBOTO_SLAB_LIGHT(JsonKeyValues.ROBOTO_SLAB_LIGHT.key, Typeface.DEFAULT, R.font.roboto_slab_light);
companion object {
fun init(context: Context) = values().forEach { it.font = it.fontRessources?.let { font -> ResourcesCompat.getFont(context, font) } ?: it.font }
fun font(key: String) = values().firstOrNull { it.key == key }?.font ?: DEFAULT.font
fun key() = DEFAULT.key
}
}
private enum class StyleMap(val key: String, val style: Int) {
NORMAL(JsonKeyValues.NORMAL.key, Typeface.NORMAL),
BOLD(JsonKeyValues.BOLD.key, Typeface.BOLD),
BOLD_ITALIC(JsonKeyValues.BOLD_ITALIC.key, Typeface.BOLD_ITALIC),
ITALIC(JsonKeyValues.ITALIC.key, Typeface.ITALIC);
companion object {
fun style(key: String?) = values().firstOrNull { it.key == key }?.style ?: NORMAL.style
fun key(style: Int) = values().firstOrNull { it.style == style }?.key ?: NORMAL.key
}
}
// This class containt mapping between keys used within json of Custom Watchface and preferences
private enum class PrefMap(val key: String, @StringRes val prefKey: Int) {
SHOW_IOB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_IOB.key, R.string.key_show_iob),
SHOW_DETAILED_IOB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DETAILED_IOB.key, R.string.key_show_detailed_iob),
SHOW_COB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_COB.key, R.string.key_show_cob),
SHOW_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DELTA.key, R.string.key_show_delta),
SHOW_AVG_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_AVG_DELTA.key, R.string.key_show_avg_delta),
SHOW_DETAILED_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DETAILED_DELTA.key, R.string.key_show_detailed_delta),
SHOW_UPLOADER_BATTERY(CwfMetadataKey.CWF_PREF_WATCH_SHOW_UPLOADER_BATTERY.key, R.string.key_show_uploader_battery),
SHOW_RIG_BATTERY(CwfMetadataKey.CWF_PREF_WATCH_SHOW_RIG_BATTERY.key, R.string.key_show_rig_battery),
SHOW_TEMP_BASAL(CwfMetadataKey.CWF_PREF_WATCH_SHOW_TEMP_BASAL.key, R.string.key_show_temp_basal),
SHOW_DIRECTION(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DIRECTION.key, R.string.key_show_direction),
SHOW_AGO(CwfMetadataKey.CWF_PREF_WATCH_SHOW_AGO.key, R.string.key_show_ago),
SHOW_BG(CwfMetadataKey.CWF_PREF_WATCH_SHOW_BG.key, R.string.key_show_bg),
SHOW_BGI(CwfMetadataKey.CWF_PREF_WATCH_SHOW_BGI.key, R.string.key_show_bgi),
SHOW_LOOP_STATUS(CwfMetadataKey.CWF_PREF_WATCH_SHOW_LOOP_STATUS.key, R.string.key_show_external_status)
}
} }
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int,val customDrawable: ResFileMap?) {
NONE("??", R.drawable.ic_invalid, ResFileMap.ARROW_NONE),
TRIPLE_UP("X", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup, ResFileMap.ARROW_DOUBLE_UP),
SINGLE_UP("\u2191", R.drawable.ic_singleup, ResFileMap.ARROW_SINGLE_UP),
FORTY_FIVE_UP("\u2197", R.drawable.ic_fortyfiveup, ResFileMap.ARROW_FORTY_FIVE_UP),
FLAT("\u2192", R.drawable.ic_flat, ResFileMap.ARROW_FLAT),
FORTY_FIVE_DOWN("\u2198", R.drawable.ic_fortyfivedown, ResFileMap.ARROW_FORTY_FIVE_DOWN),
SINGLE_DOWN("\u2193", R.drawable.ic_singledown, ResFileMap.ARROW_SINGLE_DOWN),
DOUBLE_DOWN("\u21ca", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN),
TRIPLE_DOWN("X", R.drawable.ic_doubledown, ResFileMap.ARROW_DOUBLE_DOWN);
companion object {
fun drawable(direction: String?, resources: Resources, drawableDataMap: CwfResDataMap): Drawable {
val arrow = values().firstOrNull { it.symbol == direction } ?:NONE
return drawableDataMap[arrow.customDrawable]?.toDrawable(resources) ?:resources.getDrawable(arrow.icon)
}
}
}
private enum class GravityMap(val key: String, val gravity: Int) {
CENTER(JsonKeyValues.CENTER.key, Gravity.CENTER),
LEFT(JsonKeyValues.LEFT.key, Gravity.LEFT),
RIGHT(JsonKeyValues.RIGHT.key, Gravity.RIGHT);
companion object {
fun gravity(key: String?) = values().firstOrNull { it.key == key }?.gravity ?: CENTER.gravity
fun key(gravity: Int) = values().firstOrNull { it.gravity == gravity }?.key ?: CENTER.key
}
}
private enum class FontMap(val key: String, var font: Typeface, @FontRes val fontRessources: Int?, val customFont: ResFileMap?) {
SANS_SERIF(JsonKeyValues.SANS_SERIF.key, Typeface.SANS_SERIF, null, null),
DEFAULT(JsonKeyValues.DEFAULT.key, Typeface.DEFAULT, null, null),
DEFAULT_BOLD(JsonKeyValues.DEFAULT_BOLD.key, Typeface.DEFAULT_BOLD, null, null),
MONOSPACE(JsonKeyValues.MONOSPACE.key, Typeface.MONOSPACE, null, null),
SERIF(JsonKeyValues.SERIF.key, Typeface.SERIF, null, null),
ROBOTO_CONDENSED_BOLD(JsonKeyValues.ROBOTO_CONDENSED_BOLD.key, Typeface.DEFAULT, R.font.roboto_condensed_bold, null),
ROBOTO_CONDENSED_LIGHT(JsonKeyValues.ROBOTO_CONDENSED_LIGHT.key, Typeface.DEFAULT, R.font.roboto_condensed_light, null),
ROBOTO_CONDENSED_REGULAR(JsonKeyValues.ROBOTO_CONDENSED_REGULAR.key, Typeface.DEFAULT, R.font.roboto_condensed_regular, null),
ROBOTO_SLAB_LIGHT(JsonKeyValues.ROBOTO_SLAB_LIGHT.key, Typeface.DEFAULT, R.font.roboto_slab_light, null),
FONT1(JsonKeyValues.FONT1.key, Typeface.DEFAULT, null, ResFileMap.FONT1),
FONT2(JsonKeyValues.FONT2.key, Typeface.DEFAULT, null, ResFileMap.FONT2),
FONT3(JsonKeyValues.FONT3.key, Typeface.DEFAULT, null, ResFileMap.FONT3),
FONT4(JsonKeyValues.FONT4.key, Typeface.DEFAULT, null, ResFileMap.FONT4);
companion object {
fun init(context: Context, resDataMap: CwfResDataMap) = values().forEach { fontMap ->
fontMap.customFont?.let { customFont ->
fontMap.font = Typeface.DEFAULT
resDataMap[customFont]?.toTypeface()?.let { resData ->
fontMap.font = resData
}
} ?: run {
fontMap.font = fontMap.fontRessources?.let { fontResource ->
ResourcesCompat.getFont(context, fontResource)
} ?: fontMap.font
}
}
fun font(key: String) = values().firstOrNull { it.key == key }?.font ?: DEFAULT.font
fun key() = DEFAULT.key
}
}
private enum class StyleMap(val key: String, val style: Int) {
NORMAL(JsonKeyValues.NORMAL.key, Typeface.NORMAL),
BOLD(JsonKeyValues.BOLD.key, Typeface.BOLD),
BOLD_ITALIC(JsonKeyValues.BOLD_ITALIC.key, Typeface.BOLD_ITALIC),
ITALIC(JsonKeyValues.ITALIC.key, Typeface.ITALIC);
companion object {
fun style(key: String?) = values().firstOrNull { it.key == key }?.style ?: NORMAL.style
fun key(style: Int) = values().firstOrNull { it.style == style }?.key ?: NORMAL.key
}
}
// This class containt mapping between keys used within json of Custom Watchface and preferences
private enum class PrefMap(val key: String, @StringRes val prefKey: Int) {
SHOW_IOB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_IOB.key, R.string.key_show_iob),
SHOW_DETAILED_IOB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DETAILED_IOB.key, R.string.key_show_detailed_iob),
SHOW_COB(CwfMetadataKey.CWF_PREF_WATCH_SHOW_COB.key, R.string.key_show_cob),
SHOW_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DELTA.key, R.string.key_show_delta),
SHOW_AVG_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_AVG_DELTA.key, R.string.key_show_avg_delta),
SHOW_DETAILED_DELTA(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DETAILED_DELTA.key, R.string.key_show_detailed_delta),
SHOW_UPLOADER_BATTERY(CwfMetadataKey.CWF_PREF_WATCH_SHOW_UPLOADER_BATTERY.key, R.string.key_show_uploader_battery),
SHOW_RIG_BATTERY(CwfMetadataKey.CWF_PREF_WATCH_SHOW_RIG_BATTERY.key, R.string.key_show_rig_battery),
SHOW_TEMP_BASAL(CwfMetadataKey.CWF_PREF_WATCH_SHOW_TEMP_BASAL.key, R.string.key_show_temp_basal),
SHOW_DIRECTION(CwfMetadataKey.CWF_PREF_WATCH_SHOW_DIRECTION.key, R.string.key_show_direction),
SHOW_AGO(CwfMetadataKey.CWF_PREF_WATCH_SHOW_AGO.key, R.string.key_show_ago),
SHOW_BG(CwfMetadataKey.CWF_PREF_WATCH_SHOW_BG.key, R.string.key_show_bg),
SHOW_BGI(CwfMetadataKey.CWF_PREF_WATCH_SHOW_BGI.key, R.string.key_show_bgi),
SHOW_LOOP_STATUS(CwfMetadataKey.CWF_PREF_WATCH_SHOW_LOOP_STATUS.key, R.string.key_show_external_status)
}