Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnifiedPush #6228

Merged
merged 65 commits into from
Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
04b297b
Add UnifiedPush support
p1gp1g Feb 25, 2022
848adc4
Add UnifiedPush settings
p1gp1g Feb 27, 2022
f00257f
Add UnifiedPush troubleshoot
p1gp1g Feb 28, 2022
1069f77
Lint
p1gp1g Feb 28, 2022
f774f46
Check custom unifiedpush gateway
p1gp1g Feb 28, 2022
7eca405
Update UnifiedPush libs
p1gp1g Feb 28, 2022
ee7fccf
Fix compilation issues after rebase
bmarty Jun 1, 2022
d88d279
Sort alphabetically.
bmarty Jun 1, 2022
4281175
Move `ALLOW_EXTERNAL_UNIFIEDPUSH_DISTRIB` to `defaultConfig` and docu…
bmarty Jun 1, 2022
09a918b
Cleanup
bmarty Jun 1, 2022
96acb61
Add `unifiedpush` to the project dict.
bmarty Jun 1, 2022
674e3a7
Make `UnifiedPushHelper` a regular class and inject the context in th…
bmarty Jun 1, 2022
5e10449
Use the RawService to do network request.
bmarty Jun 1, 2022
f1e57d2
Use `.orEmpty()` instead of `?: ""`
bmarty Jun 1, 2022
9216d8b
Small cleanup
bmarty Jun 1, 2022
74de9c8
Small rework
bmarty Jun 1, 2022
12d969b
Prefer using `toString()`
bmarty Jun 1, 2022
cc80bf9
Use plurals
bmarty Jun 1, 2022
399e95a
`setOnDismissListener` should cover all the cases.
bmarty Jun 1, 2022
ddf6a69
Small cleanup
bmarty Jun 1, 2022
ad8cb22
We need an Activity to display the dialog
bmarty Jun 1, 2022
ff6aa11
VectorPreferences can be injected.
bmarty Jun 1, 2022
77601f6
typo
bmarty Jun 1, 2022
4018113
Better usage of Timber.
bmarty Jun 1, 2022
fb8408c
Small cleanup
bmarty Jun 1, 2022
76bc6a5
Move the setting at the beginning of the section
bmarty Jun 1, 2022
3f6b529
Add summary to the notification with the current value.
bmarty Jun 1, 2022
bdb2d29
Catch 404
bmarty Jun 1, 2022
bbbeb4b
Extract storage to its own class.
bmarty Jun 1, 2022
45768c5
Small cleanup
bmarty Jun 1, 2022
cdfb728
Clarify the data classes for the Json parsing
bmarty Jun 1, 2022
6cc2a36
Add explicit Json names
bmarty Jun 2, 2022
35e0a0a
Detekt
bmarty Jun 2, 2022
80d42f0
Remove unused methods / clarify API
bmarty Jun 2, 2022
18b4906
Change BuildConfig field to a VectorFeature.
bmarty Jun 2, 2022
110c17e
No need to have a mutable list here.
bmarty Jun 2, 2022
420144d
Fix back issue on the dialog.
bmarty Jun 2, 2022
fb7df5b
Ignore if no change is done.
bmarty Jun 2, 2022
a5378d6
avoid runBlocking
bmarty Jun 2, 2022
fc66e5f
Ignore if no change is done - bugfix
bmarty Jun 2, 2022
639c570
Create extension to get app names
bmarty Jun 2, 2022
8708719
shorter code
bmarty Jun 2, 2022
a139756
Fix an issue with empty endpoint. It can happen if the endpoint is ma…
bmarty Jun 2, 2022
905934b
Rename method for clarity
bmarty Jun 8, 2022
3c72ee6
Unregister UP when signing out
bmarty Jun 8, 2022
5846ad5
Inject constructor of `BackgroundSyncStarter` and `FcmHelper`
bmarty Jun 8, 2022
3560ac9
Create a Kotlin Config object in vector-config module, for easy confi…
bmarty Jun 9, 2022
2b8d1dd
Write documentation about UnifiedPush.
bmarty Jun 9, 2022
c43122a
Explain why the data are different when received from Firebase and fr…
bmarty Jun 9, 2022
2f2ee1b
Format project (only modified files, there are other fomatting issues).
bmarty Jun 9, 2022
4a54674
This module now have Kotlin code.
bmarty Jun 9, 2022
33911c8
Update documentation after @p1gp1g review.
bmarty Jun 9, 2022
b1e062a
Fix small issue on the settings.
bmarty Jun 9, 2022
c7d021e
Extract parser to its own file and add unit test.
bmarty Jun 13, 2022
55bac9b
Give time to the tests to perform
bmarty Jun 13, 2022
d1e2a90
Add test for the notification settings.
bmarty Jun 13, 2022
56e6f51
Rename setting key for clarity
bmarty Jun 13, 2022
279b9b5
Be lenient on the Json format for received data in a Push.
bmarty Jun 14, 2022
2174b11
Move companion at the bottom of the class.
bmarty Jun 14, 2022
0147eb4
Fix test
bmarty Jun 14, 2022
4968198
I have done some manual test, this should be fine.
bmarty Jun 14, 2022
d978d0a
Delete obsolete comment.
bmarty Jun 16, 2022
90f16c6
Remove unused dep.
bmarty Jun 16, 2022
e0fe91f
Remove alias.
bmarty Jun 16, 2022
7bd2184
Rename fun.
bmarty Jun 16, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/dictionaries/bmarty.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions changelog.d/3448.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use UnifiedPush and allows user to have push without FCM.
1 change: 1 addition & 0 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ ext.libs = [
],
squareup : [
'moshi' : "com.squareup.moshi:moshi:$moshi",
'moshiKt' : "com.squareup.moshi:moshi-kotlin:$moshi",
'moshiKotlin' : "com.squareup.moshi:moshi-kotlin-codegen:$moshi",
'retrofit' : "com.squareup.retrofit2:retrofit:$retrofit",
'retrofitMoshi' : "com.squareup.retrofit2:converter-moshi:$retrofit"
Expand Down
1 change: 1 addition & 0 deletions dependencies_groups.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ ext.groups = [
'com.github.jetradarmobile',
'com.github.MatrixFrog',
'com.github.tapadoo',
'com.github.UnifiedPush',
'com.github.vector-im',
'com.github.yalantis',
'com.github.Zhuinden',
Expand Down
58 changes: 58 additions & 0 deletions docs/unifiedpush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# UnifiedPush

<!--- TOC -->

* [Introduction](#introduction)
* [Configuration in Element-Android and their forks](#configuration-in-element-android-and-their-forks)
* [Enabling and disabling the feature](#enabling-and-disabling-the-feature)
* [Override the configuration at runtime](#override-the-configuration-at-runtime)
* [Enabling the feature](#enabling-the-feature)
* [Disabling the feature](#disabling-the-feature)
* [Useful links](#useful-links)

<!--- END -->

## Introduction

The recently started UnifiedPush project is an Android protocol and library for apps to be able to receive distributor-agnostic push notifications.

The *F-Droid* and *Gplay* flavors of Element Android support UnifiedPush, so the user can use any distributor installed on their devices. This would make it possible to have push notifications without depending on Google services or libraries. Currently, the main distributors are [ntfy](https://ntfy.sh) which does not require any setup (like manual registration) to use the public server and [NextPush](https://github.com/UP-NextPush/android), available as a nextcloud application.

The *Gplay* variant uses a UnifiedPush library which basically embed a FCM distributor built into the application (so a user doesn't need to do anything other than install the app to get FCM notifications). This variant uses Google Services to receive notifications if the user has not installed any distributor.

The *F-Droid* variant does not use this library to avoid any proprietary blob. It will use a polling service if the user has not installed any distributor.

In all cases, if there are other distributors available, the user will have to opt-in to one of them in the preferences.

## Configuration in Element-Android and their forks

### Enabling and disabling the feature

Allowing the user to use an alternative distributor can be changed in [Config](../vector-config/src/main/java/im/vector/app/config/Config.kt). The flag is named `ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS`. Default value is `true`.

#### Override the configuration at runtime

On debug version, it is possible to override this configuration at runtime, using the `Feature` screen. The Feature is named `Allow external UnifiedPush distributors`.

#### Enabling the feature

This is the default behavior of Element Android.

If `ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS` is set to true, it allows any available external UnifiedPush distributor to be chosen by the user.
- For Gplay variant it means that FCM will be used by default, but user can choose another UnifiedPush distributor;
- For F-Droid variant, it means that background polling will be used by default, but user can choose another UnifiedPush distributor.
- On the UI, the setting to choose an alternative distributor will be visible to the user, and some tests in the notification troubleshoot screen will shown.
- For F-Droid, if the user has chosen a distributor, the settings to configure the background polling will be hidden.

#### Disabling the feature

If `ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS` is set to false, it prevents the usage of external UnifiedPush distributors.
- For Gplay variant it means that only FCM will be used;
- For F-Droid variant, it means that only background polling will be used.
- On the UI, the setting to choose an alternative distributor will be hidden to the user, and some tests in the notification troubleshoot screen will be hidden.

### Useful links

- UnifiedPush official website: [https://unifiedpush.org/](https://unifiedpush.org/)
- List of available distributors can be retrieved here: [https://unifiedpush.org/users/distributors/](https://unifiedpush.org/users/distributors/)
- UnifiedPush project discussion can occurs here: [#unifiedpush:matrix.org](https://matrix.to/#/#unifiedpush:matrix.org)
5 changes: 5 additions & 0 deletions vector-config/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
plugins {
id 'com.android.library'
id 'kotlin-android'
}

android {
Expand All @@ -13,4 +14,8 @@ android {
sourceCompatibility versions.sourceCompat
targetCompatibility versions.targetCompat
}

kotlinOptions {
jvmTarget = "11"
}
}
39 changes: 39 additions & 0 deletions vector-config/src/main/java/im/vector/app/config/Config.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright (c) 2022 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.config

/**
* Set of flags to configure the application.
*/
object Config {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯 is the plan for this to become our source of truth?

(with some values being pulled in via the BuildConfig)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is what I have in mind. At the end they will be no BuildConfig fields anymore. (Also note that this class does not have access to vector module's BuildConfig class.

So we have:

  • Config here, in this dedicated super simple configuration module;
  • VectorFeatures to be able to override values at runtime.

/**
* Flag to allow external UnifiedPush distributors to be chosen by the user.
*
* Set to true to allow any available external UnifiedPush distributor to be chosen by the user.
* - For Gplay variant it means that FCM will be used by default, but user can choose another UnifiedPush distributor;
* - For F-Droid variant, it means that background polling will be used by default, but user can choose another UnifiedPush distributor.
*
* Set to false to prevent usage of external UnifiedPush distributors.
* - For Gplay variant it means that only FCM will be used;
* - For F-Droid variant, it means that only background polling will be available to the user.
*
* *Note*: When the app is already installed on users' phone:
* - Changing the value from `false` to `true` will let the user be able to select an external UnifiedPush distributor;
* - Changing the value from `true` to `false` will force the app to return to the background sync / Firebase Push.
*/
const val ALLOW_EXTERNAL_UNIFIED_PUSH_DISTRIBUTORS = true
}
4 changes: 4 additions & 0 deletions vector-config/src/main/res/values/config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
-->

<!-- 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>
<!-- 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://matrix.gateway.unifiedpush.org/_matrix/push/v1/notify</string>
<!-- Note: pusher_app_id cannot exceed 64 chars -->
<string name="pusher_app_id" translatable="false">im.vector.app.android</string>

Expand Down
7 changes: 5 additions & 2 deletions vector/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ dependencies {
implementation "com.gabrielittner.threetenbp:lazythreetenbp:0.10.0"

implementation libs.squareup.moshi
implementation libs.squareup.moshiKt
kapt libs.squareup.moshiKotlin

// Lifecycle
Expand Down Expand Up @@ -462,8 +463,10 @@ dependencies {
// Analytics
implementation 'com.posthog.android:posthog:1.1.2'

// gplay flavor only
gplayImplementation('com.google.firebase:firebase-messaging:23.0.0') {
// UnifiedPush
implementation 'com.github.UnifiedPush:android-connector:2.0.0'
// UnifiedPush gplay flavor only
gplayImplementation('com.github.UnifiedPush:android-embedded_fcm_distributor:2.0.0') {
exclude group: 'com.google.firebase', module: 'firebase-core'
exclude group: 'com.google.firebase', module: 'firebase-analytics'
exclude group: 'com.google.firebase', module: 'firebase-measurement-connector'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package im.vector.app.ui.robot.settings

import androidx.test.espresso.Espresso.pressBack
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
import com.adevinta.android.barista.interaction.BaristaClickInteractions.clickOn
import im.vector.app.R
import im.vector.app.espresso.tools.clickOnPreference
Expand All @@ -41,7 +42,18 @@ class SettingsNotificationsRobot {
clickOn(R.string.settings_call_notifications_preferences)
pressBack()
*/
// Email notification. No Emails are configured so we show go to the screen to add email
clickOnPreference(R.string.settings_notification_emails_no_emails)
assertDisplayed(R.string.settings_emails_and_phone_numbers_title)
pressBack()

// Display the notification method change dialog
clickOnPreference(R.string.settings_notification_method)
pressBack()

clickOnPreference(R.string.settings_notification_troubleshoot)
// Give time for the tests to perform
Thread.sleep(12_000)
pressBack()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class DebugFeaturesStateFactory @Inject constructor(
key = DebugFeatureKeys.onboardingCombinedLogin,
factory = VectorFeatures::isOnboardingCombinedLoginEnabled
),
createBooleanFeature(
label = "Allow external UnifiedPush distributors",
key = DebugFeatureKeys.allowExternalUnifiedPushDistributors,
factory = VectorFeatures::allowExternalUnifiedPushDistributors
),
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ class DebugVectorFeatures(
override fun isOnboardingCombinedLoginEnabled(): Boolean = read(DebugFeatureKeys.onboardingCombinedLogin)
?: vectorFeatures.isOnboardingCombinedLoginEnabled()

override fun allowExternalUnifiedPushDistributors(): Boolean = read(DebugFeatureKeys.allowExternalUnifiedPushDistributors)
?: vectorFeatures.allowExternalUnifiedPushDistributors()

override fun isScreenSharingEnabled(): Boolean = read(DebugFeatureKeys.screenSharing)
?: vectorFeatures.isScreenSharingEnabled()

Expand Down Expand Up @@ -117,6 +120,7 @@ object DebugFeatureKeys {
val onboardingPersonalize = booleanPreferencesKey("onboarding-personalize")
val onboardingCombinedRegister = booleanPreferencesKey("onboarding-combined-register")
val onboardingCombinedLogin = booleanPreferencesKey("onboarding-combined-login")
val allowExternalUnifiedPushDistributors = booleanPreferencesKey("allow-external-unified-push-distributors")
val liveLocationSharing = booleanPreferencesKey("live-location-sharing")
val screenSharing = booleanPreferencesKey("screen-sharing")
}
16 changes: 15 additions & 1 deletion vector/src/fdroid/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,25 @@
android:enabled="true"
android:exported="false" />

<receiver
android:name=".fdroid.receiver.KeepInternalDistributor"
android:enabled="true"
android:exported="false">
<intent-filter>
<!--
This action is checked to track installed and uninstalled distributors.
We declare it to keep the background sync as an internal
unifiedpush distributor.
-->
<action android:name="org.unifiedpush.android.distributor.REGISTER" />
</intent-filter>
</receiver>

<service
android:name=".fdroid.service.GuardService"
android:exported="false"
tools:ignore="Instantiatable" />

</application>

</manifest>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ import im.vector.app.fdroid.receiver.AlarmSyncBroadcastReceiver
import im.vector.app.features.settings.BackgroundSyncMode
import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber
import javax.inject.Inject

object BackgroundSyncStarter {
fun start(
context: Context,
vectorPreferences: VectorPreferences,
activeSessionHolder: ActiveSessionHolder,
clock: Clock
) {
class BackgroundSyncStarter @Inject constructor(
private val context: Context,
private val vectorPreferences: VectorPreferences,
private val clock: Clock
) {
fun start(activeSessionHolder: ActiveSessionHolder) {
if (vectorPreferences.areNotificationEnabledForDevice()) {
val activeSession = activeSessionHolder.getSafeActiveSession() ?: return
when (vectorPreferences.getFdroidSyncBackgroundMode()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2022 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.fdroid.receiver

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

/**
* UnifiedPush lib tracks an action to check installed and uninstalled distributors.
* We declare it to keep the background sync as an internal unifiedpush distributor.
* This class is used to declare this action.
*/
class KeepInternalDistributor : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,20 @@ package im.vector.app.fdroid.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import im.vector.app.core.extensions.singletonEntryPoint
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.fdroid.BackgroundSyncStarter
import timber.log.Timber
import javax.inject.Inject

@AndroidEntryPoint
class OnApplicationUpgradeOrRebootReceiver : BroadcastReceiver() {

@Inject lateinit var activeSessionHolder: ActiveSessionHolder
@Inject lateinit var backgroundSyncStarter: BackgroundSyncStarter

override fun onReceive(context: Context, intent: Intent) {
Timber.v("## onReceive() ${intent.action}")
val singletonEntryPoint = context.singletonEntryPoint()
BackgroundSyncStarter.start(
context,
singletonEntryPoint.vectorPreferences(),
singletonEntryPoint.activeSessionHolder(),
singletonEntryPoint.clock()
)
backgroundSyncStarter.start(activeSessionHolder)
}
}
Loading