Merge branch 'unifiedpush' into sc

Change-Id: I0d19e4032b0f31cc1dee404cb383046d04a3259e
This commit is contained in:
SpiritCroc 2021-08-21 09:21:19 +02:00
commit 4918b7e7bf
33 changed files with 610 additions and 448 deletions

View file

@ -44,6 +44,8 @@ allprojects {
includeGroupByRegex 'com\\.github\\.chrisbanes' includeGroupByRegex 'com\\.github\\.chrisbanes'
// PFLockScreen-Android // PFLockScreen-Android
includeGroupByRegex 'com\\.github\\.vector-im' includeGroupByRegex 'com\\.github\\.vector-im'
// UnifiedPush
includeGroupByRegex 'com\\.github\\.UnifiedPush'
// Chat effects // Chat effects
includeGroupByRegex 'com\\.github\\.jetradarmobile' includeGroupByRegex 'com\\.github\\.jetradarmobile'

View file

@ -0,0 +1 @@
Use UnifiedPush and allows user to have push without FCM.

View file

@ -311,6 +311,10 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
} }
packagingOptions {
exclude 'META-INF/lib_release.kotlin_module'
}
} }
dependencies { dependencies {
@ -344,6 +348,7 @@ dependencies {
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
@ -360,6 +365,7 @@ dependencies {
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.9.0" implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.9.0"
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1" implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.1"
kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version" kapt "com.squareup.moshi:moshi-kotlin-codegen:$moshi_version"
@ -455,8 +461,10 @@ dependencies {
implementation "com.google.dagger:dagger:$daggerVersion" implementation "com.google.dagger:dagger:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion" kapt "com.google.dagger:dagger-compiler:$daggerVersion"
// gplay flavor only // UnifiedPush
gplayImplementation('com.google.firebase:firebase-messaging:22.0.0') { implementation 'com.github.UnifiedPush:android-connector:1.2.0'
// UnifiedPush gplay flavor only
gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:1.1.0') {
exclude group: 'com.google.firebase', module: 'firebase-core' exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics' exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector' exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'

View file

@ -1,29 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="im.vector.app">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--
Required for long polling account synchronisation in background.
If not present ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent action won't work
-->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application>
<receiver android:name=".fdroid.receiver.OnApplicationUpgradeOrRebootReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".fdroid.receiver.AlarmSyncBroadcastReceiver"
android:enabled="true"
android:exported="false" />
</application>
</manifest>

View file

@ -17,56 +17,13 @@
package im.vector.app.push.fcm package im.vector.app.push.fcm
import android.app.Activity
import android.content.Context import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.pushers.PushersManager
import im.vector.app.fdroid.BackgroundSyncStarter
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.features.settings.VectorPreferences
/** /**
* This class has an alter ego in the gplay variant. * This class has an alter ego in the gplay variant.
*/ */
object FcmHelper { object FcmHelper {
fun isPlayServicesAvailable(context: Context): Boolean {
fun isPushSupported(): Boolean = false return false
/**
* Retrieves the FCM registration token.
*
* @return the FCM token or null if not received from FCM
*/
fun getFcmToken(context: Context): String? {
return null
}
/**
* Store FCM token to the SharedPrefs
*
* @param context android context
* @param token the token to store
*/
fun storeFcmToken(context: Context, token: String?) {
// No op
}
/**
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
*
* @param activity the first launch Activity
*/
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) {
// No op
}
fun onEnterForeground(context: Context, activeSessionHolder: ActiveSessionHolder) {
// try to stop all regardless of background mode
activeSessionHolder.getSafeActiveSession()?.stopAnyBackgroundSync()
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
BackgroundSyncStarter.start(context, vectorPreferences, activeSessionHolder)
} }
} }

View file

@ -16,21 +16,28 @@
package im.vector.app.push.fcm package im.vector.app.push.fcm
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.app.fdroid.features.settings.troubleshoot.TestAutoStartBoot import im.vector.app.core.pushers.UPHelper
import im.vector.app.fdroid.features.settings.troubleshoot.TestBackgroundRestrictions import im.vector.app.features.settings.troubleshoot.TestAutoStartBoot
import im.vector.app.fdroid.features.settings.troubleshoot.TestBatteryOptimization import im.vector.app.features.settings.troubleshoot.TestBackgroundRestrictions
import im.vector.app.features.settings.troubleshoot.TestBatteryOptimization
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TestAccountSettings import im.vector.app.features.settings.troubleshoot.TestAccountSettings
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
import im.vector.app.features.settings.troubleshoot.TestNewEndpoint
import im.vector.app.features.settings.troubleshoot.TestNotification import im.vector.app.features.settings.troubleshoot.TestNotification
import im.vector.app.features.settings.troubleshoot.TestPushFromPushGateway
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
import im.vector.app.features.settings.troubleshoot.TestSystemSettings import im.vector.app.features.settings.troubleshoot.TestSystemSettings
import im.vector.app.features.settings.troubleshoot.TestTokenRegistration
import javax.inject.Inject import javax.inject.Inject
class NotificationTroubleshootTestManagerFactory @Inject constructor( class NotificationTroubleshootTestManagerFactory @Inject constructor(
private val testSystemSettings: TestSystemSettings, private val testSystemSettings: TestSystemSettings,
private val testAccountSettings: TestAccountSettings, private val testAccountSettings: TestAccountSettings,
private val testDeviceSettings: TestDeviceSettings, private val testDeviceSettings: TestDeviceSettings,
private val testNewEndpoint: TestNewEndpoint,
private val testTokenRegistration: TestTokenRegistration,
private val testPushFromPushGateway: TestPushFromPushGateway,
private val testPushRulesSettings: TestPushRulesSettings, private val testPushRulesSettings: TestPushRulesSettings,
private val testAutoStartBoot: TestAutoStartBoot, private val testAutoStartBoot: TestAutoStartBoot,
private val testBackgroundRestrictions: TestBackgroundRestrictions, private val testBackgroundRestrictions: TestBackgroundRestrictions,
@ -44,9 +51,15 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(
mgr.addTest(testAccountSettings) mgr.addTest(testAccountSettings)
mgr.addTest(testDeviceSettings) mgr.addTest(testDeviceSettings)
mgr.addTest(testPushRulesSettings) mgr.addTest(testPushRulesSettings)
if (UPHelper.distributorExists(fragment.requireContext())) {
mgr.addTest(testNewEndpoint)
mgr.addTest(testTokenRegistration)
mgr.addTest(testPushFromPushGateway)
} else {
mgr.addTest(testAutoStartBoot) mgr.addTest(testAutoStartBoot)
mgr.addTest(testBackgroundRestrictions) mgr.addTest(testBackgroundRestrictions)
mgr.addTest(testBatteryOptimization) mgr.addTest(testBatteryOptimization)
}
mgr.addTest(testNotification) mgr.addTest(testNotification)
return mgr return mgr
} }

View file

@ -3,18 +3,12 @@
package="im.vector.app"> package="im.vector.app">
<application> <application>
<receiver android:enabled="true" android:name=".push.fcm.EmbeddedDistrib" android:exported="false">
<!-- Firebase components -->
<meta-data
android:name="firebase_analytics_collection_deactivated"
android:value="true" />
<service android:name=".gplay.push.fcm.VectorFirebaseMessagingService">
<intent-filter> <intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" /> <action android:name="org.unifiedpush.android.distributor.REGISTER"/>
<action android:name="org.unifiedpush.android.distributor.UNREGISTER"/>
</intent-filter> </intent-filter>
</service> </receiver>
</application> </application>
</manifest> </manifest>

View file

@ -1,79 +0,0 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.gplay.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.messaging.FirebaseMessaging
import im.vector.app.R
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.startAddGoogleAccountIntent
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import im.vector.app.push.fcm.FcmHelper
import timber.log.Timber
import javax.inject.Inject
/*
* Test that app can successfully retrieve a token via firebase
*/
class TestFirebaseToken @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
status = TestStatus.RUNNING
try {
FirebaseMessaging.getInstance().token
.addOnCompleteListener(context) { task ->
if (!task.isSuccessful) {
// Can't find where this constant is (not documented -or deprecated in docs- and all obfuscated)
description = when (val errorMsg = task.exception?.localizedMessage ?: "Unknown") {
"SERVICE_NOT_AVAILABLE" -> {
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_service_not_available, errorMsg)
}
"TOO_MANY_REGISTRATIONS" -> {
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_too_many_registration, errorMsg)
}
"ACCOUNT_MISSING" -> {
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
override fun doFix() {
startAddGoogleAccountIntent(context, activityResultLauncher)
}
}
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
}
else -> {
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, errorMsg)
}
}
status = TestStatus.FAILED
} else {
task.result?.let { token ->
val tok = token.take(8) + "********************"
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, tok)
Timber.e("Retrieved FCM token success [$tok].")
// Ensure it is well store in our local storage
FcmHelper.storeFcmToken(context, token)
}
status = TestStatus.SUCCESS
}
}
} catch (e: Throwable) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed, e.localizedMessage)
status = TestStatus.FAILED
}
}
}

