Skip to content

Commit

Permalink
Merge pull request #4052 from vector-im/feature/adm/email_notificatio…
Browse files Browse the repository at this point in the history
…n_toggle

Add email notification toggle
  • Loading branch information
bmarty authored Sep 24, 2021
2 parents 9a30da1 + 4482cbd commit 045e4bb
Show file tree
Hide file tree
Showing 22 changed files with 325 additions and 41 deletions.
1 change: 1 addition & 0 deletions changelog.d/2243.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Adds email notification registration to Settings
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ data class Pusher(
val data: PusherData,

val state: PusherState
)
) {
companion object {

const val KIND_EMAIL = "email"
const val KIND_HTTP = "http"
const val APP_ID_EMAIL = "m.email"
}
}

enum class PusherState {
UNREGISTERED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,12 @@ interface PushersService {

/**
* Add a new HTTP pusher.
* Note that only `http` kind is supported by the SDK for now.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
*
* @param pushkey This is a unique identifier for this pusher. The value you should use for
* this is the routing or destination address information for the notification,
* for example, the APNS token for APNS or the Registration ID for GCM. If your
* notification client has no such concept, use any unique identifier. Max length, 512 chars.
* If the kind is "email", this is the email address to send notifications to.
* @param appId the application id
* This is a reverse-DNS style identifier for the application. It is recommended
* that this end with the platform, such that different platform versions get
Expand Down Expand Up @@ -64,6 +62,30 @@ interface PushersService {
append: Boolean,
withEventIdOnly: Boolean): UUID

/**
* Add a new Email pusher.
* Ref: https://matrix.org/docs/spec/client_server/latest#post-matrix-client-r0-pushers-set
*
* @param email The email address to send notifications to.
* @param lang The preferred language for receiving notifications (e.g. "en" or "en-US").
* @param emailBranding The branding placeholder to include in the email communications.
* @param appDisplayName A human readable string that will allow the user to identify what application owns this pusher.
* @param deviceDisplayName A human readable string that will allow the user to identify what device owns this pusher.
* @param append If true, the homeserver should add another pusher with the given pushkey and App ID in addition
* to any others with different user IDs. Otherwise, the homeserver must remove any other pushers
* with the same App ID and pushkey for different users. Typically We always want to append for
* email pushers since we don't want to stop other accounts notifying to the same email address.
* @return A work request uuid. Can be used to listen to the status
* (LiveData<WorkInfo> status = workManager.getWorkInfoByIdLiveData(<UUID>))
* @throws [InvalidParameterException] if a parameter is not correct
*/
fun addEmailPusher(email: String,
lang: String,
emailBranding: String,
appDisplayName: String,
deviceDisplayName: String,
append: Boolean = true): UUID

/**
* Directly ask the push gateway to send a push to this device
* If successful, the push gateway has accepted the request. In this case, the app should receive a Push with the provided eventId.
Expand All @@ -80,10 +102,23 @@ interface PushersService {
eventId: String)

/**
* Remove the http pusher
* Remove a registered pusher
* @param pusher the pusher to remove, can be http or email
*/
suspend fun removePusher(pusher: Pusher)

/**
* Remove a Http pusher by its pushkey and appId
* @see addHttpPusher
*/
suspend fun removeHttpPusher(pushkey: String, appId: String)

/**
* Remove an Email pusher
* @see addEmailPusher
*/
suspend fun removeEmailPusher(email: String)

/**
* Get the current pushers, as a LiveData
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import org.matrix.android.sdk.internal.session.integrationmanager.IntegrationMan
import org.matrix.android.sdk.internal.session.media.MediaModule
import org.matrix.android.sdk.internal.session.openid.OpenIdModule
import org.matrix.android.sdk.internal.session.profile.ProfileModule
import org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker
import org.matrix.android.sdk.internal.session.pushers.AddPusherWorker
import org.matrix.android.sdk.internal.session.pushers.PushersModule
import org.matrix.android.sdk.internal.session.room.RoomModule
import org.matrix.android.sdk.internal.session.room.relation.SendRelationWorker
Expand Down Expand Up @@ -127,7 +127,7 @@ internal interface SessionComponent {

fun inject(worker: SyncWorker)

fun inject(worker: AddHttpPusherWorker)
fun inject(worker: AddPusherWorker)

fun inject(worker: SendVerificationMessageWorker)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import javax.inject.Inject

internal class AddHttpPusherWorker(context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<AddHttpPusherWorker.Params>(context, params, Params::class.java) {
internal class AddPusherWorker(context: Context, params: WorkerParameters)
: SessionSafeCoroutineWorker<AddPusherWorker.Params>(context, params, Params::class.java) {

@JsonClass(generateAdapter = true)
internal data class Params(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,45 @@ internal class DefaultPushersService @Inject constructor(
deviceDisplayName: String,
url: String,
append: Boolean,
withEventIdOnly: Boolean)
: UUID {
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
if (pushkey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'")
withEventIdOnly: Boolean
) = addPusher(
JsonPusher(
pushKey = pushkey,
kind = Pusher.KIND_HTTP,
appId = appId,
profileTag = profileTag,
lang = lang,
appDisplayName = appDisplayName,
deviceDisplayName = deviceDisplayName,
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
append = append
)
)

val pusher = JsonPusher(
pushKey = pushkey,
kind = "http",
appId = appId,
appDisplayName = appDisplayName,
deviceDisplayName = deviceDisplayName,
profileTag = profileTag,
lang = lang,
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
append = append)
override fun addEmailPusher(email: String,
lang: String,
emailBranding: String,
appDisplayName: String,
deviceDisplayName: String,
append: Boolean
) = addPusher(
JsonPusher(
pushKey = email,
kind = Pusher.KIND_EMAIL,
appId = Pusher.APP_ID_EMAIL,
profileTag = "",
lang = lang,
appDisplayName = appDisplayName,
deviceDisplayName = deviceDisplayName,
data = JsonPusherData(brand = emailBranding),
append = append
)
)

val params = AddHttpPusherWorker.Params(sessionId, pusher)

val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddHttpPusherWorker>()
private fun addPusher(pusher: JsonPusher): UUID {
pusher.validateParameters()
val params = AddPusherWorker.Params(sessionId, pusher)
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
.setConstraints(WorkManagerProvider.workConstraints)
.setInputData(WorkerParamsFactory.toData(params))
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
Expand All @@ -95,8 +113,27 @@ internal class DefaultPushersService @Inject constructor(
return request.id
}

private fun JsonPusher.validateParameters() {
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem
if (pushKey.length > 512) throw InvalidParameterException("pushkey should not exceed 512 chars")
if (appId.length > 64) throw InvalidParameterException("appId should not exceed 64 chars")
data?.url?.let { url -> if ("/_matrix/push/v1/notify" !in url) throw InvalidParameterException("url should contain '/_matrix/push/v1/notify'") }
}

override suspend fun removePusher(pusher: Pusher) {
removePusher(pusher.pushKey, pusher.appId)
}

override suspend fun removeHttpPusher(pushkey: String, appId: String) {
val params = RemovePusherTask.Params(pushkey, appId)
removePusher(pushkey, appId)
}

override suspend fun removeEmailPusher(email: String) {
removePusher(pushKey = email, Pusher.APP_ID_EMAIL)
}

private suspend fun removePusher(pushKey: String, pushAppId: String) {
val params = RemovePusherTask.Params(pushKey, pushAppId)
removePusherTask.execute(params)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,8 @@ internal data class JsonPusherData(
* Currently the only format available is 'event_id_only'.
*/
@Json(name = "format")
val format: String? = null
val format: String? = null,

@Json(name = "brand")
val brand: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,15 @@ import im.vector.app.features.themes.ThemeUtils
/**
* Set a text in the TextView, or set visibility to GONE if the text is null
*/
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true) {
fun TextView.setTextOrHide(newText: CharSequence?, hideWhenBlank: Boolean = true, vararg relatedViews: View = emptyArray()) {
if (newText == null
|| (newText.isBlank() && hideWhenBlank)) {
isVisible = false
relatedViews.forEach { it.isVisible = false }
} else {
this.text = newText
isVisible = true
relatedViews.forEach { it.isVisible = true }
}
}

Expand Down
17 changes: 17 additions & 0 deletions vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ class PushersManager @Inject constructor(
)
}

fun registerEmailForPush(email: String) {
val currentSession = activeSessionHolder.getActiveSession()
val appName = appNameProvider.getAppName()
currentSession.addEmailPusher(
email = email,
lang = localeProvider.current().language,
emailBranding = appName,
appDisplayName = appName,
deviceDisplayName = currentSession.sessionParams.deviceId ?: "MOBILE"
)
}

suspend fun unregisterEmailPusher(email: String) {
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.removeEmailPusher(email)
}

suspend fun unregisterPusher(pushKey: String) {
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
// notifications
const val SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY = "SETTINGS_ENABLE_ALL_NOTIF_PREFERENCE_KEY"
const val SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY = "SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY"
const val SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY = "SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY"

// public static final String SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY = "SETTINGS_TURN_SCREEN_ON_PREFERENCE_KEY";
const val SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY = "SETTINGS_SYSTEM_CALL_NOTIFICATION_PREFERENCE_KEY"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import im.vector.app.databinding.ActivityVectorSettingsBinding
import im.vector.app.features.discovery.DiscoverySettingsFragment
import im.vector.app.features.settings.devices.VectorSettingsDevicesFragment
import im.vector.app.features.settings.notifications.VectorSettingsNotificationPreferenceFragment
import im.vector.app.features.settings.threepids.ThreePidsSettingsFragment

import org.matrix.android.sdk.api.failure.GlobalError
import org.matrix.android.sdk.api.session.Session
Expand Down Expand Up @@ -136,6 +137,10 @@ class VectorSettingsActivity : VectorBaseActivity<ActivityVectorSettingsBinding>
return keyToHighlight
}

override fun navigateToEmailAndPhoneNumbers() {
navigateTo(ThreePidsSettingsFragment::class.java)
}

override fun handleInvalidToken(globalError: GlobalError.InvalidToken) {
if (ignoreInvalidTokenError) {
Timber.w("Ignoring invalid token global error")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,6 @@ interface VectorSettingsFragmentInteractionListener {
fun requestHighlightPreferenceKeyOnResume(key: String?)

fun requestedKeyToHighlight(): String?

fun navigateToEmailAndPhoneNumbers()
}
Loading

0 comments on commit 045e4bb

Please sign in to comment.