diff --git a/README.md b/README.md
index 4ea25808be..a58e2a5359 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,8 @@
alt="Get it on F-Droid"
height="80">](https://f-droid.org/packages/com.nextcloud.talk2/)
-Please note that Notifications won't work with the F-Droid version due to missing Google Play Services.
+Please note that the F-Droid version uses UnifiedPush notifications and the Play Store version uses Google Play
+Services notifications.
|||||||
|---|---|---|---|---|---|
@@ -63,7 +64,8 @@ Easy starting points are also reviewing [pull requests](https://github.com/nextc
So you would like to contribute by testing? Awesome, we appreciate that very much.
To report a bug for the alpha or beta version, just create an issue on github like you would for the stable version and
- provide the version number. Please remember that Google Services are necessary to receive push notifications.
+ provide the version number. Please remember that Google Services are necessary to receive push notifications in the
+Play Store version whereas the F-Droid version uses UnifiedPush notifications.
#### Beta versions (Release Candidates) :package:
diff --git a/app/build.gradle b/app/build.gradle
index 5d02952046..8798d8ec65 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -303,6 +303,9 @@ dependencies {
implementation 'com.github.nextcloud.android-common:ui:0.23.0'
implementation 'com.github.nextcloud-deps:android-talk-webrtc:121.6167.0'
+ // unified push library for generic flavour
+ genericImplementation 'org.codeberg.UnifiedPush:android-connector:2.4.0'
+
gplayImplementation 'com.google.android.gms:play-services-base:18.5.0'
gplayImplementation "com.google.firebase:firebase-messaging:24.0.1"
@@ -418,4 +421,4 @@ detekt {
ksp {
arg('room.schemaLocation', "$projectDir/schemas")
-}
\ No newline at end of file
+}
diff --git a/app/src/generic/AndroidManifest.xml b/app/src/generic/AndroidManifest.xml
new file mode 100644
index 0000000000..842263df52
--- /dev/null
+++ b/app/src/generic/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/generic/java/com/nextcloud/talk/receivers/UnifiedPush.kt b/app/src/generic/java/com/nextcloud/talk/receivers/UnifiedPush.kt
new file mode 100644
index 0000000000..36c086ffa2
--- /dev/null
+++ b/app/src/generic/java/com/nextcloud/talk/receivers/UnifiedPush.kt
@@ -0,0 +1,163 @@
+/*
+ * Nextcloud Talk - Android Client
+ *
+ * SPDX-FileCopyrightText: 2024 Your Name
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+package com.nextcloud.talk.receivers
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.text.SpannableString
+import android.text.method.LinkMovementMethod
+import android.text.util.Linkify
+import android.util.Log
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import androidx.work.Data
+import androidx.work.OneTimeWorkRequest
+import androidx.work.WorkManager
+import com.nextcloud.talk.R
+import com.nextcloud.talk.activities.MainActivity
+import com.nextcloud.talk.jobs.NotificationWorker
+import com.nextcloud.talk.utils.bundle.BundleKeys
+import org.greenrobot.eventbus.EventBus
+import org.unifiedpush.android.connector.MessagingReceiver
+import org.unifiedpush.android.connector.PREF_MASTER
+import org.unifiedpush.android.connector.PREF_MASTER_NO_DISTRIB_DIALOG_ACK
+import org.unifiedpush.android.connector.UnifiedPush
+
+class UnifiedPush : MessagingReceiver() {
+ private val TAG: String? = UnifiedPush::class.java.simpleName
+
+ companion object {
+ fun getNumberOfDistributorsAvailable(context: Context) : Int {
+ return UnifiedPush.getDistributors(context).size
+ }
+
+ private fun resetSeenNoDistributorsInfo(context: Context) {
+ context.getSharedPreferences(PREF_MASTER, Context.MODE_PRIVATE)
+ .edit().putBoolean(PREF_MASTER_NO_DISTRIB_DIALOG_ACK, false).apply()
+ }
+
+ fun registerForPushMessaging(context: Context, accountName: String): Boolean {
+ // if a distributor is registered and available, re-register to ensure in sync
+ if (UnifiedPush.getSavedDistributor(context) !== null) {
+ UnifiedPush.registerApp(context, accountName)
+ return false
+ }
+
+ val distributors = UnifiedPush.getDistributors(context)
+
+ // if no distributors available
+ if (distributors.isEmpty()) {
+ // if user has already been shown the info dialog, return
+ val preferences = context.getSharedPreferences(PREF_MASTER, Context.MODE_PRIVATE)
+ if (preferences.getBoolean(PREF_MASTER_NO_DISTRIB_DIALOG_ACK, false) == true) {
+ return false
+ }
+
+ // show user some info about unified push
+ val message = TextView(context)
+ val s = SpannableString(context.getString(R.string.unified_push_no_distributors_dialog_text))
+ Linkify.addLinks(s, Linkify.WEB_URLS)
+ message.text = s
+ message.movementMethod = LinkMovementMethod.getInstance()
+ message.setPadding(32, 32, 32, 32)
+ AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.unified_push_no_distributors_dialog_title))
+ .setView(message)
+ .setPositiveButton(context.getString(R.string.nc_ok)) {
+ _, _ -> preferences.edit().putBoolean(PREF_MASTER_NO_DISTRIB_DIALOG_ACK, true).apply()
+ }.setOnDismissListener {
+ // send message to main activity that it can move on to it's next default activity
+ EventBus.getDefault().post(MainActivity.ProceedToConversationsListMessageEvent())
+ }.show()
+
+ return true // have a dialog to show, need main activity to wait
+ }
+
+ // 1 distributor available
+ if (distributors.size == 1) {
+ UnifiedPush.saveDistributor(context, distributors.first())
+ UnifiedPush.registerApp(context, accountName)
+ return false
+ }
+
+ // multiple distributors available, show dialog for user to choose
+ val distributorsArray = distributors.toTypedArray()
+ val distributorsNameArray = distributorsArray.map {
+ try {
+ val ai = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ context.packageManager.getApplicationInfo(
+ it,
+ PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong())
+ )
+ } else {
+ context.packageManager.getApplicationInfo(it, 0)
+ }
+ context.packageManager.getApplicationLabel(ai)
+ } catch (e: PackageManager.NameNotFoundException) {
+ it
+ } as String
+ }.toTypedArray()
+
+ AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.unified_push_choose_distributor_title))
+ .setItems(distributorsNameArray) { _, which ->
+ val distributor = distributorsArray[which]
+ UnifiedPush.saveDistributor(context, distributor)
+ UnifiedPush.registerApp(context, accountName)
+ }.setOnDismissListener {
+ // send message to main activity that it can move on to it's next default activity
+ EventBus.getDefault().post(MainActivity.ProceedToConversationsListMessageEvent())
+ }.show()
+
+ return true // have a dialog to show, need main activity to wait
+ }
+
+ fun unregisterForPushMessaging(context: Context, accountName: String) {
+ // try and unregister with unified push distributor
+ UnifiedPush.unregisterApp(context, instance = accountName)
+ resetSeenNoDistributorsInfo(context)
+ }
+ }
+
+ override fun onMessage(context: Context, message: ByteArray, instance: String) {
+ Log.d(TAG, "UP onMessage")
+
+ val messageString = message.toString(Charsets.UTF_8)
+
+ if (messageString.isNotEmpty() && instance.isNotEmpty()) {
+ val messageData = Data.Builder()
+ .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, messageString)
+ .putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, instance)
+ .putInt(
+ BundleKeys.KEY_NOTIFICATION_BACKEND_TYPE, NotificationWorker.Companion.BackendType.UNIFIED_PUSH.value)
+ .build()
+ val notificationWork =
+ OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
+ .build()
+ WorkManager.getInstance().enqueue(notificationWork)
+ }
+ }
+
+ override fun onNewEndpoint(context: Context, endpoint: String, instance: String) {
+ // called when a new endpoint is to be used for sending push messages
+ // do nothing
+ }
+
+ override fun onRegistrationFailed(context: Context, instance: String) {
+ // called when the registration is not possible, eg. no network
+ // just dump the registration to make sure it is cleaned up. re-register will be auto-reattempted
+ unregisterForPushMessaging(context, instance)
+
+ }
+
+ override fun onUnregistered(context: Context, instance: String) {
+ // called when this application is remotely unregistered from receiving push messages
+ unregisterForPushMessaging(context, instance)
+ }
+}
diff --git a/app/src/generic/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java b/app/src/generic/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
index cafaa071d5..5bad91d55c 100644
--- a/app/src/generic/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
+++ b/app/src/generic/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
@@ -8,7 +8,11 @@
package com.nextcloud.talk.utils;
+import android.content.Context;
import com.nextcloud.talk.interfaces.ClosedInterface;
+import com.nextcloud.talk.receivers.UnifiedPush;
+import androidx.annotation.NonNull;
+
public class ClosedInterfaceImpl implements ClosedInterface {
@Override
@@ -17,12 +21,29 @@ public void providerInstallerInstallIfNeededAsync() {
}
@Override
- public boolean isGooglePlayServicesAvailable() {
- return false;
+ public boolean isPushMessagingServiceAvailable(Context context) {
+ return (UnifiedPush.Companion.getNumberOfDistributorsAvailable(context) > 0);
+ }
+
+ @Override
+ public String pushMessagingProvider() {
+ return "unifiedpush";
+ }
+
+ @Override
+ public boolean registerWithServer(@NonNull Context context, String username) {
+ // unified push available in generic build
+ if (username == null)
+ return false;
+ return UnifiedPush.Companion.registerForPushMessaging(context, username);
}
+ @NonNull
@Override
- public void setUpPushTokenRegistration() {
- // no push notifications for generic build variant
+ public void unregisterWithServer(@NonNull Context context, @NonNull String username) {
+ // unified push available in generic build
+ if (username == null)
+ return;
+ UnifiedPush.Companion.unregisterForPushMessaging(context, username);
}
}
diff --git a/app/src/gplay/java/com/nextcloud/talk/services/firebase/NCFirebaseMessagingService.kt b/app/src/gplay/java/com/nextcloud/talk/services/firebase/NCFirebaseMessagingService.kt
index ac4e888aee..5e0d44aff5 100644
--- a/app/src/gplay/java/com/nextcloud/talk/services/firebase/NCFirebaseMessagingService.kt
+++ b/app/src/gplay/java/com/nextcloud/talk/services/firebase/NCFirebaseMessagingService.kt
@@ -50,6 +50,7 @@ class NCFirebaseMessagingService : FirebaseMessagingService() {
val messageData = Data.Builder()
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
.putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
+ .putInt(BundleKeys.KEY_NOTIFICATION_BACKEND_TYPE, NotificationWorker.Companion.BackendType.FIREBASE_CLOUD_MESSAGING.value)
.build()
val notificationWork =
OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData)
diff --git a/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt b/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt
index 6a2fd0d3b9..dbc108bd9c 100644
--- a/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt
+++ b/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt
@@ -8,6 +8,7 @@
*/
package com.nextcloud.talk.utils
+import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.work.ExistingPeriodicWorkPolicy
@@ -18,6 +19,7 @@ import autodagger.AutoInjector
import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.security.ProviderInstaller
+import com.nextcloud.talk.activities.BaseActivity
import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.interfaces.ClosedInterface
import com.nextcloud.talk.jobs.GetFirebasePushTokenWorker
@@ -26,7 +28,13 @@ import java.util.concurrent.TimeUnit
@AutoInjector(NextcloudTalkApplication::class)
class ClosedInterfaceImpl : ClosedInterface, ProviderInstaller.ProviderInstallListener {
- override val isGooglePlayServicesAvailable: Boolean = isGPlayServicesAvailable()
+ override fun isPushMessagingServiceAvailable(context: Context): Boolean {
+ return isGPlayServicesAvailable()
+ }
+
+ override fun pushMessagingProvider(): String {
+ return "gplay"
+ }
override fun providerInstallerInstallIfNeededAsync() {
NextcloudTalkApplication.sharedApplication?.let {
@@ -59,11 +67,17 @@ class ClosedInterfaceImpl : ClosedInterface, ProviderInstaller.ProviderInstallLi
}
}
- override fun setUpPushTokenRegistration() {
+ override fun registerWithServer(context: Context, username: String?): Boolean {
val firebasePushTokenWorker = OneTimeWorkRequest.Builder(GetFirebasePushTokenWorker::class.java).build()
WorkManager.getInstance().enqueue(firebasePushTokenWorker)
setUpPeriodicTokenRefreshFromFCM()
+
+ return false
+ }
+
+ override fun unregisterWithServer(context: Context, username: String?) {
+ // do nothing
}
private fun setUpPeriodicTokenRefreshFromFCM() {
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 0b3a3caefe..7a58cd884d 100644
--- a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt
@@ -9,6 +9,7 @@
package com.nextcloud.talk.account
import android.annotation.SuppressLint
+import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle
@@ -235,6 +236,8 @@ class AccountVerificationActivity : BaseActivity() {
}
private fun storeProfile(displayName: String?, userId: String, capabilitiesOverall: CapabilitiesOverall) {
+ val activityContext: Context = this // for capture by lambda used by subscribe() below
+
userManager.storeProfile(
username,
UserManager.UserAttributes(
@@ -260,8 +263,8 @@ class AccountVerificationActivity : BaseActivity() {
@SuppressLint("SetTextI18n")
override fun onSuccess(user: User) {
internalAccountId = user.id!!
- if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
- ClosedInterfaceImpl().setUpPushTokenRegistration()
+ if (ClosedInterfaceImpl().isPushMessagingServiceAvailable(context)) {
+ ClosedInterfaceImpl().registerWithServer(activityContext, user.username)
} else {
Log.w(TAG, "Skipping push registration.")
runOnUiThread {
diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
index e56f4f8983..be58a0333c 100644
--- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt
@@ -46,10 +46,14 @@ import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
+import org.greenrobot.eventbus.ThreadMode
+import org.greenrobot.eventbus.Subscribe
import javax.inject.Inject
@AutoInjector(NextcloudTalkApplication::class)
class MainActivity : BaseActivity(), ActionBarProvider {
+ class ProceedToConversationsListMessageEvent { }
+
lateinit var binding: ActivityMainBinding
@Inject
@@ -76,9 +80,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
})
// Set the default theme to replace the launch screen theme.
- setTheme(R.style.AppTheme)
binding = ActivityMainBinding.inflate(layoutInflater)
- setContentView(binding.root)
NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this)
@@ -138,6 +140,11 @@ class MainActivity : BaseActivity(), ActionBarProvider {
super.onStop()
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onMessageEvent(event: ProceedToConversationsListMessageEvent) {
+ openConversationList()
+ }
+
private fun openConversationList() {
val intent = Intent(this, ConversationsListActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
@@ -257,6 +264,7 @@ class MainActivity : BaseActivity(), ActionBarProvider {
appPreferences.isDbRoomMigrated = true
}
+ val activityContext: Context = this // for capture by lambda used by subscribe() below
userManager.users.subscribe(object : SingleObserver> {
override fun onSubscribe(d: Disposable) {
// unused atm
@@ -264,9 +272,13 @@ class MainActivity : BaseActivity(), ActionBarProvider {
override fun onSuccess(users: List) {
if (users.isNotEmpty()) {
- ClosedInterfaceImpl().setUpPushTokenRegistration()
runOnUiThread {
- openConversationList()
+ var needUIFeedbackFromUser = ClosedInterfaceImpl().registerWithServer(activityContext,
+ users.first().username)
+ // if push registration does not need to show a dialog, open the conversation list now
+ if (needUIFeedbackFromUser == false) {
+ openConversationList()
+ }
}
} else {
runOnUiThread {
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 9b9d74f549..6828e2af42 100644
--- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt
@@ -250,7 +250,7 @@ class ConversationsListActivity :
// handle notification permission on API level >= 33
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
!platformPermissionUtil.isPostNotificationsPermissionGranted() &&
- ClosedInterfaceImpl().isGooglePlayServicesAvailable
+ ClosedInterfaceImpl().isPushMessagingServiceAvailable(context)
) {
requestPermissions(
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
@@ -1428,7 +1428,7 @@ class ConversationsListActivity :
// whenever user allowed notifications, also check to ignore battery optimization
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (!PowerManagerUtils().isIgnoringBatteryOptimizations() &&
- ClosedInterfaceImpl().isGooglePlayServicesAvailable
+ ClosedInterfaceImpl().isPushMessagingServiceAvailable(context)
) {
val dialogText = String.format(
context.resources.getString(R.string.nc_ignore_battery_optimization_dialog_text),
diff --git a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt
index 68e69dc65d..1437141e9c 100644
--- a/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/diagnose/DiagnoseActivity.kt
@@ -64,7 +64,8 @@ class DiagnoseActivity : BaseActivity() {
@Inject
lateinit var platformPermissionUtil: PlatformPermissionUtil
- private var isGooglePlayServicesAvailable: Boolean = false
+ private var isPushMessagingServiceAvailable: Boolean = false
+ private var pushMessagingProvider: String = ""
private val markdownText = SpannableStringBuilder()
@@ -82,7 +83,8 @@ class DiagnoseActivity : BaseActivity() {
super.onResume()
supportActionBar?.show()
- isGooglePlayServicesAvailable = ClosedInterfaceImpl().isGooglePlayServicesAvailable
+ isPushMessagingServiceAvailable = ClosedInterfaceImpl().isPushMessagingServiceAvailable(context)
+ pushMessagingProvider = ClosedInterfaceImpl().pushMessagingProvider()
markdownText.clear()
setupMetaValues()
@@ -214,12 +216,14 @@ class DiagnoseActivity : BaseActivity() {
addKey(context.resources.getString(R.string.nc_diagnose_android_version_title))
addValue(Build.VERSION.SDK_INT.toString())
- if (isGooglePlayServicesAvailable) {
- addKey(context.resources.getString(R.string.nc_diagnose_gplay_available_title))
- addValue(context.resources.getString(R.string.nc_diagnose_gplay_available_yes))
- } else {
- addKey(context.resources.getString(R.string.nc_diagnose_gplay_available_title))
- addValue(context.resources.getString(R.string.nc_diagnose_gplay_available_no))
+ addKey(context.resources.getString(R.string.nc_diagnose_push_notifications_available_title))
+ if (pushMessagingProvider == "gplay") {
+ addValue(context.resources.getString(R.string.nc_diagnose_push_notifications_gplay))
+ } else if (pushMessagingProvider == "unifiedpush") {
+ addValue(context.resources.getString(R.string.nc_diagnose_push_notifications_unified_push))
+ }
+ if (isPushMessagingServiceAvailable == false) {
+ addValue(context.resources.getString(R.string.nc_diagnose_push_notifications_available_no))
}
}
@@ -237,7 +241,7 @@ class DiagnoseActivity : BaseActivity() {
addKey(context.resources.getString(R.string.nc_diagnose_flavor))
addValue(BuildConfig.FLAVOR)
- if (isGooglePlayServicesAvailable) {
+ if (isPushMessagingServiceAvailable) {
addKey(context.resources.getString(R.string.nc_diagnose_battery_optimization_title))
if (PowerManagerUtils().isIgnoringBatteryOptimizations()) {
@@ -270,30 +274,32 @@ class DiagnoseActivity : BaseActivity() {
)
)
- addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_title))
- if (appPreferences.pushToken.isNullOrEmpty()) {
- addValue(context.resources.getString(R.string.nc_diagnose_firebase_push_token_missing))
- } else {
- addValue("${appPreferences.pushToken.substring(0, 5)}...")
- }
+ if (pushMessagingProvider == "gplay") {
+ addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_title))
+ if (appPreferences.pushToken.isNullOrEmpty()) {
+ addValue(context.resources.getString(R.string.nc_diagnose_firebase_push_token_missing))
+ } else {
+ addValue("${appPreferences.pushToken.substring(0, 5)}...")
+ }
- addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_generated))
- if (appPreferences.pushTokenLatestGeneration != null && appPreferences.pushTokenLatestGeneration != 0L) {
- addValue(
- DisplayUtils.unixTimeToHumanReadable(
- appPreferences
- .pushTokenLatestGeneration
+ addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_generated))
+ if (appPreferences.pushTokenLatestGeneration != null && appPreferences.pushTokenLatestGeneration != 0L) {
+ addValue(
+ DisplayUtils.unixTimeToHumanReadable(
+ appPreferences
+ .pushTokenLatestGeneration
+ )
)
- )
- } else {
- addValue(context.resources.getString(R.string.nc_common_unknown))
- }
+ } else {
+ addValue(context.resources.getString(R.string.nc_common_unknown))
+ }
- addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_fetch))
- if (appPreferences.pushTokenLatestFetch != null && appPreferences.pushTokenLatestFetch != 0L) {
- addValue(DisplayUtils.unixTimeToHumanReadable(appPreferences.pushTokenLatestFetch))
- } else {
- addValue(context.resources.getString(R.string.nc_common_unknown))
+ addKey(context.resources.getString(R.string.nc_diagnose_firebase_push_token_latest_fetch))
+ if (appPreferences.pushTokenLatestFetch != null && appPreferences.pushTokenLatestFetch != 0L) {
+ addValue(DisplayUtils.unixTimeToHumanReadable(appPreferences.pushTokenLatestFetch))
+ } else {
+ addValue(context.resources.getString(R.string.nc_common_unknown))
+ }
}
}
@@ -324,7 +330,7 @@ class DiagnoseActivity : BaseActivity() {
)
)
- if (isGooglePlayServicesAvailable) {
+ if ((isPushMessagingServiceAvailable) && (pushMessagingProvider == "gplay")) {
setupPushRegistrationDiagnose()
}
diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt
index eda9062443..cd761861cd 100644
--- a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt
+++ b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt
@@ -7,9 +7,14 @@
*/
package com.nextcloud.talk.interfaces
+import android.content.Context
+
+
interface ClosedInterface {
- val isGooglePlayServicesAvailable: Boolean
+ fun isPushMessagingServiceAvailable(context: Context): Boolean
+ fun pushMessagingProvider(): String
fun providerInstallerInstallIfNeededAsync()
- fun setUpPushTokenRegistration()
+ fun registerWithServer(context: Context, username: String?): Boolean
+ fun unregisterWithServer(context: Context, username: String?)
}
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
index 870bb94194..4145a7cfe6 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
+++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt
@@ -148,7 +148,33 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
sharedApplication!!.componentApplication.inject(this)
context = applicationContext
- initDecryptedData(inputData)
+ when (inputData.getInt(BundleKeys.KEY_NOTIFICATION_BACKEND_TYPE, -1)) {
+ Companion.BackendType.FIREBASE_CLOUD_MESSAGING.value -> {
+ initDecryptedData(inputData)
+ }
+ Companion.BackendType.UNIFIED_PUSH.value -> {
+ pushMessage = LoganSquare.parse(inputData.getString(BundleKeys.KEY_NOTIFICATION_SUBJECT),
+ DecryptedPushMessage::class.java)
+
+ val messageUser = inputData.getString(BundleKeys.KEY_NOTIFICATION_SIGNATURE)
+ val users = userManager!!.users.blockingGet()
+ if (users != null && users.size > 0) {
+ for (user in users) {
+ if (user.username == messageUser) {
+ signatureVerification = SignatureVerification(true, user)
+ break
+ }
+ }
+ }
+ if (signatureVerification === null)
+ return Result.failure()
+ }
+ else -> {
+ // message not received from a valid backend
+ return Result.failure()
+ }
+ }
+
initNcApiAndCredentials()
notificationManager = NotificationManagerCompat.from(context!!)
@@ -1050,5 +1076,13 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
private const val TIMER_COUNT = 12
private const val TIMER_DELAY: Long = 5
private const val GET_ROOM_RETRY_COUNT: Long = 3
+ enum class BackendType(val value: Int) {
+ NONE(-1),
+ FIREBASE_CLOUD_MESSAGING(1),
+ UNIFIED_PUSH(2);
+ companion object {
+ fun fromInt(value: Int) = BackendType.values().first { it.value == value }
+ }
+ }
}
}
diff --git a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java
index 6473d0e06d..9d9165252b 100644
--- a/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java
+++ b/app/src/main/java/com/nextcloud/talk/jobs/PushRegistrationWorker.java
@@ -40,15 +40,18 @@ public class PushRegistrationWorker extends Worker {
@Inject
OkHttpClient okHttpClient;
+ Context workerContext;
+
public PushRegistrationWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
+ workerContext = context;
}
@NonNull
@Override
public Result doWork() {
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
- if (new ClosedInterfaceImpl().isGooglePlayServicesAvailable()) {
+ if (new ClosedInterfaceImpl().isPushMessagingServiceAvailable(workerContext)) {
Data data = getInputData();
String origin = data.getString("origin");
Log.d(TAG, "PushRegistrationWorker called via " + origin);
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 e86439a59a..abb34ccdc3 100644
--- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
+++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt
@@ -149,6 +149,7 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
)
setupDiagnose()
+ setupResetPushPreference()
setupPrivacyUrl()
setupSourceCodeUrl()
binding.settingsVersionSummary.text = String.format("v" + BuildConfig.VERSION_NAME)
@@ -262,8 +263,8 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
@Suppress("LongMethod")
private fun setupNotificationPermissionSettings() {
- if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) {
- binding.settingsGplayOnlyWrapper.visibility = View.VISIBLE
+ if (ClosedInterfaceImpl().isPushMessagingServiceAvailable(context)) {
+ binding.settingsPushNotificationsOnlyWrapper.visibility = View.VISIBLE
setTroubleshootingClickListenersIfNecessary()
@@ -327,8 +328,20 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
binding.settingsNotificationsPermissionWrapper.visibility = View.GONE
}
} else {
- binding.settingsGplayOnlyWrapper.visibility = View.GONE
- binding.settingsGplayNotAvailable.visibility = View.VISIBLE
+ binding.settingsPushNotificationsOnlyWrapper.visibility = View.GONE
+
+ val pushMessagingProvider = ClosedInterfaceImpl().pushMessagingProvider()
+ if (pushMessagingProvider == "gplay") {
+ binding.settingsPushNotificationsNotAvailableText.append("\n".plus(
+ resources!!.getString(R.string.nc_diagnose_push_notifications_gplay).plus("\n").plus(resources!!
+ .getString(R.string.nc_diagnose_push_notifications_gplay_rectify))))
+ } else if (pushMessagingProvider == "unifiedpush") {
+ binding.settingsPushNotificationsNotAvailableText.append("\n".plus(
+ resources!!.getString(R.string.nc_diagnose_push_notifications_unified_push).plus("\n").plus
+ (resources!!.getString(R.string.nc_diagnose_push_notifications_unified_push_rectify))))
+ }
+
+ binding.settingsPushNotificationsNotAvailable.visibility = View.VISIBLE
}
}
@@ -464,6 +477,21 @@ class SettingsActivity : BaseActivity(), SetPhoneNumberDialogFragment.SetPhoneNu
}
}
+ private fun setupResetPushPreference() {
+ binding.resetPushNotificationsWrapper.setOnClickListener {
+ for (user in userManager.users.blockingGet()) {
+ ClosedInterfaceImpl().unregisterWithServer(binding.root.context, user.username)
+ }
+
+ Snackbar.make(
+ binding.root,
+ resources!!.getString(R.string.prefs_reset_push_done),
+ Snackbar.LENGTH_LONG
+ ).show()
+ true
+ }
+ }
+
private fun setupLicenceSetting() {
if (!TextUtils.isEmpty(resources!!.getString(R.string.nc_gpl3_url))) {
binding.settingsLicence.setOnClickListener {
diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
index 53de01b275..1bf4e83fbb 100644
--- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
+++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt
@@ -32,6 +32,7 @@ object BundleKeys {
const val KEY_MODIFIED_BASE_URL = "KEY_MODIFIED_BASE_URL"
const val KEY_NOTIFICATION_SUBJECT = "KEY_NOTIFICATION_SUBJECT"
const val KEY_NOTIFICATION_SIGNATURE = "KEY_NOTIFICATION_SIGNATURE"
+ const val KEY_NOTIFICATION_BACKEND_TYPE = "KEY_NOTIFICATION_BACKEND_TYPE"
const val KEY_INTERNAL_USER_ID = "KEY_INTERNAL_USER_ID"
const val KEY_CONVERSATION_TYPE = "KEY_CONVERSATION_TYPE"
const val KEY_INVITED_PARTICIPANTS = "KEY_INVITED_PARTICIPANTS"
diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml
index 36c164bc63..afe7830677 100644
--- a/app/src/main/res/layout/activity_settings.xml
+++ b/app/src/main/res/layout/activity_settings.xml
@@ -206,7 +206,7 @@
android:textStyle="bold"/>
@@ -254,7 +254,7 @@
+ android:text="@string/nc_diagnose_push_notifications_available_no" />
@@ -602,14 +603,36 @@
android:id="@+id/diagnose_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="@dimen/headline_text_size"
- android:text="@string/nc_settings_diagnose_title"/>
+ android:text="@string/nc_settings_diagnose_title"
+ android:textSize="@dimen/headline_text_size" />
+ android:text="@string/nc_settings_diagnose_subtitle" />
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 13ed2b442f..037bab4c89 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -191,9 +191,14 @@ How to translate with transifex:
App name
App version
Registered users
- Google Play services
- Google Play services are available
- Google Play services are not available. Notifications are not supported
+ Push notification services
+ Push notifications are not available.
+ Push notifications are using the Google Play engine.
+ An alternate version of Talk is available for devices
+ without the Google Play Apis.
+ Push notifications are using the UnifiedPush engine.
+ Notifications can be enabled via UnifiedPush.
+ Please see https://unifiedpush.org
Battery settings
Battery optimization is not ignored. This should be changed!
Battery optimization is ignored, all fine
@@ -810,4 +815,12 @@ How to translate with transifex:
Show ban reason
Error occurred when unbanning participant
Connection lost
+ Choose a UnifiedPush distributor to use for
+ notifications
+ You can use UnifiedPush
+ To enable push and chat notifications you need to install a
+ Unified Push distributor.\nFor more information visit https://unifiedpush.org
+ Reset push notifications
+ Reset push notifications in case of push messaging issues
+ Push notifications reset
diff --git a/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java b/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
index 94778d8176..7fc3530d67 100644
--- a/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
+++ b/app/src/qa/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.java
@@ -8,7 +8,11 @@
package com.nextcloud.talk.utils;
+import android.content.Context;
import com.nextcloud.talk.interfaces.ClosedInterface;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
public class ClosedInterfaceImpl implements ClosedInterface {
@Override
@@ -17,12 +21,23 @@ public void providerInstallerInstallIfNeededAsync() {
}
@Override
- public boolean isGooglePlayServicesAvailable() {
+ public boolean isPushMessagingServiceAvailable(Context context) {
+ return false;
+ }
+
+ @Override
+ public String pushMessagingProvider() {
+ return "qa";
+ }
+
+ @Override
+ public boolean registerWithServer(Context context, @Nullable String username) {
+ // no push notifications for qa build flavour :(
return false;
}
@Override
- public void setUpPushTokenRegistration() {
+ public void unregisterWithServer(@NonNull Context context, @Nullable String username) {
// no push notifications for qa build flavour :(
}
}
diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml
index e5e82d0b49..0ff0914807 100644
--- a/gradle/verification-metadata.xml
+++ b/gradle/verification-metadata.xml
@@ -5631,7 +5631,15 @@
-
+
+
+
+
+
+
+
+
+