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',