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 @@ - + + + +