CustomWatchface Complexity reduction And Fix potential crash on phone side

This commit is contained in:
Philoul 2023-08-28 19:14:29 +02:00
parent b9975d4105
commit c1b1fe9120
3 changed files with 83 additions and 75 deletions

View file

@ -260,13 +260,13 @@ class ZipWatchfaceFormat {
}
// Valid CWF file must contains a valid json file with a name within metadata and a custom watchface image
if (metadata.containsKey(CwfMetadataKey.CWF_NAME) && drawableDatas.containsKey(CwfDrawableFileMap.CUSTOM_WATCHFACE))
return CwfData(json.toString(4), metadata, drawableDatas)
return if (metadata.containsKey(CwfMetadataKey.CWF_NAME) && drawableDatas.containsKey(CwfDrawableFileMap.CUSTOM_WATCHFACE))
CwfData(json.toString(4), metadata, drawableDatas)
else
return null
null
} catch (e: Exception) {
return null
return null // mainly IOException
}
}
@ -292,6 +292,7 @@ class ZipWatchfaceFormat {
zipOutputStream.close()
outputStream.close()
} catch (_: Exception) {
// Ignore file
}
}
@ -299,13 +300,9 @@ class ZipWatchfaceFormat {
fun loadMetadata(contents: JSONObject): CwfMetadataMap {
val metadata: CwfMetadataMap = mutableMapOf()
if (contents.has(JsonKeys.METADATA.key)) {
val meta = contents.getJSONObject(JsonKeys.METADATA.key)
for (key in meta.keys()) {
val metaKey = CwfMetadataKey.fromKey(key)
if (metaKey != null) {
metadata[metaKey] = meta.getString(key)
}
contents.optJSONObject(JsonKeys.METADATA.key)?.also { jsonObject -> // optJSONObject doesn't throw Exception
for (key in jsonObject.keys()) {
CwfMetadataKey.fromKey(key)?.let { metadata[it] = jsonObject.optString(key) } // optString doesn't throw Exception
}
}
return metadata

View file

@ -23,6 +23,8 @@ import info.nightscout.rx.weardata.CUSTOM_VERSION
import info.nightscout.rx.weardata.CwfDrawableFileMap
import info.nightscout.rx.weardata.CwfMetadataKey
import info.nightscout.rx.weardata.CwfMetadataMap
import info.nightscout.rx.weardata.JsonKeyValues
import info.nightscout.rx.weardata.JsonKeys
import info.nightscout.rx.weardata.ViewKeys
import info.nightscout.shared.interfaces.ResourceHelper
import info.nightscout.shared.sharedPreferences.SP
@ -82,13 +84,13 @@ class CwfInfosActivity : TranslatedDaggerAppCompatActivity() {
private fun updateGui() {
wearPlugin.savedCustomWatchface?.let {
val cwf_authorization = 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 drawable = it.drawableDatas[CwfDrawableFileMap.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 { author_version ->
title = "${metadata[CwfMetadataKey.CWF_NAME]} ($author_version)"
metadata[CwfMetadataKey.CWF_AUTHOR_VERSION]?.let { authorVersion ->
title = "${metadata[CwfMetadataKey.CWF_NAME]} ($authorVersion)"
}
binding.filelistName.text = rh.gs(CwfMetadataKey.CWF_FILENAME.label, metadata[CwfMetadataKey.CWF_FILENAME] ?: "")
binding.author.text = rh.gs(CwfMetadataKey.CWF_AUTHOR.label, metadata[CwfMetadataKey.CWF_AUTHOR] ?: "")
@ -99,7 +101,7 @@ class CwfInfosActivity : TranslatedDaggerAppCompatActivity() {
binding.cwfComment.text = rh.gs(CwfMetadataKey.CWF_COMMENT.label, metadata[CwfMetadataKey.CWF_COMMENT] ?: "")
if (metadata.count { it.key.isPref } > 0) {
binding.prefLayout.visibility = View.VISIBLE
binding.prefTitle.text = rh.gs(if (cwf_authorization) R.string.cwf_infos_pref_locked else R.string.cwf_infos_pref_required)
binding.prefTitle.text = rh.gs(if (cwfAuthorization) R.string.cwf_infos_pref_locked else R.string.cwf_infos_pref_required)
binding.prefRecyclerview.layoutManager = LinearLayoutManager(this)
binding.prefRecyclerview.adapter = PrefRecyclerViewAdapter(
metadata.filter { it.key.isPref && (it.value.lowercase() == "true" || it.value.lowercase() == "false") }.toList()
@ -192,7 +194,7 @@ class CwfInfosActivity : TranslatedDaggerAppCompatActivity() {
try {
val jsonValue = json.optJSONObject(viewKey.key)
if (jsonValue != null) {
val visibility = jsonValue.optString("visibility") == "visible"
val visibility = jsonValue.optString(JsonKeys.VISIBILITY.key) == JsonKeyValues.VISIBLE.key
if (visibility || allViews)
visibleKeyPairs.add(Pair(viewKey, visibility))
}

View file

@ -3,7 +3,6 @@
package info.nightscout.androidaps.watchfaces
import android.annotation.SuppressLint
import android.app.ActionBar.LayoutParams
import android.content.Context
import android.graphics.Color
import android.graphics.ColorFilter
@ -148,17 +147,16 @@ class CustomWatchface : BaseWatchFace() {
try {
val json = JSONObject(it.customWatchfaceData.json)
val drawableDataMap = it.customWatchfaceData.drawableDatas
enableSecond = (if (json.has(ENABLESECOND.key)) json.getBoolean(ENABLESECOND.key) else false) && sp.getBoolean(R.string.key_show_seconds, true)
highColor = if (json.has(HIGHCOLOR.key)) Color.parseColor(json.getString(HIGHCOLOR.key)) else ContextCompat.getColor(this, R.color.dark_highColor)
midColor = if (json.has(MIDCOLOR.key)) Color.parseColor(json.getString(MIDCOLOR.key)) else ContextCompat.getColor(this, R.color.inrange)
lowColor = if (json.has(LOWCOLOR.key)) Color.parseColor(json.getString(LOWCOLOR.key)) else ContextCompat.getColor(this, R.color.low)
lowBatColor = if (json.has(LOWBATCOLOR.key)) Color.parseColor(json.getString(LOWBATCOLOR.key)) else ContextCompat.getColor(this, R.color.dark_uploaderBatteryEmpty)
carbColor = if (json.has(CARBCOLOR.key)) Color.parseColor(json.getString(CARBCOLOR.key)) else ContextCompat.getColor(this, R.color.carbs)
basalBackgroundColor = if (json.has(BASALBACKGROUNDCOLOR.key)) Color.parseColor(json.getString(BASALBACKGROUNDCOLOR.key)) else ContextCompat.getColor(this, R.color.basal_dark)
basalCenterColor = if (json.has(BASALCENTERCOLOR.key)) Color.parseColor(json.getString(BASALCENTERCOLOR.key)) else ContextCompat.getColor(this, R.color.basal_light)
gridColor = if (json.has(GRIDCOLOR.key)) Color.parseColor(json.getString(GRIDCOLOR.key)) else Color.WHITE
pointSize = if (json.has(POINTSIZE.key)) json.getInt(POINTSIZE.key) else 2
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))
lowColor = getColor(json.optString(LOWCOLOR.key), ContextCompat.getColor(this, R.color.low))
lowBatColor = getColor(json.optString(LOWBATCOLOR.key), ContextCompat.getColor(this, R.color.dark_uploaderBatteryEmpty))
carbColor = getColor(json.optString(CARBCOLOR.key), ContextCompat.getColor(this, R.color.carbs))
basalBackgroundColor = getColor(json.optString(BASALBACKGROUNDCOLOR.key), ContextCompat.getColor(this, R.color.basal_dark))
basalCenterColor = getColor(json.optString(BASALCENTERCOLOR.key), ContextCompat.getColor(this, R.color.basal_light))
gridColor = getColor(json.optString(GRIDCOLOR.key), Color.WHITE)
pointSize = json.optInt(POINTSIZE.key, 2)
bgColor = when (singleBg.sgvLevel) {
1L -> highColor
0L -> midColor
@ -176,48 +174,49 @@ class CustomWatchface : BaseWatchFace() {
ViewMap.fromId(view.id)?.let { id ->
if (json.has(id.key)) {
val viewJson = json.getJSONObject(id.key)
val wrapContent = LayoutParams.WRAP_CONTENT
val width = if (viewJson.has(WIDTH.key)) (viewJson.getInt(WIDTH.key) * zoomFactor).toInt() else wrapContent
val height = if (viewJson.has(HEIGHT.key)) (viewJson.getInt(HEIGHT.key) * zoomFactor).toInt() else wrapContent
val width = (viewJson.optInt(WIDTH.key) * zoomFactor).toInt()
val height = (viewJson.optInt(HEIGHT.key, 0) * zoomFactor).toInt()
val params = FrameLayout.LayoutParams(width, height)
params.topMargin = if (viewJson.has(TOPMARGIN.key)) (viewJson.getInt(TOPMARGIN.key) * zoomFactor).toInt() else 0
params.leftMargin = if (viewJson.has(LEFTMARGIN.key)) (viewJson.getInt(LEFTMARGIN.key) * zoomFactor).toInt() else 0
params.topMargin = (viewJson.optInt(TOPMARGIN.key, 0) * zoomFactor).toInt()
params.leftMargin = (viewJson.optInt(LEFTMARGIN.key, 0) * zoomFactor).toInt()
view.layoutParams = params
view.visibility = if (viewJson.has(VISIBILITY.key)) setVisibility(viewJson.getString(VISIBILITY.key), id.visibility(sp)) else View.GONE
if (view is TextView) {
view.rotation = if (viewJson.has(ROTATION.key)) viewJson.getInt(ROTATION.key).toFloat() else 0F
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, ((if (viewJson.has(TEXTSIZE.key)) viewJson.getInt(TEXTSIZE.key) else 22) * zoomFactor).toFloat())
view.gravity = GravityMap.gravity(if (viewJson.has(GRAVITY.key)) viewJson.getString(GRAVITY.key) else GravityMap.CENTER.key)
view.setTypeface(
FontMap.font(if (viewJson.has(FONT.key)) viewJson.getString(FONT.key) else FontMap.DEFAULT.key),
StyleMap.style(if (viewJson.has(FONTSTYLE.key)) viewJson.getString(FONTSTYLE.key) else StyleMap.NORMAL.key)
)
if (viewJson.has(FONTCOLOR.key))
view.setTextColor(getColor(viewJson.getString(FONTCOLOR.key)))
view.visibility = setVisibility(viewJson.optString(VISIBILITY.key, JsonKeyValues.GONE.key), id.visibility(sp))
when (view) {
is TextView -> {
view.rotation = viewJson.optInt(ROTATION.key).toFloat()
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, (viewJson.optInt(TEXTSIZE.key, 22) * zoomFactor).toFloat())
view.gravity = GravityMap.gravity(viewJson.optString(GRAVITY.key, GravityMap.CENTER.key))
view.setTypeface(
FontMap.font(viewJson.optString(FONT.key, FontMap.DEFAULT.key)),
StyleMap.style(viewJson.optString(FONTSTYLE.key, StyleMap.NORMAL.key))
)
view.setTextColor(getColor(viewJson.optString(FONTCOLOR.key)))
if (viewJson.has(TEXTVALUE.key))
view.text = viewJson.getString(TEXTVALUE.key)
}
if (view is ImageView) {
view.clearColorFilter()
val drawable = if (id.key == CwfDrawableFileMap.BACKGROUND.key)
backGroundDrawable
else
drawableDataMap[CwfDrawableFileMap.fromKey(id.key)]?.toDrawable(resources)
drawable?.let {
if (viewJson.has(COLOR.key))
it.colorFilter = changeDrawableColor(getColor(viewJson.getString(COLOR.key)))
else
it.clearColorFilter()
view.setImageDrawable(it)
} ?: apply {
view.setImageDrawable(CwfDrawableFileMap.fromKey(id.key).icon?.let { context.getDrawable(it) })
if (viewJson.has(COLOR.key))
view.setColorFilter(getColor(viewJson.getString(COLOR.key)))
else
view.clearColorFilter()
if (viewJson.has(TEXTVALUE.key))
view.text = viewJson.getString(TEXTVALUE.key)
}
is ImageView -> {
view.clearColorFilter()
val drawable = if (id.key == CwfDrawableFileMap.BACKGROUND.key)
backGroundDrawable
else
drawableDataMap[CwfDrawableFileMap.fromKey(id.key)]?.toDrawable(resources)
drawable?.let {
if (viewJson.has(COLOR.key))
it.colorFilter = changeDrawableColor(getColor(viewJson.getString(COLOR.key)))
else
it.clearColorFilter()
view.setImageDrawable(it)
} ?: apply {
view.setImageDrawable(CwfDrawableFileMap.fromKey(id.key).icon?.let { context.getDrawable(it) })
if (viewJson.has(COLOR.key))
view.setColorFilter(getColor(viewJson.getString(COLOR.key)))
else
view.clearColorFilter()
}
}
}
} else {
view.visibility = View.GONE
@ -227,9 +226,7 @@ class CustomWatchface : BaseWatchFace() {
}
}
}
binding.background.visibility = View.VISIBLE
updateSecondVisibility()
setSecond() // Update second visibility for time view
manageSpecificViews()
} catch (e: Exception) {
aapsLogger.debug(LTag.WEAR, "Crash during Custom watch load")
persistence.store(defaultWatchface(), false) // relaod correct values to avoid crash of watchface
@ -238,8 +235,8 @@ class CustomWatchface : BaseWatchFace() {
}
private fun updatePref(metadata: CwfMetadataMap) {
val cwf_authorization = metadata[CwfMetadataKey.CWF_AUTHORIZATION]?.toBooleanStrictOrNull()
cwf_authorization?.let { authorization ->
val cwfAuthorization = metadata[CwfMetadataKey.CWF_AUTHORIZATION]?.toBooleanStrictOrNull()
cwfAuthorization?.let { authorization ->
if (authorization) {
PrefMap.values().forEach { pref ->
metadata[CwfMetadataKey.fromKey(pref.key)]?.toBooleanStrictOrNull()?.let { sp.putBoolean(pref.prefKey, it) }
@ -306,8 +303,7 @@ class CustomWatchface : BaseWatchFace() {
val metadataMap = ZipWatchfaceFormat.loadMetadata(json)
val drawableDataMap: CwfDrawableDataMap = mutableMapOf()
getResourceByteArray(info.nightscout.shared.R.drawable.watchface_custom)?.let {
val drawableData = DrawableData(it, DrawableFormat.PNG)
drawableDataMap[CwfDrawableFileMap.CUSTOM_WATCHFACE] = drawableData
drawableDataMap[CwfDrawableFileMap.CUSTOM_WATCHFACE] = DrawableData(it, DrawableFormat.PNG)
}
return EventData.ActionSetCustomWatchface(CwfData(json.toString(4), metadataMap, drawableDataMap))
}
@ -369,12 +365,25 @@ class CustomWatchface : BaseWatchFace() {
return ColorMatrixColorFilter(colorMatrix)
}
private fun getColor(color: String): Int =
private fun getColor(color: String, defaultColor: Int = Color.GRAY): Int =
if (color == JsonKeyValues.BGCOLOR.key)
bgColor
else
try { Color.parseColor(color) } catch (e: Exception) { Color.GRAY }
try { Color.parseColor(color) } catch (e: Exception) { defaultColor }
private fun manageSpecificViews() {
//Background should fill all the watchface and must be visible
val params = FrameLayout.LayoutParams((TEMPLATE_RESOLUTION * zoomFactor).toInt(), (TEMPLATE_RESOLUTION * zoomFactor).toInt())
params.topMargin = 0
params.leftMargin = 0
binding.background.layoutParams = params
binding.background.visibility = View.VISIBLE
// Update second visibility
updateSecondVisibility()
setSecond() // Update second visibility for time view
// Update timePeriod visibility
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),