diff --git a/app/src/main/kotlin/org/fdroid/MainActivity.kt b/app/src/main/kotlin/org/fdroid/MainActivity.kt
index 54f1550c4..5ec0ccba7 100644
--- a/app/src/main/kotlin/org/fdroid/MainActivity.kt
+++ b/app/src/main/kotlin/org/fdroid/MainActivity.kt
@@ -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()
+ }
}
diff --git a/app/src/main/kotlin/org/fdroid/NotificationManager.kt b/app/src/main/kotlin/org/fdroid/NotificationManager.kt
index 4ed97784f..cf12f74a8 100644
--- a/app/src/main/kotlin/org/fdroid/NotificationManager.kt
+++ b/app/src/main/kotlin/org/fdroid/NotificationManager.kt
@@ -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
diff --git a/app/src/main/kotlin/org/fdroid/install/AppUpdateReceiver.kt b/app/src/main/kotlin/org/fdroid/install/AppUpdateReceiver.kt
index b85cf3e9a..75bd1d37a 100644
--- a/app/src/main/kotlin/org/fdroid/install/AppUpdateReceiver.kt
+++ b/app/src/main/kotlin/org/fdroid/install/AppUpdateReceiver.kt
@@ -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()
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2ebfb085d..0570b9a1e 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -955,6 +955,8 @@ This often occurs with apps installed via Google Play or other sources, if they
Displays a notification after apps were installed automatically
Available app updates
Displays a notification after repositories were updated and app updates were found
+ Self-update
+ Notifies when the app updated itself and was closed, so you can launch it again easily
Connecting to %1$s…
Downloaded %1$s from %2$s
@@ -978,6 +980,8 @@ This often occurs with apps installed via Google Play or other sources, if they
Needs user confirmation:
Installed:
Tap to confirm
+
+ %s was updated. Tap to open
Category %1$s