diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index f8467b458e..e805548aaa 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt b/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt
index 26daba272f..b648841ddd 100644
--- a/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt
+++ b/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt
@@ -18,12 +18,15 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import org.json.JSONException
import org.json.JSONObject
+import org.openhab.habdroid.util.optStringOrNull
@Parcelize
-data class LabeledValue internal constructor(val value: String, val label: String) : Parcelable
+data class LabeledValue internal constructor(val value: String, val label: String, val icon: IconResource?) : Parcelable
@Throws(JSONException::class)
-fun JSONObject.toLabeledValue(keyName: String, valueName: String): LabeledValue {
- val value = getString(keyName)
- return LabeledValue(value, optString(valueName, value))
+fun JSONObject.toLabeledValue(valueKey: String, labelKey: String): LabeledValue {
+ val value = getString(valueKey)
+ val label = optString(labelKey, value)
+ val icon = optStringOrNull("icon")?.toOH2IconResource()
+ return LabeledValue(value, label, icon)
}
diff --git a/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt b/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt
index f76525a756..66d493546f 100644
--- a/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt
+++ b/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt
@@ -286,7 +286,7 @@ fun Node.collectWidgets(parent: Widget?): List {
"label" -> mappingLabel = childNode.textContent
}
}
- mappings.add(LabeledValue(mappingCommand, mappingLabel))
+ mappings.add(LabeledValue(mappingCommand, mappingLabel, null))
}
else -> {}
}
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/ViewExtensions.kt b/mobile/src/main/java/org/openhab/habdroid/ui/ViewExtensions.kt
index 66a6491d8a..04eb5e276b 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/ViewExtensions.kt
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/ViewExtensions.kt
@@ -16,6 +16,7 @@ package org.openhab.habdroid.ui
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
+import android.util.Log
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.webkit.WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
@@ -26,11 +27,21 @@ import android.widget.ImageView
import android.widget.RemoteViews
import androidx.appcompat.widget.TooltipCompat
import androidx.core.graphics.drawable.DrawableCompat
+import androidx.core.graphics.drawable.toDrawable
import androidx.core.net.toUri
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+import com.google.android.material.button.MaterialButton
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import okhttp3.HttpUrl
import org.openhab.habdroid.R
import org.openhab.habdroid.core.connection.Connection
+import org.openhab.habdroid.model.LabeledValue
+import org.openhab.habdroid.util.HttpClient
+import org.openhab.habdroid.util.ImageConversionPolicy
import org.openhab.habdroid.util.openInBrowser
import org.openhab.habdroid.util.resolveThemedColor
@@ -111,3 +122,27 @@ fun RemoteViews.duplicate(): RemoteViews {
clone()
}
}
+
+fun MaterialButton.setTextAndIcon(connection: Connection, mapping: LabeledValue) {
+ val iconUrl = mapping.icon?.toUrl(context, true)
+ if (iconUrl == null) {
+ icon = null
+ text = mapping.label
+ return
+ }
+ val iconSize = context.resources.getDimensionPixelSize(R.dimen.section_switch_icon)
+ CoroutineScope(Dispatchers.IO + Job()).launch {
+ val drawable = try {
+ connection.httpClient.get(iconUrl, caching = HttpClient.CachingMode.DEFAULT)
+ .asBitmap(iconSize, 0, ImageConversionPolicy.ForceTargetSize).response
+ .toDrawable(resources)
+ } catch (e: HttpClient.HttpException) {
+ Log.d(WidgetAdapter.TAG, "Error getting icon for button", e)
+ null
+ }
+ withContext(Dispatchers.Main) {
+ icon = drawable
+ text = if (drawable == null) mapping.label else null
+ }
+ }
+}
diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.kt b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.kt
index f55cdef3ce..e597bdee08 100644
--- a/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.kt
+++ b/mobile/src/main/java/org/openhab/habdroid/ui/WidgetAdapter.kt
@@ -967,8 +967,8 @@ class WidgetAdapter(
// bind views
mappings.slice(0 until buttonCount).forEachIndexed { index, mapping ->
with(group[index] as MaterialButton) {
- text = mapping.label
tag = mapping.value
+ setTextAndIcon(connection, mapping)
}
}
@@ -1037,8 +1037,8 @@ class WidgetAdapter(
val applyMapping = { button: MaterialButton, mapping: LabeledValue? ->
button.isGone = mapping == null
if (mapping != null) {
- button.text = mapping.label
button.isChecked = widget.state?.asString == mapping.value
+ button.setTextAndIcon(connection, mapping)
button.tag = mapping.value
}
}
diff --git a/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button.xml b/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button.xml
index e6bad6fd86..98f7bf63e9 100644
--- a/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button.xml
+++ b/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button.xml
@@ -14,4 +14,6 @@
android:textAppearance="?attr/textAppearanceLabelMedium"
app:toggleCheckedStateOnClick="false"
tools:layout_width="wrap_content"
+ app:iconGravity="textStart"
+ app:iconPadding="0dp"
tools:text="Action 1" />
diff --git a/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button_compact.xml b/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button_compact.xml
index 1cf7fded08..5d502f8ac4 100644
--- a/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button_compact.xml
+++ b/mobile/src/main/res/layout/widgetlist_sectionswitchitem_button_compact.xml
@@ -12,4 +12,6 @@
android:paddingBottom="2dp"
android:textAppearance="?attr/textAppearanceLabelMedium"
app:toggleCheckedStateOnClick="false"
+ app:iconGravity="textStart"
+ app:iconPadding="0dp"
tools:text="Action 1" />
diff --git a/mobile/src/main/res/values/dimens.xml b/mobile/src/main/res/values/dimens.xml
index 4e1d55fe0e..0c290d3776 100644
--- a/mobile/src/main/res/values/dimens.xml
+++ b/mobile/src/main/res/values/dimens.xml
@@ -8,6 +8,7 @@
40dp
60dp
2dp
+ 16dp
4
diff --git a/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt b/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt
index 9c3ad0e228..01adf9c0e0 100644
--- a/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt
+++ b/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt
@@ -107,8 +107,8 @@ class ItemTest {
@Test
fun getCommandOptions() {
val sut = itemWithCommandOptions.toItem()
- assertEquals(LabeledValue("1", "One"), sut.options!!.component1())
- assertEquals(LabeledValue("2", "Two"), sut.options!!.component2())
+ assertEquals(LabeledValue("1", "One", "switch".toOH2IconResource()), sut.options!!.component1())
+ assertEquals(LabeledValue("2", "Two", null), sut.options!!.component2())
}
@Test
@@ -208,7 +208,8 @@ class ItemTest {
'commandOptions': [
{
'command': '1',
- 'label': 'One'
+ 'label': 'One',
+ 'icon': 'switch'
},
{
'command': '2',