View file

@ -0,0 +1,30 @@
package im.vector.app.push.fcm
import android.content.Context
import org.unifiedpush.android.embedded_fcm_distributor.GetEndpointHandler
import org.unifiedpush.android.embedded_fcm_distributor.EmbeddedDistributorReceiver
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
val handlerFCM = object: GetEndpointHandler {
override fun getEndpoint(context: Context?, token: String, instance: String): String {
// Here token is the FCM Token, used by the gateway (sygnal)
return token
}
}
class EmbeddedDistrib: EmbeddedDistributorReceiver(handlerFCM)

View file

@ -15,99 +15,21 @@
*/ */
package im.vector.app.push.fcm package im.vector.app.push.fcm
import android.app.Activity
import android.content.Context import android.content.Context
import android.widget.Toast
import androidx.core.content.edit
import com.google.android.gms.common.ConnectionResult import com.google.android.gms.common.ConnectionResult
import com.google.android.gms.common.GoogleApiAvailability import com.google.android.gms.common.GoogleApiAvailability
import com.google.firebase.messaging.FirebaseMessaging
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.core.pushers.PushersManager
import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber
/** /**
* This class store the FCM token in SharedPrefs and ensure this token is retrieved. * This class store the FCM token in SharedPrefs and ensure this token is retrieved.
* It has an alter ego in the fdroid variant. * It has an alter ego in the fdroid variant.
*/ */
object FcmHelper { object FcmHelper {
private val PREFS_KEY_FCM_TOKEN = "FCM_TOKEN"
fun isPushSupported(): Boolean = true
/** /**
* Retrieves the FCM registration token. * Check the device to make sure it has the Google Play Services APK.
*
* @return the FCM token or null if not received from FCM
*/ */
fun getFcmToken(context: Context): String? { fun isPlayServicesAvailable(context: Context): Boolean {
return DefaultSharedPreferences.getInstance(context).getString(PREFS_KEY_FCM_TOKEN, null)
}
/**
* Store FCM token to the SharedPrefs
* TODO Store in realm
*
* @param context android context
* @param token the token to store
*/
fun storeFcmToken(context: Context,
token: String?) {
DefaultSharedPreferences.getInstance(context).edit {
putString(PREFS_KEY_FCM_TOKEN, token)
}
}
/**
* onNewToken may not be called on application upgrade, so ensure my shared pref is set
*
* @param activity the first launch Activity
*/
fun ensureFcmTokenIsRetrieved(activity: Activity, pushersManager: PushersManager, registerPusher: Boolean) {
// if (TextUtils.isEmpty(getFcmToken(activity))) {
// 'app should always check the device for a compatible Google Play services APK before accessing Google Play services features'
if (checkPlayServices(activity)) {
try {
FirebaseMessaging.getInstance().token
.addOnSuccessListener { token ->
storeFcmToken(activity, token)
if (registerPusher) {
pushersManager.registerPusherWithFcmKey(token)
}
}
.addOnFailureListener { e ->
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed")
}
} catch (e: Throwable) {
Timber.e(e, "## ensureFcmTokenIsRetrieved() : failed")
}
} else {
Toast.makeText(activity, R.string.no_valid_google_play_services_apk, Toast.LENGTH_SHORT).show()
Timber.e("No valid Google Play Services found. Cannot use FCM.")
}
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private fun checkPlayServices(activity: Activity): Boolean {
val apiAvailability = GoogleApiAvailability.getInstance() val apiAvailability = GoogleApiAvailability.getInstance()
val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity) val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
return resultCode == ConnectionResult.SUCCESS return resultCode == ConnectionResult.SUCCESS
} }
@Suppress("UNUSED_PARAMETER")
fun onEnterForeground(context: Context, activeSessionHolder: ActiveSessionHolder) {
// No op
}
@Suppress("UNUSED_PARAMETER")
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
// TODO FCM fallback
}
} }

View file

