Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9208416fa4
|
||
|
|
778ac58612
|
No files matched your search
@@ -15,6 +15,5 @@ jobs:
|
||||
tasks: |
|
||||
testDebugUnitTest :scanner-host:lintDebug :scanner-host:assembleDebug
|
||||
-PscannerTestBuildType=qa :scanner-host:assembleQa :scanner-host:assembleQaAndroidTest
|
||||
:scanner-host:lintRelease :scanner-host:assembleRelease -PscannerVersionName=0.0.0-ci -PscannerVersionCode=1
|
||||
emulator-tasks: connectedDebugAndroidTest
|
||||
publish-tasks: :qqq:android:core:publishAllPublicationsToGiteaRepository :qqq:android:mobileapp:publishAllPublicationsToGiteaRepository
|
||||
@@ -23,8 +23,6 @@ Build only after PRs 17 and 18 are merged to `main` and this branch has been reb
|
||||
./gradlew --no-daemon --console=plain :scanner-host:lintRelease :scanner-host:assembleRelease -PscannerVersionName=1.0.2 -PscannerVersionCode=4
|
||||
```
|
||||
|
||||
CI (`.gitea/workflows/build.yaml`) already runs `:scanner-host:lintRelease :scanner-host:assembleRelease` with throwaway version values on every push, so a release-variant lint or build break fails the pull request. It builds the unsigned release variant only, holds no signing material, and its APK is not a release candidate: the signed APK is still produced by the manual isolated `apksigner` step below.
|
||||
|
||||
Then sign `scanner-host/build/outputs/apk/release/scanner-host-release-unsigned.apk` in isolation with `apksigner` using the existing permanent key (certificate SHA-256 `BDDB2D2F...5B17A9FF`), verify v2+v3 with exactly one signer, 16 KiB alignment, `versionCode='4' versionName='1.0.2'` and non-debuggable, and write `QQQ-Mobile-1.0.2.apk`, `SHA256SUMS`, `signature-verification.txt`, `apk-metadata.txt` and `release-receipt.json` next to each other as for 1.0.1. Record the resulting hashes here, in [INSTALLATION](INSTALLATION.md) and in [SESSION-STATE](SESSION-STATE.md) before any tag or release.
|
||||
|
||||
## Historical status (November 4, 2026, superseded where noted)
|
||||
|
||||
+3
-2
@@ -27,8 +27,9 @@ import kotlinx.serialization.Serializable
|
||||
*******************************************************************************/
|
||||
@Serializable
|
||||
data class QHelpContent(
|
||||
val content: String,
|
||||
val format: String,
|
||||
val content: String? = null, // QQQ core now sends summary-only help entries
|
||||
val summary: String? = null,
|
||||
val format: String? = null,
|
||||
val roles: Set<String> = emptySet(),
|
||||
val contentAsText: String? = null
|
||||
) {
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@
|
||||
package com.kingsrook.qqq.frontend.android.core.model.metadata
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
/*******************************************************************************
|
||||
*******************************************************************************/
|
||||
@@ -30,7 +31,7 @@ import kotlinx.serialization.Serializable
|
||||
data class QWidgetMetaData(
|
||||
val name: String,
|
||||
val label: String? = null,
|
||||
val icon: QIcon? = null,
|
||||
val icon: JsonElement? = null, // QQQ core serializes a widget icon as a plain string (e.g. "troubleshoot"); tolerate an object too
|
||||
val type: String,
|
||||
) {
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2004-2024. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.frontend.android.core.model.metadata
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/*******************************************************************************
|
||||
** Current QQQ core serializes widget icons as plain strings and sends
|
||||
** summary-only help entries; the decoder must accept those shapes and the
|
||||
** older object/content shapes.
|
||||
*******************************************************************************/
|
||||
class MetadataDecodingTest {
|
||||
private val json = Json { ignoreUnknownKeys = true }
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
fun widgetIconAsStringDecodes() {
|
||||
val widget = json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table","icon":"troubleshoot"}""")
|
||||
assertEquals(JsonPrimitive("troubleshoot"), widget.icon)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
fun widgetIconAsObjectStillDecodes() {
|
||||
val widget = json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table","icon":{"name":"troubleshoot"}}""")
|
||||
assertTrue(widget.icon is JsonObject)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
fun widgetWithoutIconDecodes() {
|
||||
assertNull(json.decodeFromString<QWidgetMetaData>("""{"name":"w","type":"table"}""").icon)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
fun summaryOnlyHelpDecodes() {
|
||||
val help = json.decodeFromString<QHelpContent>("""{"summary":"short help","roles":["ALL_SCREENS"]}""")
|
||||
assertEquals("short help", help.summary)
|
||||
assertNull(help.content)
|
||||
assertNull(help.format)
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
fun contentHelpStillDecodes() {
|
||||
val help = json.decodeFromString<QHelpContent>("""{"content":"body","format":"TEXT"}""")
|
||||
assertEquals("body", help.content)
|
||||
assertEquals("TEXT", help.format)
|
||||
assertNull(help.summary)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -128,7 +128,7 @@ fun ProcessComponentEditForm(processViewModel: ProcessViewModel, component: QFro
|
||||
OutlinedButton(onClick = { processViewModel.loadRemoteOptions(field) }) { Text("Retry choices") }
|
||||
}
|
||||
semanticFieldHint(field)?.let { Text(it) }
|
||||
field.helpContents.orEmpty().forEach { Text(it.contentAsText ?: plainText(it.content)) }
|
||||
field.helpContents.orEmpty().forEach { Text(it.contentAsText ?: it.content?.let { c -> plainText(c) } ?: it.summary.orEmpty()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user