Wear CWF Rename drawable to res (will extend map to all external resources)

This commit is contained in:
Philoul 2023-09-03 13:04:05 +02:00
parent 28dcc86313
commit 90cb0fd097
5 changed files with 69 additions and 69 deletions

View file

@ -21,7 +21,7 @@ import java.util.zip.ZipOutputStream
val CUSTOM_VERSION = "1.0"
enum class CwfDrawableFileMap(val fileName: String) {
enum class ResFileMap(val fileName: String) {
UNKNOWN("Unknown"),
CUSTOM_WATCHFACE("CustomWatchface"),
BACKGROUND("Background"),
@ -53,11 +53,11 @@ enum class CwfDrawableFileMap(val fileName: String) {
companion object {
fun fromFileName(file: String): CwfDrawableFileMap = values().firstOrNull { it.fileName == file.substringBeforeLast(".") } ?: UNKNOWN
fun fromFileName(file: String): ResFileMap = values().firstOrNull { it.fileName == file.substringBeforeLast(".") } ?: UNKNOWN
}
}
enum class DrawableFormat(val extension: String) {
enum class ResFormat(val extension: String) {
UNKNOWN(""),
SVG("svg"),
JPG("jpg"),
@ -65,24 +65,24 @@ enum class DrawableFormat(val extension: String) {
companion object {
fun fromFileName(fileName: String): DrawableFormat =
fun fromFileName(fileName: String): ResFormat =
values().firstOrNull { it.extension == fileName.substringAfterLast(".") } ?: UNKNOWN
}
}
@Serializable
data class DrawableData(val value: ByteArray, val format: DrawableFormat) {
data class ResData(val value: ByteArray, val format: ResFormat) {
fun toDrawable(resources: Resources): Drawable? {
try {
return when (format) {
DrawableFormat.PNG, DrawableFormat.JPG -> {
ResFormat.PNG, ResFormat.JPG -> {
val bitmap = BitmapFactory.decodeByteArray(value, 0, value.size)
BitmapDrawable(resources, bitmap)
}
DrawableFormat.SVG -> {
ResFormat.SVG -> {
val svg = SVG.getFromInputStream(ByteArrayInputStream(value))
val picture = svg.renderToPicture()
PictureDrawable(picture).apply {
@ -98,11 +98,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>
@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) {
@ -243,7 +243,7 @@ class ZipWatchfaceFormat {
fun loadCustomWatchface(cwfFile: File, authorization: Boolean): CwfData? {
var json = JSONObject()
var metadata: CwfMetadataMap = mutableMapOf()
val drawableDatas: CwfDrawableDataMap = mutableMapOf()
val resDatas: CwfResDataMap = mutableMapOf()
try {
val zipInputStream = ZipInputStream(cwfFile.inputStream())
@ -267,18 +267,18 @@ class ZipWatchfaceFormat {
metadata[CwfMetadataKey.CWF_FILENAME] = cwfFile.name
metadata[CwfMetadataKey.CWF_AUTHORIZATION] = authorization.toString()
} else {
val cwfDrawableFileMap = CwfDrawableFileMap.fromFileName(entryName)
val drawableFormat = DrawableFormat.fromFileName(entryName)
if (cwfDrawableFileMap != CwfDrawableFileMap.UNKNOWN && drawableFormat != DrawableFormat.UNKNOWN) {
drawableDatas[cwfDrawableFileMap] = DrawableData(byteArrayOutputStream.toByteArray(), drawableFormat)
val cwfResFileMap = ResFileMap.fromFileName(entryName)
val drawableFormat = ResFormat.fromFileName(entryName)
if (cwfResFileMap != ResFileMap.UNKNOWN && drawableFormat != ResFormat.UNKNOWN) {
resDatas[cwfResFileMap] = ResData(byteArrayOutputStream.toByteArray(), drawableFormat)
}
}
zipEntry = zipInputStream.nextEntry
}
// 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))
CwfData(json.toString(4), metadata, drawableDatas)
return if (metadata.containsKey(CwfMetadataKey.CWF_NAME) && resDatas.containsKey(ResFileMap.CUSTOM_WATCHFACE))
CwfData(json.toString(4), metadata, resDatas)
else
null
@ -300,10 +300,10 @@ class ZipWatchfaceFormat {
zipOutputStream.closeEntry()
// Ajouter les fichiers divers au ZIP
for (drawableData in customWatchface.drawableDatas) {
val fileEntry = ZipEntry("${drawableData.key.fileName}.${drawableData.value.format.extension}")
for (resData in customWatchface.resDatas) {
val fileEntry = ZipEntry("${resData.key.fileName}.${resData.value.format.extension}")
zipOutputStream.putNextEntry(fileEntry)
zipOutputStream.write(drawableData.value.value)
zipOutputStream.write(resData.value.value)
zipOutputStream.closeEntry()
}
zipOutputStream.close()

View file

@ -18,7 +18,7 @@ import info.nightscout.rx.events.EventMobileDataToWear
import info.nightscout.rx.logging.AAPSLogger
import info.nightscout.rx.weardata.CUSTOM_VERSION
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_VERSION
import info.nightscout.rx.weardata.CwfMetadataKey.CWF_CREATED_AT
@ -89,7 +89,7 @@ class CustomWatchfaceImportListActivity: TranslatedDaggerAppCompatActivity() {
override fun onBindViewHolder(holder: CwfFileViewHolder, position: Int) {
val customWatchfaceFile = customWatchfaceFileList[position]
val metadata = customWatchfaceFile.metadata
val drawable = customWatchfaceFile.drawableDatas[CwfDrawableFileMap
val drawable = customWatchfaceFile.resDatas[ResFileMap
.CUSTOM_WATCHFACE]?.toDrawable(resources)
with(holder.customWatchfaceImportListItemBinding) {
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.logging.AAPSLogger
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.EventData
import info.nightscout.shared.extensions.toVisibility
@ -111,7 +111,7 @@ class WearFragment : DaggerFragment() {
wearPlugin.savedCustomWatchface?.let {
wearPlugin.checkCustomWatchfacePreferences()
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
} ?:apply {
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.LTag
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.CwfMetadataMap
import info.nightscout.rx.weardata.JsonKeyValues
@ -86,7 +86,7 @@ class CwfInfosActivity : TranslatedDaggerAppCompatActivity() {
wearPlugin.savedCustomWatchface?.let {
val cwfAuthorization = sp.getBoolean(info.nightscout.core.utils.R.string.key_wear_custom_watchface_autorization, false)
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)
title = rh.gs(CwfMetadataKey.CWF_NAME.label, metadata[CwfMetadataKey.CWF_NAME])
metadata[CwfMetadataKey.CWF_AUTHOR_VERSION]?.let { authorVersion ->

View file

@ -35,12 +35,12 @@ import info.nightscout.androidaps.watchfaces.utils.BaseWatchFace
import info.nightscout.rx.logging.LTag
import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfData
import info.nightscout.rx.weardata.CwfDrawableFileMap
import info.nightscout.rx.weardata.CwfDrawableDataMap
import info.nightscout.rx.weardata.ResFileMap
import info.nightscout.rx.weardata.CwfResDataMap
import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.DrawableData
import info.nightscout.rx.weardata.DrawableFormat
import info.nightscout.rx.weardata.ResData
import info.nightscout.rx.weardata.ResFormat
import info.nightscout.rx.weardata.EventData
import info.nightscout.rx.weardata.JsonKeyValues
import info.nightscout.rx.weardata.JsonKeys.*
@ -63,7 +63,7 @@ class CustomWatchface : BaseWatchFace() {
private val TEMPLATE_RESOLUTION = 400
private var lowBatColor = Color.RED
private var bgColor = Color.WHITE
private var drawableDataMap: CwfDrawableDataMap = mutableMapOf()
private var resDataMap: CwfResDataMap = mutableMapOf()
override fun onCreate() {
super.onCreate()
@ -91,7 +91,7 @@ class CustomWatchface : BaseWatchFace() {
@SuppressLint("UseCompatLoadingForDrawables")
override fun setDataFields() {
super.setDataFields()
binding.direction2.setImageDrawable(TrendArrowMap.drawable(singleBg.slopeArrow, resources, drawableDataMap))
binding.direction2.setImageDrawable(TrendArrowMap.drawable(singleBg.slopeArrow, resources, resDataMap))
// rotate the second hand.
binding.secondHand.rotation = TimeOfDay().secondOfMinute * 6f
// rotate the minute hand.
@ -148,7 +148,7 @@ class CustomWatchface : BaseWatchFace() {
updatePref(it.customWatchfaceData.metadata)
try {
val json = JSONObject(it.customWatchfaceData.json)
drawableDataMap = it.customWatchfaceData.drawableDatas
resDataMap = it.customWatchfaceData.resDatas
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))
midColor = getColor(json.optString(MIDCOLOR.key), ContextCompat.getColor(this, R.color.inrange))
@ -197,7 +197,7 @@ class CustomWatchface : BaseWatchFace() {
is ImageView -> {
view.clearColorFilter()
id.drawable(resources, drawableDataMap, singleBg.sgvLevel)?.let {
id.drawable(resources, resDataMap, singleBg.sgvLevel)?.let {
if (viewJson.has(COLOR.key)) // Note only works on bitmap (png or jpg) or xml included into res, not for svg files
it.colorFilter = changeDrawableColor(getColor(viewJson.optString(COLOR.key)))
else
@ -296,9 +296,9 @@ class CustomWatchface : BaseWatchFace() {
}
}
val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
val drawableDataMap: CwfDrawableDataMap = mutableMapOf()
val drawableDataMap: CwfResDataMap = mutableMapOf()
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))
}
@ -389,9 +389,9 @@ class CustomWatchface : BaseWatchFace() {
@IdRes val id: Int,
@StringRes val pref: Int?,
@IdRes val defaultDrawable: Int?,
val customDrawable: CwfDrawableFileMap?,
val customHigh:CwfDrawableFileMap?,
val customLow: CwfDrawableFileMap?
val customDrawable: ResFileMap?,
val customHigh:ResFileMap?,
val customLow: ResFileMap?
) {
BACKGROUND(
@ -399,9 +399,9 @@ class CustomWatchface : BaseWatchFace() {
R.id.background,
null,
info.nightscout.shared.R.drawable.background,
CwfDrawableFileMap.BACKGROUND,
CwfDrawableFileMap.BACKGROUND_HIGH,
CwfDrawableFileMap.BACKGROUND_LOW
ResFileMap.BACKGROUND,
ResFileMap.BACKGROUND_HIGH,
ResFileMap.BACKGROUND_LOW
),
CHART(ViewKeys.CHART.key, R.id.chart, null, null, null, null, null),
COVER_CHART(
@ -409,9 +409,9 @@ class CustomWatchface : BaseWatchFace() {
R.id.cover_chart,
null,
null,
CwfDrawableFileMap.COVER_CHART,
CwfDrawableFileMap.COVER_CHART_HIGH,
CwfDrawableFileMap.COVER_CHART_LOW
ResFileMap.COVER_CHART,
ResFileMap.COVER_CHART_HIGH,
ResFileMap.COVER_CHART_LOW
),
FREETEXT1(ViewKeys.FREETEXT1.key, R.id.freetext1, null, null, null, null, null),
FREETEXT2(ViewKeys.FREETEXT2.key, R.id.freetext2, null, null, null, null, null),
@ -444,36 +444,36 @@ class CustomWatchface : BaseWatchFace() {
R.id.cover_plate,
null,
null,
CwfDrawableFileMap.COVER_PLATE,
CwfDrawableFileMap.COVER_PLATE_HIGH,
CwfDrawableFileMap.COVER_PLATE_LOW
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,
CwfDrawableFileMap.HOUR_HAND,
CwfDrawableFileMap.HOUR_HAND_HIGH,
CwfDrawableFileMap.HOUR_HAND_LOW
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,
CwfDrawableFileMap.MINUTE_HAND,
CwfDrawableFileMap.MINUTE_HAND_HIGH,
CwfDrawableFileMap.MINUTE_HAND_LOW
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,
CwfDrawableFileMap.SECOND_HAND,
CwfDrawableFileMap.SECOND_HAND_HIGH,
CwfDrawableFileMap.SECOND_HAND_LOW
ResFileMap.SECOND_HAND,
ResFileMap.SECOND_HAND_HIGH,
ResFileMap.SECOND_HAND_LOW
);
companion object {
@ -484,7 +484,7 @@ class CustomWatchface : BaseWatchFace() {
fun visibility(sp: SP): Boolean = this.pref?.let { sp.getBoolean(it, true) }
?: true
fun drawable(resources: Resources, drawableDataMap: CwfDrawableDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd ->
fun drawable(resources: Resources, drawableDataMap: CwfResDataMap, sgvLevel: Long): Drawable? = customDrawable?.let { cd ->
when (sgvLevel) {
1L -> { drawableDataMap[customHigh]?.toDrawable(resources) ?: drawableDataMap[cd]?.toDrawable(resources) }
0L -> { drawableDataMap[cd]?.toDrawable(resources) }
@ -495,21 +495,21 @@ class CustomWatchface : BaseWatchFace() {
}
}
private enum class TrendArrowMap(val symbol: String, @DrawableRes val icon: Int,val customDrawable: CwfDrawableFileMap?) {
NONE("??", R.drawable.ic_invalid, CwfDrawableFileMap.ARROW_NONE),
TRIPLE_UP("X", R.drawable.ic_doubleup, CwfDrawableFileMap.ARROW_DOUBLE_UP),
DOUBLE_UP("\u21c8", R.drawable.ic_doubleup, CwfDrawableFileMap.ARROW_DOUBLE_UP),
SINGLE_UP("\u2191", R.drawable.ic_singleup, CwfDrawableFileMap.ARROW_SINGLE_UP),
FORTY_FIVE_UP("\u2197", R.drawable.ic_fortyfiveup, CwfDrawableFileMap.ARROW_FORTY_FIVE_UP),
FLAT("\u2192", R.drawable.ic_flat, CwfDrawableFileMap.ARROW_FLAT),
FORTY_FIVE_DOWN("\u2198", R.drawable.ic_fortyfivedown, CwfDrawableFileMap.ARROW_FORTY_FIVE_DOWN),
SINGLE_DOWN("\u2193", R.drawable.ic_singledown, CwfDrawableFileMap.ARROW_SINGLE_DOWN),
DOUBLE_DOWN("\u21ca", R.drawable.ic_doubledown, CwfDrawableFileMap.ARROW_DOUBLE_DOWN),
TRIPLE_DOWN("X", R.drawable.ic_doubledown, CwfDrawableFileMap.ARROW_DOUBLE_DOWN);
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: CwfDrawableDataMap): Drawable {
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)
}