@ -16,27 +16,34 @@
package im.vector.app.push.fcm package im.vector.app.push.fcm
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.app.core.pushers.UPHelper
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TestAccountSettings import im.vector.app.features.settings.troubleshoot.TestAccountSettings
import im.vector.app.features.settings.troubleshoot.TestAutoStartBoot
import im.vector.app.features.settings.troubleshoot.TestBackgroundRestrictions
import im.vector.app.features.settings.troubleshoot.TestBatteryOptimization
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
import im.vector.app.features.settings.troubleshoot.TestNotification import im.vector.app.features.settings.troubleshoot.TestNotification
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
import im.vector.app.features.settings.troubleshoot.TestSystemSettings import im.vector.app.features.settings.troubleshoot.TestSystemSettings
import im.vector.app.gplay.features.settings.troubleshoot.TestFirebaseToken import im.vector.app.features.settings.troubleshoot.TestNewEndpoint
import im.vector.app.gplay.features.settings.troubleshoot.TestPlayServices import im.vector.app.gplay.features.settings.troubleshoot.TestPlayServices
import im.vector.app.gplay.features.settings.troubleshoot.TestPushFromPushGateway import im.vector.app.features.settings.troubleshoot.TestPushFromPushGateway
import im.vector.app.gplay.features.settings.troubleshoot.TestTokenRegistration import im.vector.app.features.settings.troubleshoot.TestTokenRegistration
import javax.inject.Inject import javax.inject.Inject
class NotificationTroubleshootTestManagerFactory @Inject constructor( class NotificationTroubleshootTestManagerFactory @Inject constructor(
private val testSystemSettings: TestSystemSettings, private val testSystemSettings: TestSystemSettings,
private val testAccountSettings: TestAccountSettings, private val testAccountSettings: TestAccountSettings,
private val testDeviceSettings: TestDeviceSettings, private val testDeviceSettings: TestDeviceSettings,
private val testBingRulesSettings: TestPushRulesSettings,
private val testPlayServices: TestPlayServices, private val testPlayServices: TestPlayServices,
private val testFirebaseToken: TestFirebaseToken, private val testNewEndpoint: TestNewEndpoint,
private val testTokenRegistration: TestTokenRegistration, private val testTokenRegistration: TestTokenRegistration,
private val testPushFromPushGateway: TestPushFromPushGateway, private val testPushFromPushGateway: TestPushFromPushGateway,
private val testPushRulesSettings: TestPushRulesSettings,
private val testAutoStartBoot: TestAutoStartBoot,
private val testBackgroundRestrictions: TestBackgroundRestrictions,
private val testBatteryOptimization: TestBatteryOptimization,
private val testNotification: TestNotification private val testNotification: TestNotification
) { ) {
@ -45,11 +52,17 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(
mgr.addTest(testSystemSettings) mgr.addTest(testSystemSettings)
mgr.addTest(testAccountSettings) mgr.addTest(testAccountSettings)
mgr.addTest(testDeviceSettings) mgr.addTest(testDeviceSettings)
mgr.addTest(testBingRulesSettings) mgr.addTest(testPushRulesSettings)
if (UPHelper.distributorExists(fragment.requireContext())) {
mgr.addTest(testPlayServices) mgr.addTest(testPlayServices)
mgr.addTest(testFirebaseToken) mgr.addTest(testNewEndpoint)
mgr.addTest(testTokenRegistration) mgr.addTest(testTokenRegistration)
mgr.addTest(testPushFromPushGateway) mgr.addTest(testPushFromPushGateway)
} else {
mgr.addTest(testAutoStartBoot)
mgr.addTest(testBackgroundRestrictions)
mgr.addTest(testBatteryOptimization)
}
mgr.addTest(testNotification) mgr.addTest(testNotification)
return mgr return mgr
} }

View file

@ -39,6 +39,14 @@
android:name="android.permission.WRITE_CALENDAR" android:name="android.permission.WRITE_CALENDAR"
tools:node="remove" /> tools:node="remove" />
<!-- if the user don't have FCM or any unifiedpush distributor -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--
Required for long polling account synchronisation in background.
If not present ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS intent action won't work
-->
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<!-- Jitsi SDK is now API23+ --> <!-- Jitsi SDK is now API23+ -->
<uses-sdk tools:overrideLibrary="org.jitsi.meet.sdk,com.oney.WebRTCModule,com.learnium.RNDeviceInfo,com.reactnativecommunity.asyncstorage,com.ocetnik.timer,com.calendarevents,com.reactnativecommunity.netinfo,com.kevinresol.react_native_default_preference,com.rnimmersive,com.corbt.keepawake,com.BV.LinearGradient,com.horcrux.svg" /> <uses-sdk tools:overrideLibrary="org.jitsi.meet.sdk,com.oney.WebRTCModule,com.learnium.RNDeviceInfo,com.reactnativecommunity.asyncstorage,com.ocetnik.timer,com.calendarevents,com.reactnativecommunity.netinfo,com.kevinresol.react_native_default_preference,com.rnimmersive,com.corbt.keepawake,com.BV.LinearGradient,com.horcrux.svg" />
@ -386,6 +394,30 @@
android:name="android.support.FILE_PROVIDER_PATHS" android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/sdk_provider_paths" /> android:resource="@xml/sdk_provider_paths" />
</provider> </provider>
<receiver android:exported="true" android:enabled="true" android:name=".core.pushers.VectorMessagingReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_REFUSED"/>
</intent-filter>
</receiver>
<!-- For the fallback with sync loop if the user don't have FCM or a UnifiedPush Distributor -->
<receiver android:name=".core.receiver.OnApplicationUpgradeOrRebootReceiver">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name=".core.receiver.AlarmSyncBroadcastReceiver"
android:enabled="true"
android:exported="false" />
</application> </application>
</manifest> </manifest>

View file

@ -59,7 +59,7 @@ import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.version.VersionProvider import im.vector.app.features.version.VersionProvider
import im.vector.app.push.fcm.FcmHelper import im.vector.app.core.pushers.StateHelper
import org.jitsi.meet.sdk.log.JitsiMeetDefaultLogHandler import org.jitsi.meet.sdk.log.JitsiMeetDefaultLogHandler
import org.matrix.android.sdk.api.Matrix import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixConfiguration import org.matrix.android.sdk.api.MatrixConfiguration
@ -171,7 +171,7 @@ class VectorApplication :
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME) @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun entersForeground() { fun entersForeground() {
Timber.i("App entered foreground") Timber.i("App entered foreground")
FcmHelper.onEnterForeground(appContext, activeSessionHolder) StateHelper.onEnterForeground(appContext, activeSessionHolder)
activeSessionHolder.getSafeActiveSession()?.also { activeSessionHolder.getSafeActiveSession()?.also {
it.stopAnyBackgroundSync() it.stopAnyBackgroundSync()
} }
@ -181,7 +181,7 @@ class VectorApplication :
fun entersBackground() { fun entersBackground() {
Timber.i("App entered background") // call persistInfo Timber.i("App entered background") // call persistInfo
notificationDrawerManager.persistInfo() notificationDrawerManager.persistInfo()
FcmHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder) StateHelper.onEnterBackground(appContext, vectorPreferences, activeSessionHolder)
} }
}) })
ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler) ProcessLifecycleOwner.get().lifecycle.addObserver(appStateHandler)

View file

