Show notification after self-update

because we aren't allowed to launch the app. So the user can at least re-launch us easily.
This commit is contained in:
Torsten Grote
2026-05-06 10:18:46 -03:00
parent 911a6f3ea9
commit 4da7bafbee
4 changed files with 54 additions and 3 deletions

View File

@@ -31,6 +31,7 @@ class MainActivity : AppCompatActivity() {
val requestPermissionLauncher = registerForActivityResult(RequestPermission()) {}
@Inject lateinit var settingsManager: SettingsManager
@Inject lateinit var notificationManager: NotificationManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -78,4 +79,9 @@ class MainActivity : AppCompatActivity() {
// calling super seems to be needed, so the IntentListener gets informed
super.onNewIntent(intent, caller)
}
override fun onResume() {
super.onResume()
notificationManager.cancelSelfUpdateNotification()
}
}

View File

@@ -12,11 +12,14 @@ import androidx.annotation.StringRes
import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.BigTextStyle
import androidx.core.app.NotificationCompat.CATEGORY_REMINDER
import androidx.core.app.NotificationCompat.CATEGORY_SERVICE
import androidx.core.app.NotificationCompat.PRIORITY_HIGH
import androidx.core.app.NotificationCompat.PRIORITY_MAX
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_LOW
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_MAX
import androidx.core.content.ContextCompat.checkSelfPermission
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
@@ -40,10 +43,12 @@ constructor(@param:ApplicationContext private val context: Context) {
const val NOTIFICATION_ID_APP_INSTALLS: Int = 1
const val NOTIFICATION_ID_APP_INSTALL_SUCCESS: Int = 2
const val NOTIFICATION_ID_APP_UPDATES_AVAILABLE: Int = 3
const val NOTIFICATION_ID_SELF_UPDATE: Int = 4
private const val CHANNEL_UPDATES = "update-channel"
private const val CHANNEL_INSTALLS = "install-channel"
private const val CHANNEL_INSTALL_SUCCESS = "install-success-channel"
private const val CHANNEL_UPDATES_AVAILABLE = "updates-available-channel"
private const val CHANNEL_SELF_UPDATE = "self-update-channel"
}
init {
@@ -69,6 +74,10 @@ constructor(@param:ApplicationContext private val context: Context) {
.setName(s(R.string.notification_channel_updates_available_title))
.setDescription(s(R.string.notification_channel_updates_available_description))
.build(),
NotificationChannelCompat.Builder(CHANNEL_SELF_UPDATE, IMPORTANCE_MAX)
.setName(s(R.string.notification_channel_self_update_title))
.setDescription(s(R.string.notification_channel_self_update_description))
.build(),
)
nm.createNotificationChannelsCompat(channels)
}
@@ -181,6 +190,28 @@ constructor(@param:ApplicationContext private val context: Context) {
return builder
}
fun showSelfUpdateNotification() {
val pi = getMyAppsPendingIntent(context)
val app = context.getString(R.string.app_name)
val title = context.getString(R.string.notification_self_update_title, app)
val builder =
NotificationCompat.Builder(context, CHANNEL_SELF_UPDATE)
.setSmallIcon(R.drawable.ic_notification)
.setCategory(CATEGORY_REMINDER)
.setContentTitle(title)
.setPriority(PRIORITY_MAX)
.setContentIntent(pi)
.setAutoCancel(true)
val n = builder.build()
if (checkSelfPermission(context, POST_NOTIFICATIONS) == PERMISSION_GRANTED) {
nm.notify(NOTIFICATION_ID_SELF_UPDATE, n)
}
}
fun cancelSelfUpdateNotification() {
nm.cancel(NOTIFICATION_ID_SELF_UPDATE)
}
private fun getMainActivityPendingIntent(context: Context): PendingIntent {
val i = Intent(ACTION_MAIN).apply { setClass(context, MainActivity::class.java) }
val flags = FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE

View File

@@ -5,14 +5,19 @@ import android.content.Context
import android.content.Intent
import android.content.Intent.ACTION_MY_PACKAGE_REPLACED
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import androidx.annotation.RequiresApi
import android.os.Build.VERSION.SDK_INT
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import mu.KotlinLogging
import org.fdroid.NotificationManager
@AndroidEntryPoint
class AppUpdateReceiver : BroadcastReceiver() {
private val log = KotlinLogging.logger {}
@RequiresApi(35)
@Inject lateinit var notificationManager: NotificationManager
override fun onReceive(context: Context, intent: Intent) {
if (intent.action != ACTION_MY_PACKAGE_REPLACED) {
log.warn { "Unknown action: ${intent.action}" }
@@ -23,12 +28,17 @@ class AppUpdateReceiver : BroadcastReceiver() {
context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
addFlags(FLAG_ACTIVITY_NEW_TASK)
}
if (intent != null) {
if (intent == null) {
log.error { "Could not get launch intent for ourselves" }
} else {
try {
context.startActivity(intent)
} catch (e: Exception) {
log.error(e) { "Failed to start activity after update" }
}
}
// show notification on Android 10+, because we aren't allowed to start activity from background
// see: https://developer.android.com/guide/components/activities/background-starts
if (SDK_INT >= 29) notificationManager.showSelfUpdateNotification()
}
}

View File

@@ -955,6 +955,8 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="notification_channel_install_success_description">Displays a notification after apps were installed automatically</string>
<string name="notification_channel_updates_available_title">Available app updates</string>
<string name="notification_channel_updates_available_description">Displays a notification after repositories were updated and app updates were found</string>
<string name="notification_channel_self_update_title">Self-update</string>
<string name="notification_channel_self_update_description">Notifies when the app updated itself and was closed, so you can launch it again easily</string>
<string name="notification_repo_update_default">Connecting to %1$s…</string>
<string name="notification_repo_update_downloading">Downloaded %1$s from %2$s</string>
@@ -978,6 +980,8 @@ This often occurs with apps installed via Google Play or other sources, if they
<string name="notification_installing_section_confirmation">Needs user confirmation:</string>
<string name="notification_installing_section_installed">Installed:</string>
<string name="notification_installing_confirmation">Tap to confirm</string>
<!-- The placeholder %s will be replaced with the name of this app -->
<string name="notification_self_update_title">%s was updated. Tap to open</string>
<!-- Used by the TTS engine when showing a filter "chip" in the search box -->
<string name="tts_category_name">Category %1$s</string>