fix(kiosk): fallback Intent.ACTION_VIEW when PackageInstaller STATUS=1 + fix self-update URL
- SetupActivity: catch STATUS_FAILURE=1 separately and immediately retry with Intent.ACTION_VIEW (system installer dialog) instead of showing a dead error. STATUS=1 is a generic PackageInstaller failure that can happen on many Android 14 devices even with a valid APK, but the system installer handles it. - SetupActivity: remove misleading 'incompatibile' hint for status=1 (was wrong; STATUS_FAILURE_INCOMPATIBLE = 7, not 1). - SetupActivity: deduplicate buildDeviceLabel() to shared private method - KioskActivity: fix KIOSK_DOWNLOAD_URL to point to kiosk-latest release (was pointing to 'latest' which only has the gateway APK, so self-update was silently broken). - Bump version 1.4.0 -> 1.5.0 (versionCode 5 -> 6)
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 = 5
|
versionCode = 6
|
||||||
versionName = "1.4.0"
|
versionName = "1.5.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ class KioskActivity : AppCompatActivity() {
|
|||||||
private const val KEY_SCREENSAVER = "screensaver_enabled"
|
private const val KEY_SCREENSAVER = "screensaver_enabled"
|
||||||
private const val GATEWAY_PACKAGE = "it.dadaloop.evershelf.scalegate"
|
private const val GATEWAY_PACKAGE = "it.dadaloop.evershelf.scalegate"
|
||||||
private const val GATEWAY_DOWNLOAD_URL = "https://github.com/dadaloop82/EverShelf/releases/latest/download/evershelf-scale-gateway.apk"
|
private const val GATEWAY_DOWNLOAD_URL = "https://github.com/dadaloop82/EverShelf/releases/latest/download/evershelf-scale-gateway.apk"
|
||||||
private const val KIOSK_DOWNLOAD_URL = "https://github.com/dadaloop82/EverShelf/releases/latest/download/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"
|
private const val GITHUB_RELEASES_API = "https://api.github.com/repos/dadaloop82/EverShelf/releases/latest"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ class SetupActivity : AppCompatActivity() {
|
|||||||
private const val INSTALL_CONFIRM_REQUEST = 2002
|
private const val INSTALL_CONFIRM_REQUEST = 2002
|
||||||
private const val UNINSTALL_REQUEST = 2003
|
private const val UNINSTALL_REQUEST = 2003
|
||||||
private const val PERMISSION_REQUEST_CODE = 2004
|
private const val PERMISSION_REQUEST_CODE = 2004
|
||||||
|
private const val INSTALL_FALLBACK_REQUEST = 2005
|
||||||
|
|
||||||
fun applyLocale(base: Context, lang: String): Context {
|
fun applyLocale(base: Context, lang: String): Context {
|
||||||
val locale = Locale(lang)
|
val locale = Locale(lang)
|
||||||
@@ -909,28 +910,37 @@ class SetupActivity : AppCompatActivity() {
|
|||||||
unregisterReceiver(this)
|
unregisterReceiver(this)
|
||||||
runOnUiThread { checkGatewayStatus() }
|
runOnUiThread { checkGatewayStatus() }
|
||||||
}
|
}
|
||||||
|
android.content.pm.PackageInstaller.STATUS_FAILURE -> {
|
||||||
|
// Generic failure (status=1): PackageInstaller can't install on this
|
||||||
|
// device/config. Fall back to system Intent.ACTION_VIEW installer UI.
|
||||||
|
unregisterReceiver(this)
|
||||||
|
ErrorReporter.reportMessage(
|
||||||
|
"install_failure",
|
||||||
|
"PackageInstaller STATUS_FAILURE=1, trying ACTION_VIEW fallback",
|
||||||
|
mapOf(
|
||||||
|
"pkg" to targetPkg,
|
||||||
|
"apk_kb" to (file.length() / 1024),
|
||||||
|
"android" to Build.VERSION.SDK_INT,
|
||||||
|
"device" to buildDeviceLabel()
|
||||||
|
),
|
||||||
|
forceReport = true
|
||||||
|
)
|
||||||
|
runOnUiThread { tryFallbackInstall(file, targetPkg) }
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
unregisterReceiver(this)
|
unregisterReceiver(this)
|
||||||
val msg = intent?.getStringExtra(
|
val msg = intent?.getStringExtra(
|
||||||
android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE
|
android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE
|
||||||
) ?: ""
|
) ?: ""
|
||||||
val deviceLabel = buildString {
|
val deviceLabel = buildDeviceLabel()
|
||||||
val mfr = Build.MANUFACTURER.takeIf { it.isNotBlank() && it != "unknown" }
|
|
||||||
?: Build.PRODUCT.takeIf { it.isNotBlank() && it != "unknown" }
|
|
||||||
?: Build.BOARD
|
|
||||||
val model = Build.MODEL.takeIf { it.isNotBlank() && it != "unknown" }
|
|
||||||
?: Build.HARDWARE
|
|
||||||
append("$mfr $model")
|
|
||||||
}
|
|
||||||
val hint = when (status) {
|
val hint = when (status) {
|
||||||
1 -> "APK incompatibile con questo dispositivo o versione Android"
|
|
||||||
2 -> "Bloccato da policy o da un'altra installazione in corso"
|
2 -> "Bloccato da policy o da un'altra installazione in corso"
|
||||||
3 -> "Annullato"
|
3 -> "Annullato"
|
||||||
4 -> "APK non valido o corrotto"
|
4 -> "APK non valido o corrotto"
|
||||||
5 -> "Conflitto: versione precedente con firma diversa"
|
5 -> "Conflitto: versione precedente con firma diversa"
|
||||||
6 -> "Spazio insufficiente"
|
6 -> "Spazio insufficiente"
|
||||||
7 -> "Incompatibile con questa versione di Android"
|
7 -> "Incompatibile con questa versione di Android"
|
||||||
else -> "Errore sconosciuto"
|
else -> "Errore sconosciuto (status=$status)"
|
||||||
}
|
}
|
||||||
val diagInfo = buildString {
|
val diagInfo = buildString {
|
||||||
appendLine("❌ Status $status: $hint")
|
appendLine("❌ Status $status: $hint")
|
||||||
@@ -1101,6 +1111,63 @@ class SetupActivity : AppCompatActivity() {
|
|||||||
Handler(Looper.getMainLooper()).postDelayed({ installWithPackageInstaller(f, pkg) }, 600)
|
Handler(Looper.getMainLooper()).postDelayed({ installWithPackageInstaller(f, pkg) }, 600)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
INSTALL_FALLBACK_REQUEST -> {
|
||||||
|
// System package installer returned — check if the package is now installed
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({
|
||||||
|
val installed = try { packageManager.getPackageInfo(pendingInstallPkg, 0); true } catch (_: Exception) { false }
|
||||||
|
if (installed) {
|
||||||
|
setGatewayUI("✅", getString(R.string.install_success),
|
||||||
|
getString(R.string.install_success_detail), 0xFF34d399.toInt(), btnEnabled = false)
|
||||||
|
Handler(Looper.getMainLooper()).postDelayed({ checkGatewayStatus() }, 1500)
|
||||||
|
} else {
|
||||||
|
checkGatewayStatus()
|
||||||
|
}
|
||||||
|
}, 800)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun tryFallbackInstall(file: java.io.File, targetPkg: String) {
|
||||||
|
try {
|
||||||
|
val uri = androidx.core.content.FileProvider.getUriForFile(
|
||||||
|
this, "$packageName.provider", file
|
||||||
|
)
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(uri, "application/vnd.android.package-archive")
|
||||||
|
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
}
|
||||||
|
pendingInstallFile = file
|
||||||
|
pendingInstallPkg = targetPkg
|
||||||
|
setGatewayUI("⏳", getString(R.string.install_installing),
|
||||||
|
"Conferma l'installazione nella finestra di sistema...",
|
||||||
|
0xFF94a3b8.toInt(), btnEnabled = false, progress = -1)
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
startActivityForResult(intent, INSTALL_FALLBACK_REQUEST)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val deviceLabel = buildDeviceLabel()
|
||||||
|
val diagInfo = buildString {
|
||||||
|
appendLine("❌ PackageInstaller status=1 e fallback non riuscito")
|
||||||
|
appendLine("Errore: ${e.message}")
|
||||||
|
appendLine("Android: ${Build.VERSION.SDK_INT} (${Build.VERSION.RELEASE})")
|
||||||
|
appendLine("Dispositivo: $deviceLabel")
|
||||||
|
}
|
||||||
|
setGatewayUI("❌", getString(R.string.install_error_install),
|
||||||
|
diagInfo.trim(), 0xFFf87171.toInt())
|
||||||
|
ErrorReporter.reportMessage(
|
||||||
|
"install_fallback_exception",
|
||||||
|
"tryFallbackInstall failed: ${e.message}",
|
||||||
|
mapOf("android" to Build.VERSION.SDK_INT, "device" to deviceLabel),
|
||||||
|
forceReport = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildDeviceLabel(): String {
|
||||||
|
val mfr = Build.MANUFACTURER.takeIf { it.isNotBlank() && it != "unknown" }
|
||||||
|
?: Build.PRODUCT.takeIf { it.isNotBlank() && it != "unknown" }
|
||||||
|
?: Build.BOARD
|
||||||
|
val model = Build.MODEL.takeIf { it.isNotBlank() && it != "unknown" }
|
||||||
|
?: Build.HARDWARE
|
||||||
|
return "$mfr $model"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user