diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c8c34c34adf..442e405ca63 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -339,6 +339,7 @@ dependencies {
"gplayImplementation"("com.google.firebase:firebase-messaging:25.0.1")
implementation("org.unifiedpush.android:connector:3.3.2")
+ "genericImplementation"("org.unifiedpush.android:embedded-fcm-distributor:3.1.0-rc1")
// compose
implementation(platform("androidx.compose:compose-bom:2026.04.01"))
diff --git a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt
index d81d6b2ea5c..48295356013 100644
--- a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt
@@ -408,14 +408,19 @@ class AccountVerificationActivity : BaseActivity() {
// - By default, use the Play Services if available
// - If this is a first user, and we have an External UnifiedPush distributor,
// and the server supports it: we use it
+ // - Else if there is an embedded distributor (so this is a generic flavor, and the
+ // Play services are installed) => we use it for all accounts that support web push
// - Else we skip push registrations
if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
ClosedInterfaceImpl().setUpPushTokenRegistration()
eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, true))
} else if (userManager.users.blockingGet().size == 1 &&
- UnifiedPush.getDistributors(context).isNotEmpty() &&
+ UnifiedPushUtils.getExternalDistributors(context).isNotEmpty() &&
userManager.getUserWithId(internalAccountId).blockingGet().hasWebPushCapability) {
useUnifiedPushIntroduced()
+ } else if (UnifiedPushUtils.hasEmbeddedDistributor(context) &&
+ userManager.users.blockingGet().any { it.hasWebPushCapability }) {
+ useEmbeddedUnifiedPush()
} else {
Log.w(TAG, "Skipping push registration.")
eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, false))
@@ -433,6 +438,8 @@ class AccountVerificationActivity : BaseActivity() {
dialogForUnifiedPush { res ->
if (res) {
useUnifiedPush()
+ } else {
+ fallbackToEmbeddedUnifiedPush()
}
}
} else {
@@ -440,6 +447,24 @@ class AccountVerificationActivity : BaseActivity() {
}
}
+ /**
+ * Check if there is an embedded distributor, and use it if present,
+ * else, send EventStatus PUSH_REGISTRATION with success=false
+ */
+ private fun fallbackToEmbeddedUnifiedPush() {
+ if (UnifiedPushUtils.hasEmbeddedDistributor(context)) {
+ useEmbeddedUnifiedPush()
+ } else {
+ eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, false))
+ }
+ }
+
+ private fun useEmbeddedUnifiedPush() {
+ UnifiedPushUtils.useEmbeddedDistributor(context)
+ UnifiedPushUtils.registerWithCurrentDistributor(context)
+ eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, true))
+ }
+
private fun useUnifiedPush() {
UnifiedPushUtils.useDefaultDistributor(this) { distrib ->
distrib?.let {
@@ -448,7 +473,7 @@ class AccountVerificationActivity : BaseActivity() {
eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, true))
} ?: run {
Log.d(TAG, "No UnifiedPush distrib selected")
- eventBus.post(EventStatus(internalAccountId, EventStatus.EventType.PUSH_REGISTRATION, false))
+ fallbackToEmbeddedUnifiedPush()
}
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
index bfeba3c688c..6f67fb59406 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
@@ -84,6 +84,7 @@ import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.ParticipantPermissions
import com.nextcloud.talk.utils.ShareUtils
import com.nextcloud.talk.utils.SpreedFeatures
+import com.nextcloud.talk.utils.UnifiedPushUtils
import com.nextcloud.talk.utils.UserIdUtils
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.ADD_ADDITIONAL_ACCOUNT
@@ -306,7 +307,8 @@ class ConversationsListActivity : BaseActivity() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
!platformPermissionUtil.isPostNotificationsPermissionGranted() &&
(ClosedInterfaceImpl().isGooglePlayServicesAvailable ||
- appPreferences.useUnifiedPush)
+ appPreferences.useUnifiedPush ||
+ UnifiedPushUtils.hasEmbeddedDistributor(context))
) {
requestPermissions(
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
diff --git a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt
index e773903c20d..bec50b1a2a8 100644
--- a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt
@@ -50,6 +50,7 @@ import com.nextcloud.talk.utils.DisplayUtils
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.PushUtils.Companion.LATEST_PUSH_REGISTRATION_AT_PUSH_PROXY
import com.nextcloud.talk.utils.PushUtils.Companion.LATEST_PUSH_REGISTRATION_AT_SERVER
+import com.nextcloud.talk.utils.UnifiedPushUtils
import com.nextcloud.talk.utils.UserIdUtils
import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.power.PowerManagerUtils
@@ -79,6 +80,7 @@ class DiagnosisActivity : BaseActivity() {
lateinit var platformPermissionUtil: PlatformPermissionUtil
private var isGooglePlayServicesAvailable: Boolean = false
+ private var useEmbeddedDistrib: Boolean = false
private var nUnifiedPushServices = 0
private var offerUnifiedPush: Boolean = false
@@ -103,10 +105,11 @@ class DiagnosisActivity : BaseActivity() {
val colorScheme = viewThemeUtils.getColorScheme(this)
isGooglePlayServicesAvailable = ClosedInterfaceImpl().isGooglePlayServicesAvailable
- nUnifiedPushServices = UnifiedPush.getDistributors(this).size
+ nUnifiedPushServices = UnifiedPushUtils.getExternalDistributors(this).size
offerUnifiedPush = nUnifiedPushServices > 0 &&
userManager.users.blockingGet().all { it.hasWebPushCapability }
useUnifiedPush = appPreferences.useUnifiedPush
+ useEmbeddedDistrib = UnifiedPushUtils.hasEmbeddedDistributor(context) && !useUnifiedPush
unifiedPushService = UnifiedPush.getAckDistributor(this) ?: "N/A"
setContent {
@@ -160,7 +163,9 @@ class DiagnosisActivity : BaseActivity() {
viewState = viewState,
onTestPushClick = { diagnosisViewModel.fetchTestPushResult() },
onDismissDialog = { diagnosisViewModel.dismissDialog() },
- showTestPushButton = isGooglePlayServicesAvailable || useUnifiedPush,
+ showTestPushButton = isGooglePlayServicesAvailable ||
+ useUnifiedPush ||
+ useEmbeddedDistrib,
isOnline = isOnline
)
}
@@ -254,7 +259,7 @@ class DiagnosisActivity : BaseActivity() {
value = Build.VERSION.SDK_INT.toString()
)
- if (isGooglePlayServicesAvailable) {
+ if (isGooglePlayServicesAvailable || useEmbeddedDistrib) {
addDiagnosisEntry(
key = context.resources.getString(R.string.nc_diagnosis_gplay_available_title),
value = context.resources.getString(R.string.nc_diagnosis_gplay_available_yes)
@@ -301,7 +306,7 @@ class DiagnosisActivity : BaseActivity() {
value = getStringForBoolean(useUnifiedPush)
)
- if (useUnifiedPush) {
+ if (useUnifiedPush || useEmbeddedDistrib) {
setupAppValuesForPush()
setupAppValuesForUnifiedPush()
} else if (isGooglePlayServicesAvailable) {
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt
index 20a74cb947b..d8e1092503f 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt
@@ -105,6 +105,10 @@ class PushRegistrationWorker(
} else if (useUnifiedPush) {
Log.d(TAG, "PushRegistrationWorker called via $origin (unifiedPushWork)")
unifiedPushWork()
+ } else if (UnifiedPushUtils.hasEmbeddedDistributor(applicationContext)) {
+ Log.d(TAG, "PushRegistrationWorker called via $origin (unifiedPushWork#embeddedDistrib)")
+ UnifiedPushUtils.useEmbeddedDistributor(applicationContext)
+ unifiedPushWork()
} else {
Log.d(TAG, "PushRegistrationWorker called via $origin (proxyPushWork)")
proxyPushWork()
diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
index 82840f8927f..bd6ad68b4e4 100644
--- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
@@ -326,7 +326,7 @@ class SettingsActivity :
}
private fun showUnifiedPushToggle(): Boolean {
- return UnifiedPush.getDistributors(this).isNotEmpty() &&
+ return UnifiedPushUtils.getExternalDistributors(this).isNotEmpty() &&
userManager.users.blockingGet().all { it.hasWebPushCapability }
}
@@ -341,7 +341,7 @@ class SettingsActivity :
binding.settingsUnifiedpush.visibility = View.GONE
binding.settingsUnifiedpushService.visibility = View.GONE
} else {
- val nDistrib = UnifiedPush.getDistributors(context).size
+ val nDistrib = UnifiedPushUtils.getExternalDistributors(context).size
binding.settingsUnifiedpush.visibility = View.VISIBLE
binding.settingsUnifiedpushSwitch.isChecked = appPreferences.useUnifiedPush
binding.settingsUnifiedpush.setOnClickListener {
@@ -387,7 +387,9 @@ class SettingsActivity :
@SuppressLint("StringFormatInvalid")
@Suppress("LongMethod")
private fun setupNotificationPermissionSettings() {
- if (ClosedInterfaceImpl().isGooglePlayServicesAvailable || appPreferences.useUnifiedPush) {
+ if (ClosedInterfaceImpl().isGooglePlayServicesAvailable ||
+ appPreferences.useUnifiedPush ||
+ UnifiedPushUtils.hasEmbeddedDistributor(context)) {
binding.settingsPushOnlyWrapper.visibility = View.VISIBLE
binding.settingsGplayNotAvailable.visibility = View.GONE
binding.settingsPushNotAvailable.visibility = View.GONE
diff --git a/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt
index f84f457e634..d7ebdd031a5 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt
@@ -105,6 +105,26 @@ object UnifiedPushUtils {
enqueuePushWorker(context, false, "disableExternalUnifiedPush")
}
+ /**
+ * Check if we have a FCM embedded distributor, to get push notifications,
+ * using the Play services, using Web Push
+ *
+ * Available on the generic flavor only
+ */
+ @JvmStatic
+ fun hasEmbeddedDistributor(context: Context) =
+ context.packageName in UnifiedPush.getDistributors(context)
+
+ @JvmStatic
+ fun useEmbeddedDistributor(context: Context) =
+ UnifiedPush.saveDistributor(context, context.packageName)
+
+ @JvmStatic
+ fun getExternalDistributors(context: Context) =
+ UnifiedPush.getDistributors(context).filter {
+ it != context.packageName
+ }
+
private fun enqueuePushWorker(context: Context, useUnifiedPush: Boolean, origin: String) {
val data = Data.Builder()
.putString(PushRegistrationWorker.ORIGIN, "UnifiedPushUtils#$origin")
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index 1d823fff3e4..76b817104e7 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -275,7 +275,10 @@
-
+
+
+
+