@ -16,6 +16,7 @@
package im.vector.app.core.pushers package im.vector.app.core.pushers
import android.content.Context
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.AppNameProvider import im.vector.app.core.resources.AppNameProvider
@ -33,37 +34,45 @@ class PushersManager @Inject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val appNameProvider: AppNameProvider private val appNameProvider: AppNameProvider
) { ) {
suspend fun testPush(pushKey: String) { suspend fun testPush(context: Context) {
val currentSession = activeSessionHolder.getActiveSession() val currentSession = activeSessionHolder.getActiveSession()
currentSession.testPush( currentSession.testPush(
stringProvider.getString(R.string.pusher_http_url), UPHelper.getPushGateway(context)!!,
stringProvider.getString(R.string.pusher_app_id), getPusherAppId(context),
pushKey, UPHelper.getUpEndpoint(context)!!,
TEST_EVENT_ID TEST_EVENT_ID
) )
} }
fun registerPusherWithFcmKey(pushKey: String): UUID { fun registerPusher(context: Context, pushKey: String, gateway: String): UUID {
val currentSession = activeSessionHolder.getActiveSession() val currentSession = activeSessionHolder.getActiveSession()
val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode()) val profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(currentSession.myUserId.hashCode())
return currentSession.addHttpPusher( return currentSession.addHttpPusher(
pushKey, pushKey, // this is the UnifiedPush endpoint
stringProvider.getString(R.string.pusher_app_id), getPusherAppId(context),
profileTag, profileTag,
localeProvider.current().language, localeProvider.current().language,
appNameProvider.getAppName(), appNameProvider.getAppName(),
currentSession.sessionParams.deviceId ?: "MOBILE", currentSession.sessionParams.deviceId ?: "MOBILE",
stringProvider.getString(R.string.pusher_http_url), gateway,
append = false, append = false,
withEventIdOnly = true withEventIdOnly = true
) )
} }
suspend fun unregisterPusher(pushKey: String) { suspend fun unregisterPusher(context: Context, pushKey: String) {
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id)) currentSession.removeHttpPusher(pushKey, getPusherAppId(context))
}
private fun getPusherAppId(context: Context) : String {
return if (UPHelper.isEmbeddedDistributor(context)) {
stringProvider.getString(R.string.pusher_app_id)
} else {
stringProvider.getString(R.string.up_pusher_app_id)
}
} }
companion object { companion object {

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.pushers
import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.core.receiver.BackgroundSyncStarter
import im.vector.app.features.settings.VectorPreferences
object StateHelper {
fun onEnterForeground(context: Context, activeSessionHolder: ActiveSessionHolder) {
// try to stop all regardless of background mode
activeSessionHolder.getSafeActiveSession()?.stopAnyBackgroundSync()
AlarmSyncBroadcastReceiver.cancelAlarm(context)
}
fun onEnterBackground(context: Context, vectorPreferences: VectorPreferences, activeSessionHolder: ActiveSessionHolder) {
BackgroundSyncStarter.start(context, vectorPreferences, activeSessionHolder)
}
}

View file

@ -0,0 +1,180 @@
/*
* Copyright (c) 2021 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.pushers
import android.content.Context
import android.content.pm.PackageManager
import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences
import im.vector.app.push.fcm.FcmHelper
import org.unifiedpush.android.connector.Registration
import timber.log.Timber
import java.net.URI
/**
* This class store the UnifiedPush Endpoint in SharedPrefs and ensure this token is retrieved.
* It has an alter ego in the fdroid variant.
*/
object UPHelper {
private const val PREFS_UP_ENDPOINT = "UP_ENDPOINT"
private const val PREFS_PUSH_GATEWAY = "PUSH_GATEWAY"
/**
* Retrieves the UnifiedPush Endpoint.
*
* @return the UnifiedPush Endpoint or null if not received
*/
fun getUpEndpoint(context: Context): String? {
return DefaultSharedPreferences.getInstance(context).getString(PREFS_UP_ENDPOINT, null)
}
/**
* Store UnifiedPush Endpoint to the SharedPrefs
* TODO Store in realm
*
* @param context android context
* @param endpoint the endpoint to store
*/
fun storeUpEndpoint(context: Context,
endpoint: String?) {
DefaultSharedPreferences.getInstance(context).edit {
putString(PREFS_UP_ENDPOINT, endpoint)
}
}
/**
* Retrieves the Push Gateway.
*
* @return the Push Gateway or null if not defined
*/
fun getPushGateway(context: Context): String? {
return DefaultSharedPreferences.getInstance(context).getString(PREFS_PUSH_GATEWAY, null)
}
/**
* Store Push Gateway to the SharedPrefs
* TODO Store in realm
*
* @param context android context
* @param gateway the push gateway to store
*/
fun storePushGateway(context: Context,
gateway: String?) {
DefaultSharedPreferences.getInstance(context).edit {
putString(PREFS_PUSH_GATEWAY, gateway)
}
}
fun registerUnifiedPush(context: Context) {
val up = Registration()
if (up.getDistributor(context).isNotEmpty()) {
up.registerApp(context)
return
}
val distributors = up.getDistributors(context).toMutableList()
/**
* Check if it is the gplay flavour AND GServices are not available
*/
if (!FcmHelper.isPlayServicesAvailable(context)) {
distributors.remove(context.packageName)
}
when (distributors.size) {
0 -> {
/**
* Fallback with sync service
*/
}
1 -> {
up.saveDistributor(context, distributors.first())
up.registerApp(context)
}
else -> {
val builder: AlertDialog.Builder = MaterialAlertDialogBuilder(context)
builder.setTitle(context.getString(R.string.unifiedpush_getdistributors_dialog_title))
val distributorsArray = distributors.toTypedArray()
val distributorsNameArray = distributorsArray.map {
if (it == context.packageName) {
context.getString(R.string.unifiedpush_getdistributors_dialog_fcm_fallback)
} else {
try {
val ai = context.packageManager.getApplicationInfo(it, 0)
context.packageManager.getApplicationLabel(ai)
} catch (e: PackageManager.NameNotFoundException) {
it
} as String
}
}.toTypedArray()
builder.setItems(distributorsNameArray) { _, which ->
val distributor = distributorsArray[which]
up.saveDistributor(context, distributor)
Timber.i("Saving distributor: $distributor")
up.registerApp(context)
}
val dialog: AlertDialog = builder.create()
dialog.show()
}
}
}
fun unregister(context: Context) {
val up = Registration()
up.unregisterApp(context)
}
fun customOrDefaultGateway(context: Context, endpoint: String?): String {
// if we use the embedded distributor,
// register app_id type upfcm on sygnal
// the pushkey if FCM key
val up = Registration()
if (up.getDistributor(context) == context.packageName) {
return context.getString(R.string.pusher_http_url)
}
// else, unifiedpush, and pushkey is an endpoint
val default = context.getString(R.string.default_push_gateway_http_url)
endpoint?.let {
val uri = URI(it)
val custom = "${it.split(uri.rawPath)[0]}/_matrix/push/v1/notify"
Timber.i("Testing $custom")
/**
* TODO:
* if GET custom returns """{"unifiedpush":{"gateway":"matrix"}}"""
* return custom
*/
}
return default
}
fun hasEndpoint(context: Context): Boolean {
getUpEndpoint(context)?.let {
return true
}
return false
}
fun distributorExists(context: Context): Boolean {
val up = Registration()
return up.getDistributor(context).isNotEmpty()
}
fun isEmbeddedDistributor(context: Context) : Boolean {
val up = Registration()
return up.getDistributor(context) == context.packageName
}
}

View file

@ -17,43 +17,61 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.gplay.push.fcm package im.vector.app.core.pushers
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.widget.Toast
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.firebase.messaging.FirebaseMessagingService import com.squareup.moshi.Json
import com.google.firebase.messaging.RemoteMessage import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.vectorComponent import im.vector.app.core.extensions.vectorComponent
import im.vector.app.core.network.WifiDetector import im.vector.app.core.network.WifiDetector
import im.vector.app.core.pushers.PushersManager
import im.vector.app.features.badge.BadgeProxy import im.vector.app.features.badge.BadgeProxy
import im.vector.app.features.notifications.NotifiableEventResolver import im.vector.app.features.notifications.NotifiableEventResolver
import im.vector.app.features.notifications.NotifiableMessageEvent
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.features.notifications.SimpleNotifiableEvent import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.pushrules.Action
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.events.model.Event import org.unifiedpush.android.connector.MessagingReceiver
import org.unifiedpush.android.connector.MessagingReceiverHandler
import timber.log.Timber import timber.log.Timber
@JsonClass(generateAdapter = true)
data class UnifiedPushMessage(
val notification: Notification
)
@JsonClass(generateAdapter = true)
data class Notification(
@Json(name = "event_id") val eventId: String = "",
@Json(name = "room_id") val roomId: String = "",
var unread: Int = 0,
val counts: Counts = Counts()
)
data class Counts(
val unread: Int = 0
)
/** /**
* Class extending FirebaseMessagingService. * UnifiedPush handler.
*/ */
class VectorFirebaseMessagingService : FirebaseMessagingService() { val upHandler = object: MessagingReceiverHandler {
private lateinit var notificationDrawerManager: NotificationDrawerManager private lateinit var notificationDrawerManager: NotificationDrawerManager
private lateinit var notifiableEventResolver: NotifiableEventResolver private lateinit var notifiableEventResolver: NotifiableEventResolver
@ -69,9 +87,8 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
Handler(Looper.getMainLooper()) Handler(Looper.getMainLooper())
} }
override fun onCreate() { fun initVar(context: Context) {
super.onCreate() with(context.vectorComponent()) {
with(vectorComponent()) {
notificationDrawerManager = notificationDrawerManager() notificationDrawerManager = notificationDrawerManager()
notifiableEventResolver = notifiableEventResolver() notifiableEventResolver = notifiableEventResolver()
pusherManager = pusherManager() pusherManager = pusherManager()
@ -85,17 +102,34 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* Called when message is received. * Called when message is received.
* *
* @param message the message * @param message the message
* @param instance connection, for multi-account
*/ */
override fun onMessageReceived(message: RemoteMessage) { override fun onMessage(context: Context?, message: String, instance: String) {
initVar(context!!)
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.d("## onMessageReceived() %s", message.data.toString()) Timber.d("## onMessageReceived() %s", message)
}
Timber.d("## onMessage() received")
val moshi: Moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
lateinit var notification: Notification
if (UPHelper.isEmbeddedDistributor(context)) {
notification = moshi.adapter(Notification::class.java)
.fromJson(message) ?: return
} else {
val data = moshi.adapter(UnifiedPushMessage::class.java)
.fromJson(message) ?: return
notification = data.notification
notification.unread = notification.counts.unread
} }
Timber.d("## onMessageReceived() from FCM with priority %s", message.priority)
// Diagnostic Push // Diagnostic Push
if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) { if (notification.eventId == PushersManager.TEST_EVENT_ID) {
val intent = Intent(NotificationUtils.PUSH_ACTION) val intent = Intent(NotificationUtils.PUSH_ACTION)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent) LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
return return
} }
@ -109,7 +143,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
// we are in foreground, let the sync do the things? // we are in foreground, let the sync do the things?
Timber.d("PUSH received in a foreground state, ignore") Timber.d("PUSH received in a foreground state, ignore")
} else { } else {
onMessageReceivedInternal(message.data) onMessageReceivedInternal(context, notification)
} }
} }
} }
@ -120,59 +154,71 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* when the InstanceID token is initially generated, so this is where * when the InstanceID token is initially generated, so this is where
* you retrieve the token. * you retrieve the token.
*/ */
override fun onNewToken(refreshedToken: String) { override fun onNewEndpoint(context: Context?, endpoint: String, instance: String) {
Timber.i("onNewToken: FCM Token has been updated") initVar(context!!)
FcmHelper.storeFcmToken(this, refreshedToken) Timber.i("onNewEndpoint: adding $endpoint")
if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) { if (vectorPreferences.areNotificationEnabledForDevice() && activeSessionHolder.hasActiveSession()) {
pusherManager.registerPusherWithFcmKey(refreshedToken) val gateway = UPHelper.customOrDefaultGateway(context, endpoint)
if (UPHelper.getUpEndpoint(context) != endpoint
|| UPHelper.getPushGateway(context) != gateway) {
UPHelper.storePushGateway(context, gateway)
UPHelper.storeUpEndpoint(context, endpoint)
pusherManager.registerPusher(context, endpoint, gateway)
} else {
Timber.i("onNewEndpoint: skipped")
} }
} }
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_DISABLED
vectorPreferences.setFdroidSyncBackgroundMode(mode)
}
/** override fun onRegistrationFailed(context: Context?, instance: String) {
* Called when the FCM server deletes pending messages. This may be due to: Toast.makeText(context, "Push service registration failed", Toast.LENGTH_SHORT).show()
* - Too many messages stored on the FCM server. }
* This can occur when an app's servers send a bunch of non-collapsible messages to FCM servers while the device is offline.
* - The device hasn't connected in a long time and the app server has recently (within the last 4 weeks) override fun onRegistrationRefused(context: Context?, instance: String) {
* sent a message to the app on that device. Toast.makeText(context, "Push service registration refused by server", Toast.LENGTH_LONG).show()
* }
* It is recommended that the app do a full sync with the app server after receiving this call.
*/ override fun onUnregistered(context: Context?, instance: String) {
override fun onDeletedMessages() { Timber.d("Unifiedpush: Unregistered")
Timber.v("## onDeletedMessages()") initVar(context!!)
val mode = BackgroundSyncMode.FDROID_BACKGROUND_SYNC_MODE_FOR_BATTERY
vectorPreferences.setFdroidSyncBackgroundMode(mode)
runBlocking {
try {
pusherManager.unregisterPusher(context, UPHelper.getUpEndpoint(context)!!)
} catch (e: Exception) {
Timber.d("Probably unregistering a non existant pusher")
}
}
} }
/** /**
* Internal receive method * Internal receive method
* *
* @param data Data map containing message data as key/value pairs. * @param notification Notification containing message data.
* For Set of keys use data.keySet().
*/ */
private fun onMessageReceivedInternal(data: Map<String, String>) { private fun onMessageReceivedInternal(context: Context, notification: Notification) {
try { try {
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) { if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.d("## onMessageReceivedInternal() : $data") Timber.d("## onMessageReceivedInternal() : $notification")
} else {
Timber.d("## onMessageReceivedInternal() : $data")
} }
// update the badge counter // update the badge counter
val unreadCount = data["unread"]?.let { Integer.parseInt(it) } ?: 0 BadgeProxy.updateBadgeCount(context, notification.unread)
BadgeProxy.updateBadgeCount(applicationContext, unreadCount)
val session = activeSessionHolder.getSafeActiveSession() val session = activeSessionHolder.getSafeActiveSession()
if (session == null) { if (session == null) {
Timber.w("## Can't sync from push, no current session") Timber.w("## Can't sync from push, no current session")
} else { } else {
val eventId = data["event_id"] if (isEventAlreadyKnown(notification.eventId, notification.roomId)) {
val roomId = data["room_id"]
if (isEventAlreadyKnown(eventId, roomId)) {
Timber.d("Ignoring push, event already known") Timber.d("Ignoring push, event already known")
} else { } else {
// Try to get the Event content faster // Try to get the Event content faster
Timber.d("Requesting event in fast lane") Timber.d("Requesting event in fast lane")
getEventFastLane(session, roomId, eventId) getEventFastLane(session, notification.roomId, notification.eventId)
Timber.d("Requesting background sync") Timber.d("Requesting background sync")
session.requireBackgroundSync() session.requireBackgroundSync()
@ -227,87 +273,6 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
} }
return false return false
} }
private fun handleNotificationWithoutSyncingMode(data: Map<String, String>, session: Session?) {
if (session == null) {
Timber.e("## handleNotificationWithoutSyncingMode cannot find session")
return
}
// The Matrix event ID of the event being notified about.
// This is required if the notification is about a particular Matrix event.
// It may be omitted for notifications that only contain updated badge counts.
// This ID can and should be used to detect duplicate notification requests.
val eventId = data["event_id"] ?: return // Just ignore
val eventType = data["type"]
if (eventType == null) {
// Just add a generic unknown event
val simpleNotifiableEvent = SimpleNotifiableEvent(
session.myUserId,
eventId,
null,
true, // It's an issue in this case, all event will bing even if expected to be silent.
title = getString(R.string.notification_unknown_new_event),
description = "",
type = null,
timestamp = System.currentTimeMillis(),
soundName = Action.ACTION_OBJECT_VALUE_VALUE_DEFAULT,
isPushGatewayEvent = true
)
notificationDrawerManager.onNotifiableEventReceived(simpleNotifiableEvent)
notificationDrawerManager.refreshNotificationDrawer()
} else {
val event = parseEvent(data) ?: return
val notifiableEvent = notifiableEventResolver.resolveEvent(event, session)
if (notifiableEvent == null) {
Timber.e("Unsupported notifiable event $eventId")
if (BuildConfig.LOW_PRIVACY_LOG_ENABLE) {
Timber.e("--> $event")
}
} else {
if (notifiableEvent is NotifiableMessageEvent) {
if (notifiableEvent.senderName.isNullOrEmpty()) {
notifiableEvent.senderName = data["sender_display_name"] ?: data["sender"] ?: ""
}
if (notifiableEvent.roomName.isNullOrEmpty()) {
notifiableEvent.roomName = findRoomNameBestEffort(data, session) ?: ""
}
}
notifiableEvent.isPushGatewayEvent = true
notifiableEvent.matrixID = session.myUserId
notificationDrawerManager.onNotifiableEventReceived(notifiableEvent)
notificationDrawerManager.refreshNotificationDrawer()
}
}
}
private fun findRoomNameBestEffort(data: Map<String, String>, session: Session?): String? {
var roomName: String? = data["room_name"]
val roomId = data["room_id"]
if (null == roomName && null != roomId) {
// Try to get the room name from our store
roomName = session?.getRoom(roomId)?.roomSummary()?.displayName
}
return roomName
}
/**
* Try to create an event from the FCM data
*
* @param data the FCM data
* @return the event or null if required data are missing
*/
private fun parseEvent(data: Map<String, String>?): Event? {
return Event(
eventId = data?.get("event_id") ?: return null,
senderId = data["sender"],
roomId = data["room_id"] ?: return null,
type = data["type"] ?: return null,
originServerTs = System.currentTimeMillis()
)
}
} }
class VectorMessagingReceiver : MessagingReceiver(upHandler)

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid.receiver package im.vector.app.core.receiver
import android.app.AlarmManager import android.app.AlarmManager
import android.app.PendingIntent import android.app.PendingIntent

