feat(kiosk): server reachability check in step 3 + uninstall-on-generic-failure

Server check (wizard step 3):
- New horizontal card above the scale question always shows server status
  as soon as step 3 is entered:  checking →  reachable / ⚠️ not reachable
- Pings GET $serverUrl/api/?action=check_update (5 s timeout)
- If reachable: 'Error reporting active — failures sent to GitHub Issues'
- If not reachable: 'Check the URL in step 2' warning
- checkServerReachability() called every time goToStep(3) runs
- Strings added in EN / IT / DE

Signature-conflict fallback (else branch in installWithPackageInstaller):
- When PackageInstaller returns a generic STATUS_FAILURE and the target
  package is already installed, a signature conflict is the most likely
  cause (CONFLICT/INCOMPATIBLE are caught separately earlier)
- New AlertDialog: 'Disinstalla e riprova' → startActivityForResult
  ACTION_DELETE → UNINSTALL_REQUEST → auto-retries install on return
- Only shown when all else has already failed
This commit is contained in:
dadaloop82
2026-05-03 19:35:20 +00:00
parent 15e1dfbd69
commit 38eb66cfbf
5 changed files with 157 additions and 1 deletions
@@ -88,6 +88,11 @@ class KioskActivity : AppCompatActivity() {
private lateinit var downloadProgressBar: ProgressBar
private lateinit var downloadProgressText: TextView
private lateinit var bannerProgressBar: ProgressBar
// Step 3: server-reachability check card
private lateinit var serverStatusCard: LinearLayout
private lateinit var serverCheckIcon: TextView
private lateinit var serverCheckText: TextView
private lateinit var serverCheckDetail: TextView
private var pendingApkDownloadUrl: String = ""
private var pendingInstallFile: java.io.File? = null
private var pendingInstallPkg: String = ""
@@ -189,6 +194,10 @@ class KioskActivity : AppCompatActivity() {
downloadProgressBar = findViewById(R.id.downloadProgressBar)
downloadProgressText = findViewById(R.id.downloadProgressText)
bannerProgressBar = findViewById(R.id.bannerProgressBar)
serverStatusCard = findViewById(R.id.serverStatusCard)
serverCheckIcon = findViewById(R.id.serverCheckIcon)
serverCheckText = findViewById(R.id.serverCheckText)
serverCheckDetail = findViewById(R.id.serverCheckDetail)
btnDismissUpdate.setOnClickListener {
updateBanner.visibility = View.GONE
bannerProgressBar.visibility = View.GONE
@@ -523,6 +532,58 @@ class KioskActivity : AppCompatActivity() {
scaleStatusDetail.setTextColor(0xFF34d399.toInt())
}
/**
* Pings the configured EverShelf server to verify it is reachable and that the
* error-reporting API endpoint responds. Called every time step 3 is entered so
* the user knows whether install failures will be automatically sent to GitHub Issues.
*/
private fun checkServerReachability() {
val url = prefs.getString(KEY_URL, "") ?: ""
serverCheckIcon.text = ""
serverCheckText.text = getString(R.string.wizard_server_checking)
serverCheckText.setTextColor(0xFF94a3b8.toInt())
serverCheckDetail.visibility = View.GONE
if (url.isEmpty()) {
serverCheckIcon.text = "⚠️"
serverCheckText.text = getString(R.string.wizard_server_error)
serverCheckText.setTextColor(0xFFfbbf24.toInt())
serverCheckDetail.text = getString(R.string.wizard_server_error_detail)
serverCheckDetail.visibility = View.VISIBLE
return
}
Thread {
var reachable = false
try {
val base = url.trimEnd('/')
val conn = java.net.URL("$base/api/?action=check_update")
.openConnection() as java.net.HttpURLConnection
conn.requestMethod = "GET"
conn.connectTimeout = 5000
conn.readTimeout = 5000
val code = conn.responseCode
conn.disconnect()
reachable = code in 200..499 // any HTTP response = server is up
} catch (_: Exception) {}
runOnUiThread {
if (reachable) {
serverCheckIcon.text = ""
serverCheckText.text = getString(R.string.wizard_server_ok)
serverCheckText.setTextColor(0xFF34d399.toInt())
serverCheckDetail.text = getString(R.string.wizard_server_ok_detail)
serverCheckDetail.visibility = View.VISIBLE
} else {
serverCheckIcon.text = "⚠️"
serverCheckText.text = getString(R.string.wizard_server_error)
serverCheckText.setTextColor(0xFFfbbf24.toInt())
serverCheckDetail.text = getString(R.string.wizard_server_error_detail)
serverCheckDetail.visibility = View.VISIBLE
}
}
}.start()
}
/**
* Central UI updater for the download/install progress.
* - Updates the wizard status card if it is currently visible (step 3).
@@ -1223,6 +1284,33 @@ class KioskActivity : AppCompatActivity() {
runOnUiThread { activeInstallBtn?.text = getString(R.string.install_btn_retry) }
ErrorReporter.reportMessage("install_failure",
"PackageInstaller status=$status msg=$msg pkg=$targetPkg")
// Generic failure on an already-installed package often means
// a signature conflict with the old version. Offer uninstall as
// last resort (only after the system installer already failed).
val pkgInstalled = try {
packageManager.getPackageInfo(targetPkg, 0); true
} catch (_: Exception) { false }
if (pkgInstalled) {
runOnUiThread {
pendingInstallFile = file
pendingInstallPkg = targetPkg
androidx.appcompat.app.AlertDialog.Builder(this@KioskActivity)
.setTitle("⚠️ Installazione fallita")
.setMessage("Installazione fallita (status=$status).\n\n" +
"Se la versione precedente usa una firma diversa " +
"bisogna prima disinstallarla.\n\n" +
"Disinstalla ora e riprova automaticamente?")
.setPositiveButton("Disinstalla e riprova") { _, _ ->
startActivityForResult(
Intent(Intent.ACTION_DELETE,
android.net.Uri.parse("package:$targetPkg")),
UNINSTALL_REQUEST
)
}
.setNegativeButton("Annulla", null)
.show()
}
}
}
}
}
@@ -316,7 +316,54 @@
android:textSize="15sp"
android:gravity="center"
android:lineSpacingExtra="4dp"
android:layout_marginBottom="24dp" />
android:layout_marginBottom="16dp" />
<!-- Server reachability check — shown as soon as step 3 is entered -->
<LinearLayout
android:id="@+id/serverStatusCard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/card_background"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="12dp"
android:paddingBottom="12dp"
android:layout_marginBottom="16dp"
android:gravity="center_vertical">
<TextView
android:id="@+id/serverCheckIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="⏳"
android:textSize="20sp"
android:layout_marginEnd="12dp" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/serverCheckText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/wizard_server_checking"
android:textColor="#94a3b8"
android:textSize="13sp" />
<TextView
android:id="@+id/serverCheckDetail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text=""
android:textColor="#64748b"
android:textSize="11sp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
<!-- Scale question card — shown first, hidden after answer -->
<LinearLayout
@@ -37,4 +37,11 @@
<string name="btn_launch_no_scale">🚀 Ohne Waage starten</string>
<string name="btn_download_gateway">📥 Scale Gateway installieren</string>
<string name="btn_update_gateway">📥 Scale Gateway aktualisieren</string>
<!-- Server-Erreichbarkeit prüfen (Wizard Schritt 3) -->
<string name="wizard_server_checking">Server-Verbindung wird geprüft…</string>
<string name="wizard_server_ok">Server erreichbar ✅</string>
<string name="wizard_server_ok_detail">Fehlerberichterstattung aktiv — Installationsfehler werden automatisch an GitHub Issues gesendet.</string>
<string name="wizard_server_error">Server nicht erreichbar ⚠️</string>
<string name="wizard_server_error_detail">Fehler werden GitHub Issues nicht erreichen. URL in Schritt 2 prüfen.</string>
</resources>
@@ -37,4 +37,11 @@
<string name="btn_launch_no_scale">🚀 Avvia senza bilancia</string>
<string name="btn_download_gateway">📥 Installa Scale Gateway</string>
<string name="btn_update_gateway">📥 Aggiorna Scale Gateway</string>
<!-- Verifica raggiungibilità server (step 3 wizard) -->
<string name="wizard_server_checking">Verifica connessione server…</string>
<string name="wizard_server_ok">Server raggiungibile ✅</string>
<string name="wizard_server_ok_detail">Segnalazione errori attiva — i problemi di installazione vengono inviati automaticamente alle GitHub Issues.</string>
<string name="wizard_server_error">Server non raggiungibile ⚠️</string>
<string name="wizard_server_error_detail">Gli errori non raggiungeranno GitHub Issues. Verifica l\'URL inserito al passaggio 2.</string>
</resources>
@@ -36,4 +36,11 @@
<string name="btn_launch_no_scale">🚀 Launch without scale</string>
<string name="btn_download_gateway">📥 Install Scale Gateway</string>
<string name="btn_update_gateway">📥 Update Scale Gateway</string>
<!-- Server reachability check (wizard step 3) -->
<string name="wizard_server_checking">Checking server connection…</string>
<string name="wizard_server_ok">Server reachable ✅</string>
<string name="wizard_server_ok_detail">Error reporting is active — install failures will be sent to GitHub Issues automatically.</string>
<string name="wizard_server_error">Server not reachable ⚠️</string>
<string name="wizard_server_error_detail">Install errors won\'t reach GitHub Issues. Check the URL entered in step 2.</string>
</resources>