From 4fa962c1102dbf1b1e27dcdb3605118e33e65491 Mon Sep 17 00:00:00 2001 From: sim Date: Sat, 17 Jan 2026 08:02:05 +0100 Subject: [PATCH 1/4] Add embedded distributor to use Play Services with the generic flavor Signed-off-by: sim --- app/build.gradle.kts | 1 + .../account/AccountVerificationActivity.kt | 31 +++++++++++++++++-- .../talk/diagnosis/DiagnosisActivity.kt | 3 +- .../talk/jobs/PushRegistrationWorker.kt | 4 +++ .../talk/settings/SettingsActivity.kt | 4 +-- .../nextcloud/talk/utils/UnifiedPushUtils.kt | 18 +++++++++++ gradle/verification-metadata.xml | 1 + 7 files changed, 56 insertions(+), 6 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index c29bd30a04..44737de6c4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -339,6 +339,7 @@ dependencies { "gplayImplementation"("com.google.firebase:firebase-messaging:25.1.0") implementation("org.unifiedpush.android:connector:3.3.3") + "genericImplementation"("org.unifiedpush.android:embedded-fcm-distributor:3.1.0") // compose implementation(platform("androidx.compose:compose-bom:2026.05.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 af94dd324e..aa99d2d028 100644 --- a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt @@ -60,7 +60,6 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode -import org.unifiedpush.android.connector.UnifiedPush import java.net.CookieManager import javax.inject.Inject @@ -408,15 +407,21 @@ 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)) @@ -434,6 +439,8 @@ class AccountVerificationActivity : BaseActivity() { dialogForUnifiedPush { res -> if (res) { useUnifiedPush() + } else { + fallbackToEmbeddedUnifiedPush() } } } else { @@ -441,6 +448,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 { @@ -449,7 +474,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/diagnosis/DiagnosisActivity.kt b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt index 629580f65b..7ac4ac1276 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 @@ -103,7 +104,7 @@ 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 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 e5d5c19f31..2d3107193d 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.kt @@ -102,6 +102,10 @@ class PushRegistrationWorker(context: Context, workerParams: WorkerParameters) : } 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 64fe261961..3f512f3821 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -328,7 +328,7 @@ class SettingsActivity : } private fun showUnifiedPushToggle(): Boolean = - UnifiedPush.getDistributors(this).isNotEmpty() && + UnifiedPushUtils.getExternalDistributors(this).isNotEmpty() && userManager.users.blockingGet().all { it.hasWebPushCapability } private fun setupUnifiedPushSettings() { @@ -342,7 +342,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 { 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 8f387507cc..c77d687c68 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/UnifiedPushUtils.kt @@ -97,6 +97,24 @@ 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 0213ea6381..139e3d80c3 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -278,6 +278,7 @@ + From b291c3429c778ace612769e990e3dc39db5e4f29 Mon Sep 17 00:00:00 2001 From: sim Date: Sat, 17 Jan 2026 08:49:44 +0100 Subject: [PATCH 2/4] Request notif permission with embedded distrib Signed-off-by: sim --- .../talk/conversationlist/ConversationsListActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 fd3c2546ef..80ea7be6b7 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -89,6 +89,7 @@ import com.nextcloud.talk.utils.ParticipantPermissions import com.nextcloud.talk.utils.ShareUtils import com.nextcloud.talk.utils.ShortcutManagerHelper 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 @@ -363,7 +364,8 @@ class ConversationsListActivity : BaseActivity() { !platformPermissionUtil.isPostNotificationsPermissionGranted() && ( ClosedInterfaceImpl().isGooglePlayServicesAvailable || - appPreferences.useUnifiedPush + appPreferences.useUnifiedPush || + UnifiedPushUtils.hasEmbeddedDistributor(context) ) ) { requestPermissions( From 1adb85c7493c72dce041aab414fd86bb1f719754 Mon Sep 17 00:00:00 2001 From: sim Date: Sat, 17 Jan 2026 08:56:32 +0100 Subject: [PATCH 3/4] Fix settings & diagnose with embedded distrib Signed-off-by: sim --- .../com/nextcloud/talk/diagnosis/DiagnosisActivity.kt | 10 +++++++--- .../com/nextcloud/talk/settings/SettingsActivity.kt | 5 ++++- 2 files changed, 11 insertions(+), 4 deletions(-) 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 7ac4ac1276..e95f8ed18f 100644 --- a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt @@ -80,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 @@ -108,6 +109,7 @@ class DiagnosisActivity : BaseActivity() { offerUnifiedPush = nUnifiedPushServices > 0 && userManager.users.blockingGet().all { it.hasWebPushCapability } useUnifiedPush = appPreferences.useUnifiedPush + useEmbeddedDistrib = UnifiedPushUtils.hasEmbeddedDistributor(context) && !useUnifiedPush unifiedPushService = UnifiedPush.getAckDistributor(this) ?: "N/A" setContent { @@ -161,7 +163,9 @@ class DiagnosisActivity : BaseActivity() { viewState = viewState, onTestPushClick = { diagnosisViewModel.fetchTestPushResult() }, onDismissDialog = { diagnosisViewModel.dismissDialog() }, - showTestPushButton = isGooglePlayServicesAvailable || useUnifiedPush, + showTestPushButton = isGooglePlayServicesAvailable || + useUnifiedPush || + useEmbeddedDistrib, isOnline = isOnline ) } @@ -255,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) @@ -306,7 +310,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/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index 3f512f3821..11bd8570ed 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -388,7 +388,10 @@ 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 From 7415fec49f698a2d923450af2b12bee1dd4b9cb7 Mon Sep 17 00:00:00 2001 From: sim Date: Tue, 23 Jun 2026 13:56:03 +0200 Subject: [PATCH 4/4] Use dedicated diagnosis entry for FCM support with embedded distrib Signed-off-by: sim --- .../talk/diagnosis/DiagnosisActivity.kt | 30 ++++++++++++------- app/src/main/res/values/strings.xml | 1 + 2 files changed, 21 insertions(+), 10 deletions(-) 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 e95f8ed18f..f8a91c9c52 100644 --- a/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/diagnosis/DiagnosisActivity.kt @@ -259,17 +259,27 @@ class DiagnosisActivity : BaseActivity() { value = Build.VERSION.SDK_INT.toString() ) - 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) - ) - } else { - addDiagnosisEntry( - key = context.resources.getString(R.string.nc_diagnosis_gplay_available_title), - value = context.resources.getString(R.string.nc_diagnosis_gplay_available_no_short) - ) + when (true) { + isGooglePlayServicesAvailable -> { + addDiagnosisEntry( + key = context.resources.getString(R.string.nc_diagnosis_gplay_available_title), + value = context.resources.getString(R.string.nc_diagnosis_gplay_available_yes) + ) + } + useEmbeddedDistrib -> { + addDiagnosisEntry( + key = context.resources.getString(R.string.nc_diagnosis_gplay_available_title), + value = context.resources.getString(R.string.nc_diagnosis_gplay_available_yes_up) + ) + } + else -> { + addDiagnosisEntry( + key = context.resources.getString(R.string.nc_diagnosis_gplay_available_title), + value = context.resources.getString(R.string.nc_diagnosis_gplay_available_no_short) + ) + } } + addDiagnosisEntry( key = getString(R.string.nc_diagnosis_unifiedpush_available_title), value = context.resources.getQuantityString( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index addf50e603..c5996722ca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -236,6 +236,7 @@ How to translate with transifex: Registered users Google Play services Google Play services are available + Google Play services are available, with the embedded distributor Google Play services are not available. Notifications are not supported Google Play services are not available. UnifiedPush services