View file

@ -14,11 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid package im.vector.app.core.receiver
import android.content.Context import android.content.Context
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber import timber.log.Timber

View file

@ -15,14 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid.receiver package im.vector.app.core.receiver
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import im.vector.app.core.di.HasVectorInjector import im.vector.app.core.di.HasVectorInjector
import im.vector.app.core.extensions.vectorComponent import im.vector.app.core.extensions.vectorComponent
import im.vector.app.fdroid.BackgroundSyncStarter
import timber.log.Timber import timber.log.Timber
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() { class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {

View file

@ -29,8 +29,8 @@ import im.vector.app.features.call.lookup.CallProtocolsChecker
import im.vector.app.features.call.lookup.CallUserMapper import im.vector.app.features.call.lookup.CallUserMapper
import im.vector.app.features.call.utils.EglUtils import im.vector.app.features.call.utils.EglUtils
import im.vector.app.features.call.vectorCallService import im.vector.app.features.call.vectorCallService
import im.vector.app.core.pushers.UPHelper
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
@ -262,7 +262,7 @@ class WebRtcCallManager @Inject constructor(
audioManager.setMode(CallAudioManager.Mode.DEFAULT) audioManager.setMode(CallAudioManager.Mode.DEFAULT)
// did we start background sync? so we should stop it // did we start background sync? so we should stop it
if (isInBackground) { if (isInBackground) {
if (FcmHelper.isPushSupported()) { if (UPHelper.hasEndpoint(context)) {
currentSession?.stopAnyBackgroundSync() currentSession?.stopAnyBackgroundSync()
} else { } else {
// for fdroid we should not stop, it should continue syncing // for fdroid we should not stop, it should continue syncing
@ -367,7 +367,7 @@ class WebRtcCallManager @Inject constructor(
// and thus won't be able to received events. For example if the call is // and thus won't be able to received events. For example if the call is
// accepted on an other session this device will continue ringing // accepted on an other session this device will continue ringing
if (isInBackground) { if (isInBackground) {
if (FcmHelper.isPushSupported()) { if (UPHelper.hasEndpoint(context)) {
// only for push version as fdroid version is already doing it? // only for push version as fdroid version is already doing it?
currentSession?.startAutomaticBackgroundSync(30, 0) currentSession?.startAutomaticBackgroundSync(30, 0)
} else { } else {

View file

@ -68,7 +68,7 @@ import im.vector.app.features.spaces.share.ShareSpaceBottomSheet
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.features.workers.signout.ServerBackupStatusViewState
import im.vector.app.push.fcm.FcmHelper import im.vector.app.core.pushers.UPHelper
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService import org.matrix.android.sdk.api.session.initsync.InitialSyncProgressService
@ -165,7 +165,7 @@ class HomeActivity :
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) UPHelper.registerUnifiedPush(this)
sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java) sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java)
views.drawerLayout.addDrawerListener(drawerListener) views.drawerLayout.addDrawerListener(drawerListener)
if (isFirstCreation()) { if (isFirstCreation()) {

View file

@ -21,6 +21,7 @@ import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas import android.graphics.Canvas
import android.os.Build import android.os.Build
import android.text.TextUtils
import android.view.View import android.view.View
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
@ -29,6 +30,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.getAllChildFragments import im.vector.app.core.extensions.getAllChildFragments
import im.vector.app.core.extensions.toOnOff import im.vector.app.core.extensions.toOnOff
import im.vector.app.core.pushers.UPHelper
import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorLocale
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.devtools.GossipingEventsSerializer import im.vector.app.features.settings.devtools.GossipingEventsSerializer
@ -55,6 +57,7 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
import java.net.HttpURLConnection import java.net.HttpURLConnection
import java.net.URL
import java.util.Locale import java.util.Locale
import java.util.zip.GZIPOutputStream import java.util.zip.GZIPOutputStream
import javax.inject.Inject import javax.inject.Inject
@ -276,6 +279,13 @@ class BugReporter @Inject constructor(
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context)) .addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
.addFormDataPart("server_version", serverVersion) .addFormDataPart("server_version", serverVersion)
// UnifiedPush
// Only include the UP endpoint base url to exclude private user tokens in the path or parameters
builder.addFormDataPart("unifiedpush_endpoint", strippedUrl(UPHelper.getUpEndpoint(context)).toString())
.addFormDataPart("unifiedpush_gateway", UPHelper.getPushGateway(context))
.addFormDataPart("unifiedpush_distributor_exists", UPHelper.distributorExists(context).toString())
.addFormDataPart("unifiedpush_is_embedded_distributor", UPHelper.isEmbeddedDistributor(context).toString())
val buildNumber = context.getString(R.string.build_number) val buildNumber = context.getString(R.string.build_number)
if (buildNumber.isNotEmpty() && buildNumber != "0") { if (buildNumber.isNotEmpty() && buildNumber != "0") {
builder.addFormDataPart("build_number", buildNumber) builder.addFormDataPart("build_number", buildNumber)
@ -454,6 +464,16 @@ class BugReporter @Inject constructor(
} }
} }
fun strippedUrl(url: String?): String? {
if (TextUtils.isEmpty(url)) return null
return try {
val parsed = URL(url)
"${parsed.protocol}://${parsed.host}"
} catch (e: Exception) {
"Malformed url"
}
}
/** /**
* Send a bug report either with email or with Vector. * Send a bug report either with email or with Vector.
*/ */

View file

@ -37,16 +37,17 @@ import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.isIgnoringBatteryOptimizations
import im.vector.app.core.utils.requestDisablingBatteryOptimization import im.vector.app.core.utils.requestDisablingBatteryOptimization
import im.vector.app.features.notifications.NotificationUtils import im.vector.app.features.notifications.NotificationUtils
import im.vector.app.core.pushers.UPHelper
import im.vector.app.features.settings.BackgroundSyncMode import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.BackgroundSyncModeChooserDialog import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsBaseFragment import im.vector.app.features.settings.VectorSettingsBaseFragment
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.pushrules.RuleIds import org.matrix.android.sdk.api.pushrules.RuleIds
import org.matrix.android.sdk.api.pushrules.RuleKind import org.matrix.android.sdk.api.pushrules.RuleKind
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
// Referenced in vector_settings_preferences_root.xml // Referenced in vector_settings_preferences_root.xml
@ -150,7 +151,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
} }
findPreference<VectorPreferenceCategory>(VectorPreferences.SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY)?.let { findPreference<VectorPreferenceCategory>(VectorPreferences.SETTINGS_BACKGROUND_SYNC_PREFERENCE_KEY)?.let {
it.isVisible = !FcmHelper.isPushSupported() it.isVisible = !UPHelper.hasEndpoint(requireContext())
} }
findPreference<VectorEditTextPreference>(VectorPreferences.SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY)?.let { findPreference<VectorEditTextPreference>(VectorPreferences.SETTINGS_SET_SYNC_TIMEOUT_PREFERENCE_KEY)?.let {
@ -254,7 +255,7 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
private fun refreshPref() { private fun refreshPref() {
// This pref may have change from troubleshoot pref fragment // This pref may have change from troubleshoot pref fragment
if (!FcmHelper.isPushSupported()) { if (!UPHelper.hasEndpoint(requireContext())) {
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_START_ON_BOOT_PREFERENCE_KEY) findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_START_ON_BOOT_PREFERENCE_KEY)
?.isChecked = vectorPreferences.autoStartOnBoot() ?.isChecked = vectorPreferences.autoStartOnBoot()
} }
@ -294,13 +295,22 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
private fun updateEnabledForDevice(preference: Preference?) { private fun updateEnabledForDevice(preference: Preference?) {
val switchPref = preference as SwitchPreference val switchPref = preference as SwitchPreference
if (switchPref.isChecked) { if (switchPref.isChecked) {
FcmHelper.getFcmToken(requireContext())?.let { UPHelper.registerUnifiedPush(requireContext())
pushManager.registerPusherWithFcmKey(it)
}
} else { } else {
FcmHelper.getFcmToken(requireContext())?.let { UPHelper.getUpEndpoint(requireContext())?.let {
lifecycleScope.launch { lifecycleScope.launch {
runCatching { pushManager.unregisterPusher(it) } runCatching {
try {
pushManager.unregisterPusher(requireContext(), it)
} catch (e: Exception) {
Timber.d("Probably unregistering a non existant pusher")
}
try {
UPHelper.unregister(requireContext())
} catch (e: Exception) {
Timber.d("Probably unregistering to a non-saved distributor")
}
}
.fold( .fold(
{ session.refreshPushers() }, { session.refreshPushers() },
{ {

View file

@ -13,14 +13,13 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject import javax.inject.Inject
/** /**

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2018 New Vector Ltd * Copyright (c) 2021 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent import android.content.Intent
import android.net.ConnectivityManager import android.net.ConnectivityManager
@ -23,7 +23,6 @@ import androidx.core.content.getSystemService
import androidx.core.net.ConnectivityManagerCompat import androidx.core.net.ConnectivityManagerCompat
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject import javax.inject.Inject
class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity, class TestBackgroundRestrictions @Inject constructor(private val context: AppCompatActivity,

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.fdroid.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@ -22,7 +22,6 @@ import im.vector.app.R
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.isIgnoringBatteryOptimizations import im.vector.app.core.utils.isIgnoringBatteryOptimizations
import im.vector.app.core.utils.requestDisablingBatteryOptimization import im.vector.app.core.utils.requestDisablingBatteryOptimization
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import javax.inject.Inject import javax.inject.Inject
class TestBatteryOptimization @Inject constructor( class TestBatteryOptimization @Inject constructor(

View file

@ -0,0 +1,56 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.troubleshoot
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R
import im.vector.app.core.pushers.UPHelper
import im.vector.app.core.resources.StringProvider
import javax.inject.Inject
/*
* Test that app can successfully retrieve a new endpoint
*/
class TestNewEndpoint @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider
) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
status = TestStatus.RUNNING
val endpoint = UPHelper.getUpEndpoint(context)
if (UPHelper.isEmbeddedDistributor(context)) {
if (!endpoint.isNullOrEmpty()) {
status = TestStatus.SUCCESS
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_success, endpoint)
} else {
status = TestStatus.FAILED
description = stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed)
}
} else {
if (!endpoint.isNullOrEmpty()) {
status = TestStatus.SUCCESS
description = stringProvider.getString(R.string.settings_troubleshoot_test_endpoint_success, endpoint)
} else {
status = TestStatus.FAILED
description = stringProvider.getString(R.string.settings_troubleshoot_test_endpoint_failed)
}
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright 2020 New Vector Ltd * Copyright (c) 2021 New Vector Ltd
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.gplay.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@ -24,8 +24,7 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.core.pushers.UPHelper
import im.vector.app.push.fcm.FcmHelper
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -48,12 +47,12 @@ class TestPushFromPushGateway @Inject constructor(private val context: AppCompat
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
pushReceived = false pushReceived = false
val fcmToken = FcmHelper.getFcmToken(context) ?: run { UPHelper.getUpEndpoint(context) ?: run {
status = TestStatus.FAILED status = TestStatus.FAILED
return return
} }
action = activeSessionHolder.getActiveSession().coroutineScope.launch { action = activeSessionHolder.getActiveSession().coroutineScope.launch {
val result = runCatching { pushersManager.testPush(fcmToken) } val result = runCatching { pushersManager.testPush(context) }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
status = result status = result

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package im.vector.app.gplay.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
@ -26,8 +26,7 @@ import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.core.pushers.UPHelper
import im.vector.app.push.fcm.FcmHelper
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -37,11 +36,15 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val pushersManager: PushersManager, private val pushersManager: PushersManager,
private val activeSessionHolder: ActiveSessionHolder) private val activeSessionHolder: ActiveSessionHolder)
: TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) { : TroubleshootTest(R.string.settings_troubleshoot_test_pusher_registration_title) {
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) { override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
// Check if we have a registered pusher for this token // Check if we have a registered pusher for this token
val fcmToken = FcmHelper.getFcmToken(context) ?: run { val pushToken = UPHelper.getUpEndpoint(context) ?: run {
status = TestStatus.FAILED
return
}
val pushGateway = UPHelper.getPushGateway(context) ?: run {
status = TestStatus.FAILED status = TestStatus.FAILED
return return
} }
@ -50,14 +53,14 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
return return
} }
val pushers = session.getPushers().filter { val pushers = session.getPushers().filter {
it.pushKey == fcmToken && it.state == PusherState.REGISTERED it.pushKey == pushToken && it.state == PusherState.REGISTERED
} }
if (pushers.isEmpty()) { if (pushers.isEmpty()) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_failed, description = stringProvider.getString(R.string.settings_troubleshoot_test_pusher_registration_failed,
stringProvider.getString(R.string.sas_error_unknown)) stringProvider.getString(R.string.sas_error_unknown))
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) { quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_token_registration_quick_fix) {
override fun doFix() { override fun doFix() {
val workId = pushersManager.registerPusherWithFcmKey(fcmToken) val workId = pushersManager.registerPusher(context, pushToken, pushGateway)
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo -> WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
if (workInfo != null) { if (workInfo != null) {
if (workInfo.state == WorkInfo.State.SUCCEEDED) { if (workInfo.state == WorkInfo.State.SUCCEEDED) {
@ -72,7 +75,7 @@ class TestTokenRegistration @Inject constructor(private val context: AppCompatAc
status = TestStatus.FAILED status = TestStatus.FAILED
} else { } else {
description = stringProvider.getString(R.string.settings_troubleshoot_test_token_registration_success) description = stringProvider.getString(R.string.settings_troubleshoot_test_pusher_registration_success)
status = TestStatus.SUCCESS status = TestStatus.SUCCESS
} }
} }

View file

@ -14,7 +14,11 @@
--> -->
<!-- Note: pusher_http_url should have path '/_matrix/push/v1/notify' --> <!-- Note: pusher_http_url should have path '/_matrix/push/v1/notify' -->
<!-- It is the push gateway for FCM embedded distributor -->
<string name="pusher_http_url" translatable="false">https://matrix.org/_matrix/push/v1/notify</string> <string name="pusher_http_url" translatable="false">https://matrix.org/_matrix/push/v1/notify</string>
<!-- Note: default_push_gateway_http_url should have path '/_matrix/push/v1/notify' -->
<!-- It is the push gateway for UnifiedPush -->
<string name="default_push_gateway_http_url" translatable="false">https://up.schildi.chat/_matrix/push/v1/notify</string>
<!-- Note: pusher_app_id cannot exceed 64 chars --> <!-- Note: pusher_app_id cannot exceed 64 chars -->
<string name="pusher_app_id" translatable="false">im.vector.app.android</string> <string name="pusher_app_id" translatable="false">im.vector.app.android</string>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="up_pusher_app_id" translatable="false">de.spiritcroc.riotx.up</string>
</resources>

View file

@ -1131,6 +1131,16 @@
<string name="template_settings_troubleshoot_test_play_services_failed">${app_name} uses Google Play Services to deliver push messages but it doesnt seem to be configured correctly:\n%1$s</string> <string name="template_settings_troubleshoot_test_play_services_failed">${app_name} uses Google Play Services to deliver push messages but it doesnt seem to be configured correctly:\n%1$s</string>
<string name="settings_troubleshoot_test_play_services_quickfix">Fix Play Services</string> <string name="settings_troubleshoot_test_play_services_quickfix">Fix Play Services</string>
<!-- Unifiedpush Endpoint -->
<string name="settings_troubleshoot_test_endpoint_title">Endpoint/FCM</string>
<string name="settings_troubleshoot_test_endpoint_success">Endpoint successfully retrieved:\n%1$s</string>
<string name="settings_troubleshoot_test_endpoint_failed">Failed to retrieved Endpoint.</string>
<!-- UnifiedPush Distributor Picker Dialog -->
<string name="unifiedpush_getdistributors_dialog_title">Choose a distributor</string>
<string name="unifiedpush_getdistributors_dialog_fcm_fallback">FCM Fallback</string>
<!-- FCM Token, not used anymore -->
<string name="settings_troubleshoot_test_fcm_title">Firebase Token</string> <string name="settings_troubleshoot_test_fcm_title">Firebase Token</string>
<string name="settings_troubleshoot_test_fcm_success">FCM token successfully retrieved:\n%1$s</string> <string name="settings_troubleshoot_test_fcm_success">FCM token successfully retrieved:\n%1$s</string>
<string name="settings_troubleshoot_test_fcm_failed">Failed to retrieved FCM token:\n%1$s</string> <string name="settings_troubleshoot_test_fcm_failed">Failed to retrieved FCM token:\n%1$s</string>
@ -1142,6 +1152,12 @@
<string name="template_settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]\nThis error is out of control of ${app_name}. There is no Google account on the phone. Please open the account manager and add a Google account.</string> <string name="template_settings_troubleshoot_test_fcm_failed_account_missing">[%1$s]\nThis error is out of control of ${app_name}. There is no Google account on the phone. Please open the account manager and add a Google account.</string>
<string name="settings_troubleshoot_test_fcm_failed_account_missing_quick_fix">Add Account</string> <string name="settings_troubleshoot_test_fcm_failed_account_missing_quick_fix">Add Account</string>
<!-- Pusher registration -->
<string name="settings_troubleshoot_test_pusher_registration_title">Pusher Registration</string>
<string name="settings_troubleshoot_test_pusher_registration_success">Pusher successfully registered to HomeServer.</string>
<string name="settings_troubleshoot_test_pusher_registration_failed">Failed to register pusher to HomeServer:\n%1$s</string>
<!-- FCM token, not used -->
<string name="settings_troubleshoot_test_token_registration_title">Token Registration</string> <string name="settings_troubleshoot_test_token_registration_title">Token Registration</string>
<string name="settings_troubleshoot_test_token_registration_success">FCM token successfully registered to homeserver.</string> <string name="settings_troubleshoot_test_token_registration_success">FCM token successfully registered to homeserver.</string>
<string name="settings_troubleshoot_test_token_registration_failed">Failed to register FCM token to homeserver:\n%1$s</string> <string name="settings_troubleshoot_test_token_registration_failed">Failed to register FCM token to homeserver:\n%1$s</string>