Merge develop → main: kiosk v1.7.16 fix aggiornamenti STATUS=1
This commit is contained in:
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "it.dadaloop.evershelf.kiosk"
|
applicationId = "it.dadaloop.evershelf.kiosk"
|
||||||
minSdk = 24
|
minSdk = 24
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 16
|
versionCode = 17
|
||||||
versionName = "1.7.15"
|
versionName = "1.7.16"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|||||||
@@ -113,7 +113,9 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
private const val KEY_SCREENSAVER = "screensaver_enabled"
|
private const val KEY_SCREENSAVER = "screensaver_enabled"
|
||||||
private const val KIOSK_DOWNLOAD_URL = "https://github.com/dadaloop82/EverShelf/releases/download/kiosk-latest/evershelf-kiosk.apk"
|
private const val KIOSK_DOWNLOAD_URL = "https://github.com/dadaloop82/EverShelf/releases/download/kiosk-latest/evershelf-kiosk.apk"
|
||||||
private const val SPLASH_DURATION = 1500L
|
private const val SPLASH_DURATION = 1500L
|
||||||
private const val GITHUB_RELEASES_API = "https://api.github.com/repos/dadaloop82/EverShelf/releases/latest"
|
// Use the kiosk-specific rolling release tag so version comparison is always
|
||||||
|
// against the KIOSK version, not the webapp version (they diverge).
|
||||||
|
private const val GITHUB_RELEASES_API = "https://api.github.com/repos/dadaloop82/EverShelf/releases/tags/kiosk-latest"
|
||||||
// Keys for persisting a pending update across restarts
|
// Keys for persisting a pending update across restarts
|
||||||
private const val KEY_PENDING_UPDATE_VERSION = "pending_update_version"
|
private const val KEY_PENDING_UPDATE_VERSION = "pending_update_version"
|
||||||
private const val KEY_PENDING_UPDATE_URL = "pending_update_url"
|
private const val KEY_PENDING_UPDATE_URL = "pending_update_url"
|
||||||
@@ -627,10 +629,16 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
packageManager.getPackageInfo(packageName, 0).versionName ?: ""
|
packageManager.getPackageInfo(packageName, 0).versionName ?: ""
|
||||||
} catch (_: Exception) { "" }
|
} catch (_: Exception) { "" }
|
||||||
|
|
||||||
// Strip any non-numeric prefix so "kiosk-1.7.0", "v1.7.0", "kiosk-v1.7.1"
|
// The kiosk-latest release uses a non-semver tag ("kiosk-latest").
|
||||||
// all normalise to "1.7.0" / "1.7.1" for comparison.
|
// Extract the actual kiosk version from the release body text.
|
||||||
|
// Body format: "Alias automatico → kiosk-X.Y.Z" or just "kiosk-X.Y.Z".
|
||||||
|
// Fall back to stripping the tag prefix if body parsing fails.
|
||||||
|
val bodyText = json.optString("body", "")
|
||||||
val norm = { v: String -> v.replace(Regex("^[^0-9]*"), "") }
|
val norm = { v: String -> v.replace(Regex("^[^0-9]*"), "") }
|
||||||
val isSemver = norm(latestTag).matches(Regex("\\d+\\.\\d+.*"))
|
val remoteKioskVersion = Regex("""kiosk-v?(\d+\.\d+(?:\.\d+)?)""")
|
||||||
|
.find(bodyText)?.groupValues?.get(1)
|
||||||
|
?.takeIf { it.isNotEmpty() }
|
||||||
|
?: norm(latestTag)
|
||||||
|
|
||||||
// Compare semver: returns true if `remote` is strictly greater than `local`
|
// Compare semver: returns true if `remote` is strictly greater than `local`
|
||||||
fun semverNewer(remote: String, local: String): Boolean {
|
fun semverNewer(remote: String, local: String): Boolean {
|
||||||
@@ -645,29 +653,31 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isSemver = remoteKioskVersion.matches(Regex("\\d+\\.\\d+.*"))
|
||||||
|
|
||||||
|
// Get APK URL from assets; fall back to the hardcoded KIOSK_DOWNLOAD_URL
|
||||||
val assets = json.optJSONArray("assets")
|
val assets = json.optJSONArray("assets")
|
||||||
var kioskApkUrl = ""
|
var kioskApkUrl = ""
|
||||||
if (assets != null) {
|
if (assets != null) {
|
||||||
for (i in 0 until assets.length()) {
|
for (i in 0 until assets.length()) {
|
||||||
val a = assets.getJSONObject(i)
|
val a = assets.getJSONObject(i)
|
||||||
val name = a.optString("name", "").lowercase()
|
val url = a.optString("browser_download_url", "")
|
||||||
val url = a.optString("browser_download_url", "")
|
if (url.endsWith(".apk", ignoreCase = true) && url.isNotEmpty()) {
|
||||||
if (name.contains("kiosk") && url.isNotEmpty()) kioskApkUrl = url
|
kioskApkUrl = url; break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (kioskApkUrl.isEmpty()) kioskApkUrl = KIOSK_DOWNLOAD_URL
|
if (kioskApkUrl.isEmpty()) kioskApkUrl = KIOSK_DOWNLOAD_URL
|
||||||
|
|
||||||
// Only flag an update when the remote tag is parseable as semver AND
|
// Only flag an update when the remote version is parseable as semver AND
|
||||||
// the remote version is strictly greater than the installed version.
|
// strictly greater than the installed version.
|
||||||
// Non-semver tags (e.g. "kiosk-latest", "rolling") cannot be compared
|
val kioskNeedsUpdate = currentKiosk.isNotEmpty() && isSemver &&
|
||||||
// numerically → treat as "no update" to avoid false positives.
|
semverNewer(remoteKioskVersion, currentKiosk)
|
||||||
val kioskNeedsUpdate = currentKiosk.isNotEmpty() &&
|
|
||||||
isSemver && semverNewer(norm(latestTag), norm(currentKiosk))
|
|
||||||
|
|
||||||
val result = JSONObject()
|
val result = JSONObject()
|
||||||
.put("has_update", kioskNeedsUpdate)
|
.put("has_update", kioskNeedsUpdate)
|
||||||
.put("current", currentKiosk)
|
.put("current", currentKiosk)
|
||||||
.put("latest", latestTag)
|
.put("latest", remoteKioskVersion)
|
||||||
.put("apk_url", kioskApkUrl)
|
.put("apk_url", kioskApkUrl)
|
||||||
|
|
||||||
notifyJs(result)
|
notifyJs(result)
|
||||||
@@ -680,12 +690,11 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
// Persist the pending update so the banner reappears after a crash/restart
|
// Persist the pending update so the banner reappears after a crash/restart
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putString(KEY_PENDING_UPDATE_VERSION, latestTag)
|
.putString(KEY_PENDING_UPDATE_VERSION, remoteKioskVersion)
|
||||||
.putString(KEY_PENDING_UPDATE_URL, kioskApkUrl)
|
.putString(KEY_PENDING_UPDATE_URL, kioskApkUrl)
|
||||||
.apply()
|
.apply()
|
||||||
|
|
||||||
val label = if (isSemver) "$currentKiosk → $latestTag" else latestTag
|
runOnUiThread { showNativeUpdateBanner("🔄 Kiosk $currentKiosk → $remoteKioskVersion", kioskApkUrl) }
|
||||||
runOnUiThread { showNativeUpdateBanner("🔄 Kiosk $label", kioskApkUrl) }
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
notifyJs(JSONObject().put("has_update", false).put("error", e.message ?: "network error"))
|
notifyJs(JSONObject().put("has_update", false).put("error", e.message ?: "network error"))
|
||||||
}
|
}
|
||||||
@@ -802,6 +811,52 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
file.delete()
|
file.delete()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// ── Pre-install validation via PackageManager ──────────────────────
|
||||||
|
// This catches version-downgrade or same-version attempts before PackageInstaller
|
||||||
|
// gets them (which would silently fail with STATUS_FAILURE=1 on many OEMs).
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val apkInfo = try { packageManager.getPackageArchiveInfo(file.absolutePath, 0) } catch (_: Exception) { null }
|
||||||
|
if (apkInfo != null) {
|
||||||
|
// Wrong package: would always fail with STATUS_FAILURE=1
|
||||||
|
if (apkInfo.packageName != packageName) {
|
||||||
|
val detail = "APK package=${apkInfo.packageName}, expected=$packageName"
|
||||||
|
setInstallUI("\u274C", "APK non valido", detail, 0xFFf87171.toInt(), btnEnabled = true, progress = -2)
|
||||||
|
runOnUiThread { activeInstallBtn?.text = getString(R.string.install_btn_retry) }
|
||||||
|
ErrorReporter.reportMessage("install_wrong_package", detail, mapOf("apk_pkg" to apkInfo.packageName, "expected" to packageName), forceReport = true)
|
||||||
|
file.delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Version downgrade or same versionCode: Android rejects it
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val apkVc: Long = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||||
|
apkInfo.longVersionCode
|
||||||
|
else
|
||||||
|
apkInfo.versionCode.toLong()
|
||||||
|
val installedVc: Long = try {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||||
|
packageManager.getPackageInfo(packageName, 0).longVersionCode
|
||||||
|
else
|
||||||
|
packageManager.getPackageInfo(packageName, 0).versionCode.toLong()
|
||||||
|
} catch (_: Exception) { -1L }
|
||||||
|
|
||||||
|
if (installedVc >= 0 && apkVc <= installedVc) {
|
||||||
|
// Same or older version — no real update, dismiss banner silently
|
||||||
|
runOnUiThread {
|
||||||
|
updateBanner.visibility = View.GONE
|
||||||
|
bannerProgressBar.visibility = View.GONE
|
||||||
|
prefs.edit().remove(KEY_PENDING_UPDATE_VERSION).remove(KEY_PENDING_UPDATE_URL).apply()
|
||||||
|
}
|
||||||
|
ErrorReporter.reportMessage(
|
||||||
|
"install_no_upgrade",
|
||||||
|
"APK versionCode=$apkVc (${apkInfo.versionName}) ≤ installed=$installedVc — not an upgrade",
|
||||||
|
mapOf("apk_vc" to apkVc, "apk_ver" to (apkInfo.versionName ?: ""), "installed_vc" to installedVc),
|
||||||
|
forceReport = true
|
||||||
|
)
|
||||||
|
file.delete()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
// Only kiosk self-update is handled; gateway is now integrated
|
// Only kiosk self-update is handled; gateway is now integrated
|
||||||
val targetPkg = packageName
|
val targetPkg = packageName
|
||||||
installWithPackageInstaller(file, targetPkg)
|
installWithPackageInstaller(file, targetPkg)
|
||||||
|
|||||||
Reference in New Issue
Block a user