fix: APK install conflict (PackageInstaller) + dashboard stat skeleton
APK install conflict:
- Replace ACTION_VIEW-based install with PackageInstaller.Session API (API 21+)
- PackageInstaller gives us the actual install status via BroadcastReceiver:
STATUS_PENDING_USER_ACTION → launch system confirmation dialog automatically
STATUS_SUCCESS → success toast
STATUS_FAILURE_CONFLICT/INCOMPATIBLE → show AlertDialog offering to
uninstall the old version (ACTION_DELETE) so user can re-download and install
- FileProvider no longer needed for install (still kept for other uses)
- Kiosk: derive target package from filename (gateway vs kiosk self-update)
Dashboard 0-flash:
- Replace hardcoded 0 in HTML stat-value spans with ... placeholder
- Add .stat-loading CSS class: shimmer skeleton animation (gradient sweep)
- showPage(dashboard): set ... + stat-loading before API call
- loadDashboard: remove stat-loading class and set real count after data arrives
This commit is contained in:
+67
-11
@@ -8,6 +8,8 @@ import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.app.PendingIntent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
@@ -517,21 +519,75 @@ class MainActivity : AppCompatActivity(), BleScaleListener, ServerEventListener
|
||||
}
|
||||
|
||||
private fun installApk(file: java.io.File) {
|
||||
if (!file.exists() || file.length() == 0L) {
|
||||
runOnUiThread { Toast.makeText(this, "File APK non trovato", Toast.LENGTH_LONG).show() }
|
||||
return
|
||||
}
|
||||
try {
|
||||
val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
androidx.core.content.FileProvider.getUriForFile(
|
||||
this, "$packageName.provider", file
|
||||
val pi = packageManager.packageInstaller
|
||||
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||
params.setAppPackageName(packageName)
|
||||
val sessionId = pi.createSession(params)
|
||||
pi.openSession(sessionId).use { session ->
|
||||
file.inputStream().use { input ->
|
||||
session.openWrite("package", 0, file.length()).use { out ->
|
||||
input.copyTo(out)
|
||||
session.fsync(out)
|
||||
}
|
||||
}
|
||||
val action = "it.dadaloop.evershelf.scalegate.INSTALL_RESULT_$sessionId"
|
||||
val resultReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(ctx: Context?, intent: Intent?) {
|
||||
unregisterReceiver(this)
|
||||
val status = intent?.getIntExtra(
|
||||
PackageInstaller.EXTRA_STATUS,
|
||||
PackageInstaller.STATUS_FAILURE
|
||||
) ?: PackageInstaller.STATUS_FAILURE
|
||||
when (status) {
|
||||
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
|
||||
@Suppress("DEPRECATION")
|
||||
val confirmIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||
intent?.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java)
|
||||
else intent?.getParcelableExtra(Intent.EXTRA_INTENT)
|
||||
if (confirmIntent != null) startActivity(confirmIntent)
|
||||
}
|
||||
PackageInstaller.STATUS_SUCCESS ->
|
||||
runOnUiThread { Toast.makeText(this@MainActivity, "✅ Aggiornamento installato", Toast.LENGTH_SHORT).show() }
|
||||
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
|
||||
PackageInstaller.STATUS_FAILURE_CONFLICT -> {
|
||||
runOnUiThread {
|
||||
AlertDialog.Builder(this@MainActivity)
|
||||
.setTitle("⚠️ Conflitto firma APK")
|
||||
.setMessage("L'app installata usa una firma diversa.\n\nDevi disinstallare la versione precedente e poi ripremere Scarica.")
|
||||
.setPositiveButton("Disinstalla") { _, _ ->
|
||||
startActivity(Intent(Intent.ACTION_DELETE,
|
||||
android.net.Uri.parse("package:$packageName")))
|
||||
}
|
||||
.setNegativeButton("Annulla", null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val msg = intent?.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
||||
?: "status=$status"
|
||||
runOnUiThread { Toast.makeText(this@MainActivity, "Installazione: $msg", Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
|
||||
RECEIVER_NOT_EXPORTED else 0
|
||||
registerReceiver(resultReceiver, IntentFilter(action), flags)
|
||||
val pi2 = PendingIntent.getBroadcast(
|
||||
this, sessionId,
|
||||
Intent(action).setPackage(packageName),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
} else { Uri.fromFile(file) }
|
||||
val install = Intent(Intent.ACTION_VIEW).apply {
|
||||
setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
session.commit(pi2.intentSender)
|
||||
}
|
||||
startActivity(install)
|
||||
Toast.makeText(this, "Installazione in corso…", Toast.LENGTH_SHORT).show()
|
||||
} catch (e: Exception) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "Errore installazione: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
runOnUiThread { Toast.makeText(this, "Errore installazione: ${e.message}", Toast.LENGTH_LONG).show() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user