From 6619fc76e4bd3398f63682dce10d6ef21b2ca962 Mon Sep 17 00:00:00 2001 From: mueller-ma Date: Sun, 30 Jun 2024 16:19:28 +0200 Subject: [PATCH] Drop support for openHAB 1 openHAB 2 has been 7 years ago, so it should be fine to drop support for openHAB 1 now. https://www.openhab.org/blog/2017-01-22-openhab2.html Signed-off-by: mueller-ma --- .../core/FcmMessageListenerService.kt | 4 +- .../habdroid/core/UpdateBroadcastReceiver.kt | 4 +- .../habdroid/model/CloudNotification.kt | 2 +- .../openhab/habdroid/model/IconResource.kt | 23 +-- .../java/org/openhab/habdroid/model/Item.kt | 43 ------ .../openhab/habdroid/model/LabeledValue.kt | 2 +- .../org/openhab/habdroid/model/LinkedPage.kt | 24 +-- .../habdroid/model/ServerProperties.kt | 49 +----- .../org/openhab/habdroid/model/Sitemap.kt | 35 +---- .../java/org/openhab/habdroid/model/Widget.kt | 105 +------------ .../habdroid/model/WidgetDataSource.kt | 21 +-- .../openhab/habdroid/ui/ItemPickerAdapter.kt | 4 +- .../activity/PageConnectionHolderFragment.kt | 47 +----- .../fragments/WidgetSettingsFragment.kt | 4 +- .../openhab/habdroid/util/ExtensionFuncs.kt | 4 - .../org/openhab/habdroid/util/ItemClient.kt | 75 ++------- .../habdroid/model/IconResourceTest.kt | 2 +- .../org/openhab/habdroid/model/ItemTest.kt | 2 +- .../org/openhab/habdroid/model/WidgetTest.kt | 63 -------- .../org/openhab/habdroid/util/UtilTest.kt | 146 ++---------------- 20 files changed, 52 insertions(+), 607 deletions(-) diff --git a/mobile/src/full/java/org/openhab/habdroid/core/FcmMessageListenerService.kt b/mobile/src/full/java/org/openhab/habdroid/core/FcmMessageListenerService.kt index 69a522da46a..820657305b6 100644 --- a/mobile/src/full/java/org/openhab/habdroid/core/FcmMessageListenerService.kt +++ b/mobile/src/full/java/org/openhab/habdroid/core/FcmMessageListenerService.kt @@ -18,7 +18,7 @@ import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import kotlinx.coroutines.runBlocking import org.openhab.habdroid.model.CloudNotification -import org.openhab.habdroid.model.toOH2IconResource +import org.openhab.habdroid.model.toIconResource class FcmMessageListenerService : FirebaseMessagingService() { private lateinit var notifHelper: NotificationHelper @@ -49,7 +49,7 @@ class FcmMessageListenerService : FirebaseMessagingService() { // timestamp, so use the (undocumented) google.sent_time as a time reference // in that case. If that also isn't present, don't show time at all. data["timestamp"]?.toLong() ?: message.sentTime, - data["icon"].toOH2IconResource(), + data["icon"].toIconResource(), data["severity"] ) diff --git a/mobile/src/main/java/org/openhab/habdroid/core/UpdateBroadcastReceiver.kt b/mobile/src/main/java/org/openhab/habdroid/core/UpdateBroadcastReceiver.kt index 1b2e5de82c0..b39c07d1f5c 100644 --- a/mobile/src/main/java/org/openhab/habdroid/core/UpdateBroadcastReceiver.kt +++ b/mobile/src/main/java/org/openhab/habdroid/core/UpdateBroadcastReceiver.kt @@ -31,7 +31,7 @@ import org.openhab.habdroid.model.DefaultSitemap import org.openhab.habdroid.model.ServerConfiguration import org.openhab.habdroid.model.ServerPath import org.openhab.habdroid.model.putIconResource -import org.openhab.habdroid.model.toOH2IconResource +import org.openhab.habdroid.model.toIconResource import org.openhab.habdroid.ui.homescreenwidget.ItemUpdateWidget import org.openhab.habdroid.ui.preference.PreferencesActivity import org.openhab.habdroid.util.PrefKeys @@ -107,7 +107,7 @@ class UpdateBroadcastReceiver : BroadcastReceiver() { val widgetPrefs = ItemUpdateWidget.getPrefsForWidget(context, id) val icon = widgetPrefs.getStringOrNull(PreferencesActivity.ITEM_UPDATE_WIDGET_ICON) widgetPrefs.edit { - putIconResource(PreferencesActivity.ITEM_UPDATE_WIDGET_ICON, icon.toOH2IconResource()) + putIconResource(PreferencesActivity.ITEM_UPDATE_WIDGET_ICON, icon.toIconResource()) } } diff --git a/mobile/src/main/java/org/openhab/habdroid/model/CloudNotification.kt b/mobile/src/main/java/org/openhab/habdroid/model/CloudNotification.kt index 6dc7d64e8ff..80147bc7eae 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/CloudNotification.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/CloudNotification.kt @@ -51,7 +51,7 @@ fun JSONObject.toCloudNotification(): CloudNotification { getString("_id"), getString("message"), created, - optStringOrNull("icon").toOH2IconResource(), + optStringOrNull("icon").toIconResource(), optStringOrNull("severity") ) } diff --git a/mobile/src/main/java/org/openhab/habdroid/model/IconResource.kt b/mobile/src/main/java/org/openhab/habdroid/model/IconResource.kt index ba4763bb196..ae4875751c0 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/IconResource.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/IconResource.kt @@ -32,7 +32,6 @@ import org.openhab.habdroid.util.getStringOrNull @Parcelize data class IconResource internal constructor( internal val icon: String, - internal val isOh2: Boolean, internal val customState: String ) : Parcelable { fun toUrl(context: Context, includeState: Boolean): String { @@ -42,10 +41,6 @@ data class IconResource internal constructor( @VisibleForTesting fun toUrl(includeState: Boolean, iconFormat: IconFormat, desiredSizePixels: Int): String { - if (!isOh2) { - return "images/$icon.png" - } - var iconSource = "oh" var iconSet = "classic" var iconName = "none" @@ -119,7 +114,7 @@ data class IconResource internal constructor( } fun withCustomState(state: String): IconResource { - return IconResource(icon, isOh2, state) + return IconResource(icon, state) } } @@ -128,9 +123,8 @@ fun SharedPreferences.getIconResource(key: String): IconResource? { return try { val obj = JSONObject(iconString) val icon = obj.getString("icon") - val isOh2 = obj.getInt("ohversion") == 2 val customState = obj.optString("state") - IconResource(icon, isOh2, customState) + IconResource(icon, customState) } catch (e: JSONException) { null } @@ -142,7 +136,6 @@ fun SharedPreferences.Editor.putIconResource(key: String, icon: IconResource?): } else { val iconString = JSONObject() .put("icon", icon.icon) - .put("ohversion", if (icon.isOh2) 2 else 1) .put("state", icon.customState) .toString() putString(key, iconString) @@ -153,15 +146,11 @@ fun SharedPreferences.Editor.putIconResource(key: String, icon: IconResource?): @VisibleForTesting fun String.isNoneIcon() = "(oh:([a-z]+:)?)?none".toRegex().matches(this) -fun String?.toOH1IconResource(): IconResource? { - return if (isNullOrEmpty() || isNoneIcon()) null else IconResource(this, false, "") -} - -fun String?.toOH2IconResource(): IconResource? { - return if (isNullOrEmpty() || isNoneIcon()) null else IconResource(this, true, "") +fun String?.toIconResource(): IconResource? { + return if (isNullOrEmpty() || isNoneIcon()) null else IconResource(this, "") } -internal fun String?.toOH2WidgetIconResource( +internal fun String?.toWidgetIconResource( item: Item?, type: Widget.Type, hasMappings: Boolean, @@ -202,7 +191,7 @@ internal fun String?.toOH2WidgetIconResource( else -> item.state.asString } - return IconResource(this, true, iconState.orEmpty()) + return IconResource(this, iconState.orEmpty()) } enum class IconFormat { diff --git a/mobile/src/main/java/org/openhab/habdroid/model/Item.kt b/mobile/src/main/java/org/openhab/habdroid/model/Item.kt index 9b9f7fa700b..5fb651ac3d2 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/Item.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/Item.kt @@ -19,12 +19,10 @@ import kotlinx.parcelize.Parcelize import org.json.JSONException import org.json.JSONObject import org.openhab.habdroid.R -import org.openhab.habdroid.util.forEach import org.openhab.habdroid.util.map import org.openhab.habdroid.util.mapString import org.openhab.habdroid.util.optFloatOrNull import org.openhab.habdroid.util.optStringOrNull -import org.w3c.dom.Node @Parcelize data class Item internal constructor( @@ -245,47 +243,6 @@ data class Item internal constructor( } } -fun Node.toItem(): Item? { - var name: String? = null - var state: String? = null - var link: String? = null - var type = Item.Type.None - var groupType = Item.Type.None - childNodes.forEach { node -> - when (node.nodeName) { - "type" -> type = node.textContent.toItemType() - "groupType" -> groupType = node.textContent.toItemType() - "name" -> name = node.textContent - "state" -> state = node.textContent - "link" -> link = node.textContent - } - } - - val finalName = name ?: return null - if (state == "Uninitialized" || state == "Undefined") { - state = null - } - - return Item( - name = finalName, - rawLabel = finalName, - category = null, - type = type, - groupType = groupType, - link = link, - readOnly = false, - members = emptyList(), - options = null, - state = state.toParsedState(), - tags = emptyList(), - groupNames = emptyList(), - minimum = null, - maximum = null, - step = null, - linkToMore = null - ) -} - @Throws(JSONException::class) fun JSONObject.toItem(): Item { val name = getString("name") 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 134e9d64a0b..49e4d616dc8 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/LabeledValue.kt @@ -32,6 +32,6 @@ data class LabeledValue internal constructor( fun JSONObject.toLabeledValue(valueKey: String, labelKey: String): LabeledValue { val value = getString(valueKey) val label = optString(labelKey, value) - val icon = optStringOrNull("icon")?.toOH2IconResource() + val icon = optStringOrNull("icon")?.toIconResource() return LabeledValue(value, label, icon, optInt("row"), optInt("column")) } diff --git a/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.kt b/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.kt index 98ddbe914e9..ee4a1a1f88a 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/LinkedPage.kt @@ -16,9 +16,7 @@ package org.openhab.habdroid.model import android.os.Parcelable import kotlinx.parcelize.Parcelize import org.json.JSONObject -import org.openhab.habdroid.util.forEach import org.openhab.habdroid.util.optStringOrNull -import org.w3c.dom.Node /** * This is a class to hold information about openHAB linked page. @@ -43,26 +41,6 @@ data class LinkedPage( } } -fun Node.toLinkedPage(): LinkedPage? { - var id: String? = null - var title: String? = null - var icon: String? = null - var link: String? = null - - childNodes.forEach { node -> - when (node.nodeName) { - "id" -> id = node.textContent - "title" -> title = node.textContent - "icon" -> icon = node.textContent - "link" -> link = node.textContent - } - } - - val finalId = id ?: return null - val finalLink = link ?: return null - return LinkedPage.build(finalId, title, icon.toOH1IconResource(), finalLink) -} - fun JSONObject?.toLinkedPage(): LinkedPage? { if (this == null) { return null @@ -71,7 +49,7 @@ fun JSONObject?.toLinkedPage(): LinkedPage? { return LinkedPage.build( getString("id"), optStringOrNull("title"), - icon.toOH2IconResource(), + icon.toIconResource(), getString("link") ) } diff --git a/mobile/src/main/java/org/openhab/habdroid/model/ServerProperties.kt b/mobile/src/main/java/org/openhab/habdroid/model/ServerProperties.kt index 74a8b34c345..750ef9173b8 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/ServerProperties.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/ServerProperties.kt @@ -15,10 +15,6 @@ package org.openhab.habdroid.model import android.os.Parcelable import android.util.Log -import java.io.IOException -import java.io.StringReader -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.parsers.ParserConfigurationException import kotlinx.parcelize.Parcelize import okhttp3.Request import org.json.JSONArray @@ -26,15 +22,9 @@ import org.json.JSONException import org.json.JSONObject import org.openhab.habdroid.core.connection.Connection import org.openhab.habdroid.util.HttpClient -import org.xml.sax.InputSource -import org.xml.sax.SAXException @Parcelize data class ServerProperties(val flags: Int, val sitemaps: List) : Parcelable { - fun hasJsonApi(): Boolean { - return flags and SERVER_FLAG_JSON_REST_API != 0 - } - fun hasSseSupport(): Boolean { return flags and SERVER_FLAG_SSE_SUPPORT != 0 } @@ -50,7 +40,6 @@ data class ServerProperties(val flags: Int, val sitemaps: List) : Parce companion object { private val TAG = ServerProperties::class.java.simpleName - const val SERVER_FLAG_JSON_REST_API = 1 shl 0 const val SERVER_FLAG_SSE_SUPPORT = 1 shl 1 const val SERVER_FLAG_ICON_FORMAT_SUPPORT = 1 shl 2 const val SERVER_FLAG_CHART_SCALING_SUPPORT = 1 shl 3 @@ -87,12 +76,7 @@ data class ServerProperties(val flags: Int, val sitemaps: List) : Parce val result = client.get("rest/").asText() try { val resultJson = JSONObject(result.response) - // If this succeeded, we're talking to OH2 - var flags = ( - SERVER_FLAG_JSON_REST_API - or SERVER_FLAG_ICON_FORMAT_SUPPORT - or SERVER_FLAG_CHART_SCALING_SUPPORT - ) + var flags = (SERVER_FLAG_ICON_FORMAT_SUPPORT or SERVER_FLAG_CHART_SCALING_SUPPORT) try { val version = resultJson.getString("version").toInt() Log.i(TAG, "Server has rest api version $version") @@ -130,12 +114,7 @@ data class ServerProperties(val flags: Int, val sitemaps: List) : Parce FlagsSuccess(flags) } catch (e: JSONException) { - if (result.response.startsWith(") : Parce private suspend fun fetchSitemaps(client: HttpClient, flags: Int): PropsResult = try { val result = client.get("rest/sitemaps").asText() - // OH1 returns XML, later versions return JSON - val sitemaps = if (flags and SERVER_FLAG_JSON_REST_API != 0) { - loadSitemapsFromJson(result.response) - } else { - loadSitemapsFromXml(result.response) - } - + val sitemaps = loadSitemapsFromJson(result.response) Log.d(TAG, "Server returned sitemaps: $sitemaps") PropsSuccess(ServerProperties(flags, sitemaps)) } catch (e: HttpClient.HttpException) { PropsFailure(e.request, e.statusCode, e) } - private fun loadSitemapsFromXml(response: String): List { - val dbf = DocumentBuilderFactory.newInstance() - try { - val builder = dbf.newDocumentBuilder() - val sitemapsXml = builder.parse(InputSource(StringReader(response))) - return sitemapsXml.toSitemapList() - } catch (e: ParserConfigurationException) { - Log.e(TAG, "Failed parsing sitemap XML", e) - } catch (e: SAXException) { - Log.e(TAG, "Failed parsing sitemap XML", e) - } catch (e: IOException) { - Log.e(TAG, "Failed parsing sitemap XML", e) - } - return emptyList() - } - private fun loadSitemapsFromJson(response: String): List { return try { val jsonArray = JSONArray(response) diff --git a/mobile/src/main/java/org/openhab/habdroid/model/Sitemap.kt b/mobile/src/main/java/org/openhab/habdroid/model/Sitemap.kt index 5ef69b7ead2..ab84922e91e 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/Sitemap.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/Sitemap.kt @@ -19,10 +19,7 @@ import kotlinx.parcelize.Parcelize import org.json.JSONArray import org.json.JSONException import org.json.JSONObject -import org.openhab.habdroid.util.forEach import org.openhab.habdroid.util.optStringOrNull -import org.w3c.dom.Document -import org.w3c.dom.Node @Parcelize data class Sitemap internal constructor( @@ -32,43 +29,13 @@ data class Sitemap internal constructor( val homepageLink: String ) : Parcelable -fun Node.toSitemap(): Sitemap? { - var label: String? = null - var name: String? = null - var icon: String? = null - var homepageLink: String? = null - - childNodes.forEach { node -> - when (node.nodeName) { - "name" -> name = node.textContent - "label" -> label = node.textContent - "icon" -> icon = node.textContent - "homepage" -> - node.childNodes.forEach { pageNode -> - if (pageNode.nodeName == "link") { - homepageLink = pageNode.textContent - } - } - } - } - - val finalName = name ?: return null - val finalLink = homepageLink ?: return null - return Sitemap(finalName, label ?: finalName, icon.toOH1IconResource(), finalLink) -} - fun JSONObject.toSitemap(): Sitemap? { val name = optStringOrNull("name") ?: return null val homepageLink = optJSONObject("homepage")?.optStringOrNull("link") ?: return null val label = optStringOrNull("label") val icon = optStringOrNull("icon") - return Sitemap(name, label ?: name, icon.toOH2IconResource(), homepageLink) -} - -fun Document.toSitemapList(): List { - val sitemapNodes = getElementsByTagName("sitemap") - return (0 until sitemapNodes.length).mapNotNull { index -> sitemapNodes.item(index).toSitemap() } + return Sitemap(name, label ?: name, icon.toIconResource(), homepageLink) } fun JSONArray.toSitemapList(): List { 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 f43558a8980..19f593e350a 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/Widget.kt @@ -31,7 +31,6 @@ import org.openhab.habdroid.util.optFloatOrNull import org.openhab.habdroid.util.optStringOrFallback import org.openhab.habdroid.util.optStringOrNull import org.openhab.habdroid.util.shouldRequestHighResChart -import org.w3c.dom.Node @Parcelize data class Widget( @@ -186,7 +185,7 @@ data class Widget( val iconName = eventPayload.optStringOrFallback("icon", source.icon?.icon) val staticIcon = source.icon?.customState?.isEmpty() == true val hasMappings = source.mappings.isNotEmpty() - val icon = iconName.toOH2WidgetIconResource(item, source.type, hasMappings, !staticIcon) + val icon = iconName.toWidgetIconResource(item, source.type, hasMappings, !staticIcon) return Widget( id = source.id, parentId = source.parentId, @@ -260,106 +259,6 @@ fun String?.toLabelSource(): Widget.LabelSource = when (this) { else -> Widget.LabelSource.Unknown } -fun Node.collectWidgets(parent: Widget?): List { - var item: Item? = null - var linkedPage: LinkedPage? = null - var id: String? = null - var label: String? = null - var icon: String? = null - var url: String? = null - var period = "" - var service = "" - var encoding: String? = null - var iconColor: String? = null - var labelColor: String? = null - var valueColor: String? = null - var switchSupport = false - var type = Widget.Type.Unknown - var minValue = 0f - var maxValue = 100f - var step = 1f - var refresh = 0 - var height = 0 - val mappings = ArrayList() - val childWidgetNodes = ArrayList() - - childNodes.forEach { node -> - when (node.nodeName) { - "item" -> item = node.toItem() - "linkedPage" -> linkedPage = node.toLinkedPage() - "widget" -> childWidgetNodes.add(node) - "type" -> type = node.textContent.toWidgetType() - "widgetId" -> id = node.textContent - "label" -> label = node.textContent - "icon" -> icon = node.textContent - "url" -> url = node.textContent - "minValue" -> minValue = node.textContent.toFloat() - "maxValue" -> maxValue = node.textContent.toFloat() - "step" -> step = node.textContent.toFloat() - "refresh" -> refresh = node.textContent.toInt() - "period" -> period = node.textContent - "service" -> service = node.textContent - "height" -> height = node.textContent.toInt() - "iconcolor" -> iconColor = node.textContent - "valuecolor" -> valueColor = node.textContent - "labelcolor" -> labelColor = node.textContent - "encoding" -> encoding = node.textContent - "switchSupport" -> switchSupport = node.textContent?.toBoolean() == true - "mapping" -> { - var mappingCommand = "" - var mappingLabel = "" - node.childNodes.forEach { childNode -> - when (childNode.nodeName) { - "command" -> mappingCommand = childNode.textContent - "label" -> mappingLabel = childNode.textContent - } - } - mappings.add(LabeledValue(mappingCommand, mappingLabel, null, 0, 0)) - } - else -> {} - } - } - - val finalId = id ?: return emptyList() - - val widget = Widget( - id = finalId, - parentId = parent?.id, - rawLabel = label.orEmpty(), - labelSource = Widget.LabelSource.Unknown, - icon = icon.toOH1IconResource(), - state = item?.state, - type = type, - url = url, - item = item, - linkedPage = linkedPage, - mappings = mappings, - encoding = encoding, - iconColor = iconColor, - labelColor = labelColor, - valueColor = valueColor, - refresh = Widget.sanitizeRefreshRate(refresh), - rawMinValue = minValue, - rawMaxValue = maxValue, - rawStep = step, - period = Widget.sanitizePeriod(period), - service = service, - legend = null, - // forceAsItem was added in openHAB 3, so no support for openHAB 1 required. - forceAsItem = false, - yAxisDecimalPattern = null, - switchSupport = switchSupport, - releaseOnly = null, - height = height, - // inputHint was added in openHAB 4, so no support for openHAB 1 required. - rawInputHint = null, - visibility = true - ) - val childWidgets = childWidgetNodes.map { node -> node.collectWidgets(widget) }.flatten() - - return listOf(widget) + childWidgets -} - @Throws(JSONException::class) fun JSONObject.collectWidgets(parent: Widget?): List { val mappings = if (has("mappings")) { @@ -378,7 +277,7 @@ fun JSONObject.collectWidgets(parent: Widget?): List { parentId = parent?.id, rawLabel = optString("label", ""), labelSource = optStringOrNull("labelSource").toLabelSource(), - icon = icon.toOH2WidgetIconResource(item, type, mappings.isNotEmpty(), !staticIcon), + icon = icon.toWidgetIconResource(item, type, mappings.isNotEmpty(), !staticIcon), state = Widget.determineWidgetState(optStringOrNull("state"), item), type = type, url = optStringOrNull("url"), diff --git a/mobile/src/main/java/org/openhab/habdroid/model/WidgetDataSource.kt b/mobile/src/main/java/org/openhab/habdroid/model/WidgetDataSource.kt index a85498ceb5b..ad339e0d1e0 100644 --- a/mobile/src/main/java/org/openhab/habdroid/model/WidgetDataSource.kt +++ b/mobile/src/main/java/org/openhab/habdroid/model/WidgetDataSource.kt @@ -18,14 +18,13 @@ import org.json.JSONException import org.json.JSONObject import org.openhab.habdroid.util.forEach import org.openhab.habdroid.util.optStringOrNull -import org.w3c.dom.Node /** * This class provides datasource for openHAB widgets from sitemap page. - * It uses a sitemap page XML document to create a list of widgets + * It uses a sitemap page JSON document to create a list of widgets */ -class WidgetDataSource() { +class WidgetDataSource { private val allWidgets = ArrayList() var title: String = "" private set @@ -45,22 +44,6 @@ class WidgetDataSource() { .filter { w -> w.parentId == null || w.parentId in firstLevelWidgetIds } } - fun setSourceNode(rootNode: Node?) { - if (rootNode == null) { - return - } - rootNode.childNodes.forEach { node -> - when (node.nodeName) { - "widget" -> allWidgets.addAll(node.collectWidgets(null)) - "title" -> title = node.textContent.orEmpty() - "id" -> id = node.textContent - "icon" -> icon = node.textContent - "link" -> link = node.textContent - else -> { } - } - } - } - fun setSourceJson(jsonObject: JSONObject) { if (!jsonObject.has("widgets")) { return diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/ItemPickerAdapter.kt b/mobile/src/main/java/org/openhab/habdroid/ui/ItemPickerAdapter.kt index e425bb662de..f2828218e30 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/ItemPickerAdapter.kt +++ b/mobile/src/main/java/org/openhab/habdroid/ui/ItemPickerAdapter.kt @@ -23,7 +23,7 @@ import java.util.Locale import org.openhab.habdroid.R import org.openhab.habdroid.core.connection.ConnectionFactory import org.openhab.habdroid.model.Item -import org.openhab.habdroid.model.toOH2IconResource +import org.openhab.habdroid.model.toIconResource import org.openhab.habdroid.ui.widget.WidgetImageView import org.openhab.habdroid.util.determineDataUsagePolicy @@ -117,7 +117,7 @@ class ItemPickerAdapter(context: Context, private val itemClickListener: ItemCli val context = itemView.context val connection = ConnectionFactory.primaryUsableConnection?.connection - val icon = item.category.toOH2IconResource() + val icon = item.category.toIconResource() if (icon != null && connection != null) { iconView.setImageUrl( connection, diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/activity/PageConnectionHolderFragment.kt b/mobile/src/main/java/org/openhab/habdroid/ui/activity/PageConnectionHolderFragment.kt index 4a2ba48ef51..2177de2717d 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/activity/PageConnectionHolderFragment.kt +++ b/mobile/src/main/java/org/openhab/habdroid/ui/activity/PageConnectionHolderFragment.kt @@ -17,11 +17,6 @@ import android.os.Bundle import android.util.Log import androidx.core.net.toUri import androidx.fragment.app.Fragment -import java.io.IOException -import java.io.StringReader -import java.util.HashMap -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.parsers.ParserConfigurationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job @@ -38,8 +33,6 @@ import org.openhab.habdroid.model.WidgetDataSource import org.openhab.habdroid.ui.WidgetListFragment import org.openhab.habdroid.util.HttpClient import org.openhab.habdroid.util.appendQueryParameter -import org.xml.sax.InputSource -import org.xml.sax.SAXException /** * Fragment that manages connections for active instances of @@ -266,9 +259,6 @@ class PageConnectionHolderFragment : Fragment(), CoroutineScope { Log.d(TAG, "Loading data for $url, long polling $longPolling") val headers = HashMap() - if (callback.serverProperties?.hasJsonApi() == false) { - headers["Accept"] = "application/xml" - } if (longPolling) { headers["X-Atmosphere-Transport"] = "long-polling" @@ -317,11 +307,7 @@ class PageConnectionHolderFragment : Fragment(), CoroutineScope { } val dataSource = WidgetDataSource() - val hasUpdate = if (callback.serverProperties?.hasJsonApi() == true) { - parseResponseJson(dataSource, response) - } else { - parseResponseXml(dataSource, response) - } + val hasUpdate = parseResponseJson(dataSource, response) if (hasUpdate) { // Remove frame widgets with no label text @@ -340,36 +326,7 @@ class PageConnectionHolderFragment : Fragment(), CoroutineScope { load() } - private fun parseResponseXml(dataSource: WidgetDataSource, response: String): Boolean { - val dbf = DocumentBuilderFactory.newInstance() - try { - val builder = dbf.newDocumentBuilder() - val document = builder.parse(InputSource(StringReader(response))) - if (document == null) { - Log.d(TAG, "Got empty XML document for $url") - longPolling = false - return false - } - val rootNode = document.firstChild - dataSource.setSourceNode(rootNode) - longPolling = true - return true - } catch (e: ParserConfigurationException) { - Log.d(TAG, "Parsing data for $url failed", e) - longPolling = false - return false - } catch (e: SAXException) { - Log.d(TAG, "Parsing data for $url failed", e) - longPolling = false - return false - } catch (e: IOException) { - Log.d(TAG, "Parsing data for $url failed", e) - longPolling = false - return false - } - } - - private fun parseResponseJson(dataSource: WidgetDataSource, response: String): Boolean { + private fun parseResponseJson(dataSource: WidgetDataSource, response: String): Boolean { try { val pageJson = JSONObject(response) // In case of a server timeout in the long polling request, nothing is done diff --git a/mobile/src/main/java/org/openhab/habdroid/ui/preference/fragments/WidgetSettingsFragment.kt b/mobile/src/main/java/org/openhab/habdroid/ui/preference/fragments/WidgetSettingsFragment.kt index 7bef3c92060..e83a1d65a8f 100644 --- a/mobile/src/main/java/org/openhab/habdroid/ui/preference/fragments/WidgetSettingsFragment.kt +++ b/mobile/src/main/java/org/openhab/habdroid/ui/preference/fragments/WidgetSettingsFragment.kt @@ -36,7 +36,7 @@ import androidx.preference.SwitchPreferenceCompat import com.google.android.material.snackbar.Snackbar import org.openhab.habdroid.R import org.openhab.habdroid.background.BackgroundTasksManager -import org.openhab.habdroid.model.toOH2IconResource +import org.openhab.habdroid.model.toIconResource import org.openhab.habdroid.ui.BasicItemPickerActivity import org.openhab.habdroid.ui.homescreenwidget.ItemUpdateWidget import org.openhab.habdroid.ui.preference.PreferencesActivity @@ -160,7 +160,7 @@ class WidgetSettingsFragment : label = itemAndStatePref.label.orEmpty(), widgetLabel = namePref.text.orEmpty(), mappedState = itemAndStatePref.mappedState.orEmpty(), - icon = itemAndStatePref.icon.toOH2IconResource(), + icon = itemAndStatePref.icon.toIconResource(), showState = showStatePref.isChecked ) } diff --git a/mobile/src/main/java/org/openhab/habdroid/util/ExtensionFuncs.kt b/mobile/src/main/java/org/openhab/habdroid/util/ExtensionFuncs.kt index 14675ef2fcd..d8fb2132986 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/ExtensionFuncs.kt +++ b/mobile/src/main/java/org/openhab/habdroid/util/ExtensionFuncs.kt @@ -84,8 +84,6 @@ import org.openhab.habdroid.model.ServerConfiguration import org.openhab.habdroid.model.ServerPath import org.openhab.habdroid.model.ServerProperties import org.openhab.habdroid.util.Util.TAG -import org.w3c.dom.Node -import org.w3c.dom.NodeList fun Throwable?.hasCause(cause: Class): Boolean { var error = this @@ -272,8 +270,6 @@ fun InputStream.svgToBitmap( } } -fun NodeList.forEach(action: (Node) -> Unit) = (0 until length).forEach { index -> action(item(index)) } - fun JSONArray.forEach(action: (JSONObject) -> Unit) = (0 until length()).forEach { index -> action(getJSONObject(index)) } diff --git a/mobile/src/main/java/org/openhab/habdroid/util/ItemClient.kt b/mobile/src/main/java/org/openhab/habdroid/util/ItemClient.kt index 28b9ccfbbac..3a0adf42357 100644 --- a/mobile/src/main/java/org/openhab/habdroid/util/ItemClient.kt +++ b/mobile/src/main/java/org/openhab/habdroid/util/ItemClient.kt @@ -14,10 +14,6 @@ package org.openhab.habdroid.util import android.util.Log -import java.io.IOException -import java.io.StringReader -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.parsers.ParserConfigurationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.isActive import org.json.JSONArray @@ -26,80 +22,29 @@ import org.json.JSONObject import org.openhab.habdroid.core.connection.Connection import org.openhab.habdroid.model.Item import org.openhab.habdroid.model.toItem -import org.xml.sax.InputSource -import org.xml.sax.SAXException object ItemClient { private val TAG = ItemClient::class.java.simpleName suspend fun loadItems(connection: Connection): List? { val response = connection.httpClient.get("rest/items") - val contentType = response.response.contentType() val content = response.asText().response - - if (contentType?.type == "application" && contentType.subtype == "json") { - // JSON - return try { - JSONArray(content).map { it.toItem() } - } catch (e: JSONException) { - Log.e(TAG, "Failed parsing JSON result for items", e) - null - } - } else { - // XML - return try { - val dbf = DocumentBuilderFactory.newInstance() - val builder = dbf.newDocumentBuilder() - val document = builder.parse(InputSource(StringReader(content))) - val nodes = document.childNodes - val items = ArrayList(nodes.length) - for (i in 0 until nodes.length) { - nodes.item(i).toItem()?.let { items.add(it) } - } - items - } catch (e: ParserConfigurationException) { - Log.e(TAG, "Failed parsing XML result for items", e) - null - } catch (e: SAXException) { - Log.e(TAG, "Failed parsing XML result for items", e) - null - } catch (e: IOException) { - Log.e(TAG, "Failed parsing XML result for items", e) - null - } + return try { + JSONArray(content).map { it.toItem() } + } catch (e: JSONException) { + Log.e(TAG, "Failed parsing JSON result for items", e) + null } } suspend fun loadItem(connection: Connection, itemName: String): Item? { val response = connection.httpClient.get("rest/items/$itemName") - val contentType = response.response.contentType() val content = response.asText().response - - if (contentType?.type == "application" && contentType.subtype == "json") { - // JSON - return try { - JSONObject(content).toItem() - } catch (e: JSONException) { - Log.e(TAG, "Failed parsing JSON result for item $itemName", e) - null - } - } else { - // XML - return try { - val dbf = DocumentBuilderFactory.newInstance() - val builder = dbf.newDocumentBuilder() - val document = builder.parse(InputSource(StringReader(content))) - document.toItem() - } catch (e: ParserConfigurationException) { - Log.e(TAG, "Failed parsing XML result for item $itemName", e) - null - } catch (e: SAXException) { - Log.e(TAG, "Failed parsing XML result for item $itemName", e) - null - } catch (e: IOException) { - Log.e(TAG, "Failed parsing XML result for item $itemName", e) - null - } + return try { + JSONObject(content).toItem() + } catch (e: JSONException) { + Log.e(TAG, "Failed parsing JSON result for item $itemName", e) + null } } diff --git a/mobile/src/test/java/org/openhab/habdroid/model/IconResourceTest.kt b/mobile/src/test/java/org/openhab/habdroid/model/IconResourceTest.kt index ca0f8d636a5..4e65e0115ee 100644 --- a/mobile/src/test/java/org/openhab/habdroid/model/IconResourceTest.kt +++ b/mobile/src/test/java/org/openhab/habdroid/model/IconResourceTest.kt @@ -88,7 +88,7 @@ class IconResourceTest { assertEquals( "$icon icon failed!", url, - IconResource(icon, true, "").toUrl(false, IconFormat.Png, 64) + IconResource(icon, "").toUrl(false, IconFormat.Png, 64) ) } } 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 133073be02b..577b9d2f9b5 100644 --- a/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt +++ b/mobile/src/test/java/org/openhab/habdroid/model/ItemTest.kt @@ -107,7 +107,7 @@ class ItemTest { @Test fun getCommandOptions() { val sut = itemWithCommandOptions.toItem() - assertEquals(LabeledValue("1", "One", "switch".toOH2IconResource(), 1, 2), sut.options!!.component1()) + assertEquals(LabeledValue("1", "One", "switch".toIconResource(), 1, 2), sut.options!!.component1()) assertEquals(LabeledValue("2", "Two", null, 0, 0), sut.options!!.component2()) } diff --git a/mobile/src/test/java/org/openhab/habdroid/model/WidgetTest.kt b/mobile/src/test/java/org/openhab/habdroid/model/WidgetTest.kt index f64452caedd..69ff3ea7390 100644 --- a/mobile/src/test/java/org/openhab/habdroid/model/WidgetTest.kt +++ b/mobile/src/test/java/org/openhab/habdroid/model/WidgetTest.kt @@ -13,20 +13,15 @@ package org.openhab.habdroid.model -import java.io.StringReader import java.security.InvalidParameterException -import javax.xml.parsers.DocumentBuilderFactory import org.json.JSONObject import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertNull import org.junit.Before import org.junit.Test -import org.w3c.dom.Node -import org.xml.sax.InputSource class WidgetTest { - private lateinit var sutXml: List private lateinit var sut1: List private lateinit var sut2: List private lateinit var sut3: List @@ -35,7 +30,6 @@ class WidgetTest { @Before @Throws(Exception::class) fun parse_createsWidget() { - sutXml = createXmlNode().collectWidgets(null) sut1 = createJsonObject(1).collectWidgets(null) sut2 = createJsonObject(2).collectWidgets(null) sut3 = createJsonObject(3).collectWidgets(null) @@ -43,18 +37,11 @@ class WidgetTest { @Test fun testCountInstances() { - assertEquals(sutXml.size, 2) assertEquals(sut1.size, 2) assertEquals(sut2.size, 1) assertEquals(sut3.size, 4) } - @Test - fun getIconPath_iconExists_returnIconUrlFromImages() { - assertEquals("images/groupicon.png", sutXml[0].icon?.toUrl(false, IconFormat.Png, 64)) - assertEquals("images/groupicon.png", sutXml[0].icon?.toUrl(true, IconFormat.Png, 64)) - } - @Test fun testGetIconPath() { assertEquals( @@ -237,56 +224,6 @@ class WidgetTest { assertEquals(null, sut2[0].encoding) } - @Throws(Exception::class) - private fun createXmlNode(): Node { - val xml = - """ - - demo - Group - - groupicon - http://localhost/url - 0.0 - 10.0 - 1 - 10 - D - D - 10 - white - white - white - - - ON - - - - GroupItem - group1 - Undefined - http://localhost/rest/items/group1 - - - 0001 - LinkedPage - linkedpageicon - http://localhost/rest/sitemaps/demo/0001 - false - - - demo11 - Switch - " - - """.trimIndent() - val dbf = DocumentBuilderFactory.newInstance() - val builder = dbf.newDocumentBuilder() - val document = builder.parse(InputSource(StringReader(xml))) - return document.firstChild - } - /** * @param id get different json objects depending on the id * 1: All values are set diff --git a/mobile/src/test/java/org/openhab/habdroid/util/UtilTest.kt b/mobile/src/test/java/org/openhab/habdroid/util/UtilTest.kt index c3e8edfb326..783d465a4a9 100644 --- a/mobile/src/test/java/org/openhab/habdroid/util/UtilTest.kt +++ b/mobile/src/test/java/org/openhab/habdroid/util/UtilTest.kt @@ -13,12 +13,8 @@ package org.openhab.habdroid.util -import java.io.IOException -import java.io.StringReader import java.security.cert.CertPathValidatorException import javax.net.ssl.SSLException -import javax.xml.parsers.DocumentBuilderFactory -import javax.xml.parsers.ParserConfigurationException import okhttp3.HttpUrl.Companion.toHttpUrl import org.json.JSONArray import org.json.JSONException @@ -29,97 +25,8 @@ import org.junit.Assert.assertTrue import org.junit.Test import org.openhab.habdroid.model.sortedWithDefaultName import org.openhab.habdroid.model.toSitemapList -import org.w3c.dom.Document -import org.xml.sax.InputSource -import org.xml.sax.SAXException class UtilTest { - private val sitemapOH1Document: Document - @Throws(ParserConfigurationException::class, IOException::class, SAXException::class) - get() { - val xml = - """ - - - - default - - http://myopenhab/rest/sitemaps/default - - http://myopenhab/rest/sitemaps/default/default - false - - - - heating - - http://myopenhab/rest/sitemaps/heating - - http://myopenhab/rest/sitemaps/heating/heating - false - - - - lighting - - http://myopenhab/rest/sitemaps/lighting - - http://myopenhab/rest/sitemaps/lighting/lighting - false - - - - heatpump - - http://myopenhab/rest/sitemaps/heatpump - - http://myopenhab/rest/sitemaps/heatpump/heatpump - false - - - - schedule - - http://myopenhab/rest/sitemaps/schedule - - http://myopenhab/rest/sitemaps/schedule/schedule - false - - - - outside - http://myopenhab/rest/sitemaps/outside - - http://myopenhab/rest/sitemaps/outside/outside - false - - - - garden - - http://myopenhab/rest/sitemaps/garden - - http://myopenhab/rest/sitemaps/garden/garden - false - - - - scenes - - http://myopenhab/rest/sitemaps/scenes - - http://myopenhab/rest/sitemaps/scenes/scenes - false - - - - """.trimIndent() - - val dbf = DocumentBuilderFactory.newInstance() - val builder = dbf.newDocumentBuilder() - return builder.parse(InputSource(StringReader(xml))) - } - @Test fun normalizeUrl() { assertEquals("http://localhost/", "http://localhost/".toNormalizedUrl()) @@ -155,23 +62,7 @@ class UtilTest { } @Test - fun parseOH1SitemapList() { - val sitemapList = sitemapOH1Document.toSitemapList() - assertFalse(sitemapList.isEmpty()) - - assertEquals("i AM DEfault", sitemapList[0].label) - assertEquals("Heating", sitemapList[1].label) - assertEquals("Lighting", sitemapList[2].label) - assertEquals("Heatpump", sitemapList[3].label) - assertEquals("Schedule", sitemapList[4].label) - assertEquals("outside", sitemapList[5].label) - assertEquals("Garden", sitemapList[6].label) - assertEquals("Scenes", sitemapList[7].label) - assertEquals(8, sitemapList.size) - } - - @Test - fun parseOH2SitemapListWithId1() { + fun parseSitemapListWithId1() { val sitemapList = createJsonArray(1).toSitemapList() assertFalse(sitemapList.isEmpty()) @@ -180,7 +71,7 @@ class UtilTest { } @Test - fun parseOH2SitemapListWithId2() { + fun parseSitemapListWithId2() { val sitemapList = createJsonArray(2).toSitemapList() assertFalse(sitemapList.isEmpty()) @@ -191,7 +82,7 @@ class UtilTest { } @Test - fun parseOH2SitemapListWithId3() { + fun parseSitemapListWithId3() { val sitemapList = createJsonArray(3).toSitemapList() assertFalse(sitemapList.isEmpty()) @@ -200,31 +91,20 @@ class UtilTest { } @Test - @Throws(IOException::class, SAXException::class, ParserConfigurationException::class) fun testSortSitemapList() { - val sitemapList = sitemapOH1Document.toSitemapList() + val sitemapList = createJsonArray(2).toSitemapList() val sorted1 = sitemapList.sortedWithDefaultName("") // Should be sorted - assertEquals("Garden", sorted1[0].label) - assertEquals("Heating", sorted1[1].label) - assertEquals("Heatpump", sorted1[2].label) - assertEquals("i AM DEfault", sorted1[3].label) - assertEquals("Lighting", sorted1[4].label) - assertEquals("outside", sorted1[5].label) - assertEquals("Scenes", sorted1[6].label) - assertEquals("Schedule", sorted1[7].label) - - val sorted2 = sitemapList.sortedWithDefaultName("schedule") - // Should be sorted, but "Schedule" should be the first one - assertEquals("Schedule", sorted2[0].label) - assertEquals("Garden", sorted2[1].label) - assertEquals("Heating", sorted2[2].label) - assertEquals("Heatpump", sorted2[3].label) - assertEquals("i AM DEfault", sorted2[4].label) - assertEquals("Lighting", sorted2[5].label) - assertEquals("outside", sorted2[6].label) - assertEquals("Scenes", sorted2[7].label) + assertEquals("HOME", sorted1[0].label) + assertEquals("Main Menu", sorted1[1].label) + assertEquals("test", sorted1[2].label) + + val sorted2 = sitemapList.sortedWithDefaultName("demo") + // Should be sorted, but "Main Menu" should be the first one + assertEquals("Main Menu", sorted2[0].label) + assertEquals("HOME", sorted2[1].label) + assertEquals("test", sorted2[2].label) } @Test