android: update download progress notification (ProgressStyle / Live Update on API 36+)

This commit is contained in:
Denozordec committed 2026-10-10 00:29:30 +07:00
1 parent 564b8aaa8d
commit 87c3d73fbc
5 files changed
+75

No files matched your search

@@ -30,6 +30,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS"/>
<!-- required to install downloaded updates of the app itself -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<!-- lets the app install its own updates in the background without the
@@ -14,6 +14,7 @@
package org.strongswan.android.logic.updates
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
@@ -113,6 +114,7 @@ class UpdateManager private constructor(private val context: Context)
private const val READ_TIMEOUT_MS = 10_000
private const val UPDATES_CHANNEL = "org.strongswan.android.updates"
private const val UPDATES_NOTIFICATION_ID = 1001
private const val UPDATES_PROGRESS_NOTIFICATION_ID = 1002
private var mInstance: UpdateManager? = null
@@ -323,6 +325,73 @@ class UpdateManager private constructor(private val context: Context)
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && UpdateInstaller.canInstall(context)
}
private fun ensureUpdatesChannel(manager: NotificationManager)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
manager.createNotificationChannel(
NotificationChannel(UPDATES_CHANNEL, context.getString(R.string.updates_channel_name),
NotificationManager.IMPORTANCE_DEFAULT))
}
}
/**
* Shows the download progress. Android 16+ gets Notification.ProgressStyle, promoted
* to a Live Update (POST_PROMOTED_NOTIFICATIONS is a normal permission, granted at
* install, so there is no extra prompt); older versions get the classic progress bar.
*/
private fun postDownloadProgress(info: UpdateInfo, progress: Int)
{
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager ?: return
ensureUpdatesChannel(manager)
val title = context.getString(R.string.update_downloading_title, info.versionName)
val intent = Intent(context, MainActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
val pending = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA)
{
Notification.Builder(context, UPDATES_CHANNEL)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText("$progress%")
.setShortCriticalText("$progress%")
.setStyle(Notification.ProgressStyle()
.setProgressSegments(listOf(Notification.ProgressStyle.Segment(100)))
.setProgress(progress))
.setRequestPromotedOngoing(true)
.setOngoing(true)
.setOnlyAlertOnce(true)
.setContentIntent(pending)
.build()
}
else
{
NotificationCompat.Builder(context, UPDATES_CHANNEL)
.setSmallIcon(R.drawable.ic_notification)
.setContentTitle(title)
.setContentText("$progress%")
.setProgress(100, progress, false)
.setOngoing(true)
.setOnlyAlertOnce(true)
.setContentIntent(pending)
.build()
}
try
{
manager.notify(UPDATES_PROGRESS_NOTIFICATION_ID, notification)
}
catch (e: SecurityException)
{ /* POST_NOTIFICATIONS denied - progress stays visible in the settings */
Log.w(TAG, "Cannot post the update progress notification", e)
}
}
private fun cancelDownloadProgress()
{
(context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager)
?.cancel(UPDATES_PROGRESS_NOTIFICATION_ID)
}
/** Posts a notification that the downloaded update is ready to install */
private fun notifyUpdateReady(info: UpdateInfo)
{
@@ -507,6 +576,7 @@ class UpdateManager private constructor(private val context: Context)
{
lastProgress = progress
mState.value = UpdateState.Downloading(info, progress)
postDownloadProgress(info, progress)
}
}
}
@@ -527,6 +597,7 @@ class UpdateManager private constructor(private val context: Context)
finally
{
conn.disconnect()
cancelDownloadProgress()
}
}
@@ -505,4 +505,5 @@
<string name="widget_description">Schutzstatus und Verbinden mit einem Tipp</string>
<string name="profile_detail_placeholder">Profil auswählen oder hinzufügen</string>
<string name="update_downloading_title">Update %1$s wird heruntergeladen</string>
</resources>
@@ -502,4 +502,5 @@
<string name="widget_label">SHX VPN</string>
<string name="widget_description">Статус защиты и подключение одним касанием</string>
<string name="profile_detail_placeholder">Выберите или добавьте профиль</string>
<string name="update_downloading_title">Загрузка обновления %1$s</string>
</resources>
@@ -507,4 +507,5 @@
<string name="widget_label">SHX VPN</string>
<string name="widget_description">Protection status and one-tap connect</string>
<string name="profile_detail_placeholder">Select or add a profile</string>
<string name="update_downloading_title">Downloading update %1$s</string>
</resources>