mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 20:29:10 +03:00
Merge pull request #4052 from vector-im/feature/adm/email_notification_toggle
Add email notification toggle
This commit is contained in:
commit
045e4bbf76
22 changed files with 325 additions and 41 deletions
1
changelog.d/2243.feature
Normal file
1
changelog.d/2243.feature
Normal file
|
@ -0,0 +1 @@
|
|||
Adds email notification registration to Settings
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
@ -127,7 +127,7 @@ internal interface SessionComponent {
|
|||
|
||||
fun inject(worker: SyncWorker)
|
||||
|
||||
fun inject(worker: AddHttpPusherWorker)
|
||||
fun inject(worker: AddPusherWorker)
|
||||
|
||||
fun inject(worker: SendVerificationMessageWorker)
|
||||
|
||||
|
|
|
@ -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(
|
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
@ -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")
|
||||
|
|
|
@ -20,4 +20,6 @@ interface VectorSettingsFragmentInteractionListener {
|
|||
fun requestHighlightPreferenceKeyOnResume(key: String?)
|
||||
|
||||
fun requestedKeyToHighlight(): String?
|
||||
|
||||
fun navigateToEmailAndPhoneNumbers()
|
||||
}
|
||||
|
|
|
@ -23,7 +23,10 @@ import android.media.RingtoneManager
|
|||
import android.net.Uri
|
||||
import android.os.Parcelable
|
||||
import android.widget.Toast
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.distinctUntilChanged
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.map
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreference
|
||||
import im.vector.app.R
|
||||
|
@ -43,10 +46,14 @@ import im.vector.app.features.settings.VectorPreferences
|
|||
import im.vector.app.features.settings.VectorSettingsBaseFragment
|
||||
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
|
||||
import im.vector.app.push.fcm.FcmHelper
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.pushrules.RuleIds
|
||||
import org.matrix.android.sdk.api.pushrules.RuleKind
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
import javax.inject.Inject
|
||||
|
||||
// Referenced in vector_settings_preferences_root.xml
|
||||
|
@ -116,11 +123,51 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
bindEmailNotifications()
|
||||
refreshBackgroundSyncPrefs()
|
||||
|
||||
handleSystemPreference()
|
||||
}
|
||||
|
||||
private fun bindEmailNotifications() {
|
||||
val initialEmails = session.getEmailsWithPushInformation()
|
||||
bindEmailNotificationCategory(initialEmails)
|
||||
session.getEmailsWithPushInformationLive().observe(this) { emails ->
|
||||
if (initialEmails != emails) {
|
||||
bindEmailNotificationCategory(emails)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindEmailNotificationCategory(emails: List<Pair<ThreePid.Email, Boolean>>) {
|
||||
findPreference<VectorPreferenceCategory>(VectorPreferences.SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY)?.let { category ->
|
||||
category.removeAll()
|
||||
if (emails.isEmpty()) {
|
||||
val vectorPreference = VectorPreference(requireContext())
|
||||
vectorPreference.title = resources.getString(R.string.settings_notification_emails_no_emails)
|
||||
category.addPreference(vectorPreference)
|
||||
vectorPreference.setOnPreferenceClickListener {
|
||||
interactionListener?.navigateToEmailAndPhoneNumbers()
|
||||
true
|
||||
}
|
||||
} else {
|
||||
emails.forEach { (emailPid, isEnabled) ->
|
||||
val pref = VectorSwitchPreference(requireContext())
|
||||
pref.title = resources.getString(R.string.settings_notification_emails_enable_for_email, emailPid.email)
|
||||
pref.isChecked = isEnabled
|
||||
pref.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked ->
|
||||
if (isChecked) {
|
||||
pushManager.registerEmailForPush(emailPid.email)
|
||||
} else {
|
||||
pushManager.unregisterEmailPusher(emailPid.email)
|
||||
}
|
||||
}
|
||||
category.addPreference(pref)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val batteryStartForActivityResult = registerStartForActivityResult {
|
||||
// Noop
|
||||
}
|
||||
|
@ -343,3 +390,43 @@ class VectorSettingsNotificationPreferenceFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun SwitchPreference.setTransactionalSwitchChangeListener(scope: CoroutineScope, transaction: suspend (Boolean) -> Unit) {
|
||||
setOnPreferenceChangeListener { switchPreference, isChecked ->
|
||||
require(switchPreference is SwitchPreference)
|
||||
val originalState = switchPreference.isChecked
|
||||
scope.launch {
|
||||
try {
|
||||
transaction(isChecked as Boolean)
|
||||
} catch (failure: Throwable) {
|
||||
switchPreference.isChecked = originalState
|
||||
Toast.makeText(switchPreference.context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the current users 3pid emails and pairs them with their enabled state.
|
||||
* If no pusher is available for a given email we can infer that push is not registered for the email.
|
||||
* @return a list of ThreePid emails paired with the email notification enabled state. true if email notifications are enabled, false if not.
|
||||
* @see ThreePid.Email
|
||||
*/
|
||||
private fun Session.getEmailsWithPushInformation(): List<Pair<ThreePid.Email, Boolean>> {
|
||||
val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL }
|
||||
return getThreePids()
|
||||
.filterIsInstance<ThreePid.Email>()
|
||||
.map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } }
|
||||
}
|
||||
|
||||
private fun Session.getEmailsWithPushInformationLive(): LiveData<List<Pair<ThreePid.Email, Boolean>>> {
|
||||
return getThreePidsLive(refreshData = false)
|
||||
.distinctUntilChanged()
|
||||
.map { threePids ->
|
||||
val emailPushers = getPushers().filter { it.kind == Pusher.KIND_EMAIL }
|
||||
threePids
|
||||
.filterIsInstance<ThreePid.Email>()
|
||||
.map { it to emailPushers.any { pusher -> pusher.pushKey == it.email } }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ class PushGateWayController @Inject constructor(
|
|||
private val stringProvider: StringProvider
|
||||
) : TypedEpoxyController<PushGatewayViewState>() {
|
||||
|
||||
var interactionListener: PushGatewayItemInteractions? = null
|
||||
|
||||
override fun buildModels(data: PushGatewayViewState?) {
|
||||
val host = this
|
||||
data?.pushGateways?.invoke()?.let { pushers ->
|
||||
|
@ -39,6 +41,9 @@ class PushGateWayController @Inject constructor(
|
|||
pushGatewayItem {
|
||||
id("${it.pushKey}_${it.appId}")
|
||||
pusher(it)
|
||||
host.interactionListener?.let {
|
||||
interactions(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
package im.vector.app.features.settings.push
|
||||
|
||||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
|
||||
sealed class PushGatewayAction : VectorViewModelAction {
|
||||
object Refresh : PushGatewayAction()
|
||||
data class RemovePusher(val pusher: Pusher) : PushGatewayAction()
|
||||
}
|
||||
|
|
|
@ -16,12 +16,14 @@
|
|||
|
||||
package im.vector.app.features.settings.push
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import com.airbnb.epoxy.EpoxyModelWithHolder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.VectorEpoxyHolder
|
||||
import im.vector.app.core.extensions.setTextOrHide
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_pushgateway)
|
||||
|
@ -30,33 +32,45 @@ abstract class PushGatewayItem : EpoxyModelWithHolder<PushGatewayItem.Holder>()
|
|||
@EpoxyAttribute
|
||||
lateinit var pusher: Pusher
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var interactions: PushGatewayItemInteractions
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
holder.kind.text = when (pusher.kind) {
|
||||
// TODO Create const
|
||||
"http" -> "Http Pusher"
|
||||
"mail" -> "Email Pusher"
|
||||
else -> pusher.kind
|
||||
Pusher.KIND_HTTP -> "Http Pusher"
|
||||
Pusher.KIND_EMAIL -> "Email Pusher"
|
||||
else -> pusher.kind
|
||||
}
|
||||
|
||||
holder.appId.text = pusher.appId
|
||||
holder.pushKey.text = pusher.pushKey
|
||||
holder.appName.text = pusher.appDisplayName
|
||||
holder.url.text = pusher.data.url
|
||||
holder.format.text = pusher.data.format
|
||||
holder.url.setTextOrHide(pusher.data.url, hideWhenBlank = true, holder.urlTitle)
|
||||
holder.format.setTextOrHide(pusher.data.format, hideWhenBlank = true, holder.formatTitle)
|
||||
holder.deviceName.text = pusher.deviceDisplayName
|
||||
holder.removeButton.setOnClickListener {
|
||||
interactions.onRemovePushTapped(pusher)
|
||||
}
|
||||
}
|
||||
|
||||
class Holder : VectorEpoxyHolder() {
|
||||
val kind by bind<TextView>(R.id.pushGatewayKind)
|
||||
val pushKey by bind<TextView>(R.id.pushGatewayKeyValue)
|
||||
val deviceName by bind<TextView>(R.id.pushGatewayDeviceNameValue)
|
||||
val formatTitle by bind<View>(R.id.pushGatewayFormat)
|
||||
val format by bind<TextView>(R.id.pushGatewayFormatValue)
|
||||
val urlTitle by bind<View>(R.id.pushGatewayURL)
|
||||
val url by bind<TextView>(R.id.pushGatewayURLValue)
|
||||
val appName by bind<TextView>(R.id.pushGatewayAppNameValue)
|
||||
val appId by bind<TextView>(R.id.pushGatewayAppIdValue)
|
||||
val removeButton by bind<View>(R.id.pushGatewayDeleteButton)
|
||||
}
|
||||
}
|
||||
|
||||
interface PushGatewayItemInteractions {
|
||||
fun onRemovePushTapped(pusher: Pusher)
|
||||
}
|
||||
|
||||
//
|
||||
// abstract class ReactionInfoSimpleItem : EpoxyModelWithHolder<ReactionInfoSimpleItem.Holder>() {
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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.features.settings.push
|
||||
|
||||
import im.vector.app.core.platform.VectorViewEvents
|
||||
|
||||
sealed class PushGatewayViewEvents : VectorViewEvents {
|
||||
data class RemovePusherFailed(val cause: Throwable): PushGatewayViewEvents()
|
||||
}
|
|
@ -24,11 +24,14 @@ import android.view.ViewGroup
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.configureWith
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentGenericRecyclerBinding
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -64,7 +67,21 @@ class PushGatewaysFragment @Inject constructor(
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
epoxyController.interactionListener = object : PushGatewayItemInteractions {
|
||||
override fun onRemovePushTapped(pusher: Pusher) = viewModel.handle(PushGatewayAction.RemovePusher(pusher))
|
||||
}
|
||||
views.genericRecyclerView.configureWith(epoxyController, dividerDrawable = R.drawable.divider_horizontal)
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
is PushGatewayViewEvents.RemovePusherFailed -> {
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.dialog_title_error)
|
||||
.setMessage(errorFormatter.toHumanReadable(it.cause))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.settings.push
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.airbnb.mvrx.Async
|
||||
import com.airbnb.mvrx.FragmentViewModelContext
|
||||
import com.airbnb.mvrx.MvRxState
|
||||
|
@ -26,8 +27,8 @@ import dagger.assisted.Assisted
|
|||
import dagger.assisted.AssistedInject
|
||||
import dagger.assisted.AssistedFactory
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||
import org.matrix.android.sdk.rx.RxSession
|
||||
|
@ -38,7 +39,7 @@ data class PushGatewayViewState(
|
|||
|
||||
class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState: PushGatewayViewState,
|
||||
private val session: Session)
|
||||
: VectorViewModel<PushGatewayViewState, PushGatewayAction, EmptyViewEvents>(initialState) {
|
||||
: VectorViewModel<PushGatewayViewState, PushGatewayAction, PushGatewayViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
|
@ -70,10 +71,21 @@ class PushGatewaysViewModel @AssistedInject constructor(@Assisted initialState:
|
|||
|
||||
override fun handle(action: PushGatewayAction) {
|
||||
when (action) {
|
||||
is PushGatewayAction.Refresh -> handleRefresh()
|
||||
is PushGatewayAction.Refresh -> handleRefresh()
|
||||
is PushGatewayAction.RemovePusher -> removePusher(action.pusher)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun removePusher(pusher: Pusher) {
|
||||
viewModelScope.launch {
|
||||
kotlin.runCatching {
|
||||
session.removePusher(pusher)
|
||||
}.onFailure {
|
||||
_viewEvents.post(PushGatewayViewEvents.RemovePusherFailed(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRefresh() {
|
||||
session.refreshPushers()
|
||||
}
|
||||
|
|
|
@ -120,7 +120,6 @@
|
|||
android:text="@string/push_gateway_item_format"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pushGatewayFormatValue"
|
||||
style="@style/Widget.Vector.TextView.Body"
|
||||
|
@ -130,5 +129,11 @@
|
|||
android:textStyle=""
|
||||
tools:text="event_id_only" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/pushGatewayDeleteButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/remove" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
|
@ -1112,6 +1112,12 @@
|
|||
<string name="settings_notification_advanced">Advanced Notification Settings</string>
|
||||
<string name="settings_notification_by_event">Notification importance by event</string>
|
||||
|
||||
<string name="settings_notification_emails_category">Email notification</string>
|
||||
<string name="settings_notification_emails_no_emails">To receive email with notification, please associate an email to your Matrix account</string>
|
||||
|
||||
<!-- The variable is a single email address, eg Enable email notifications for example@matrix.org -->
|
||||
<string name="settings_notification_emails_enable_for_email">Enable email notifications for %s</string>
|
||||
|
||||
<string name="settings_notification_default">Default Notifications</string>
|
||||
<string name="settings_notification_mentions_and_keywords">Mentions and Keywords</string>
|
||||
<string name="settings_notification_other">Other</string>
|
||||
|
|
|
@ -55,6 +55,10 @@
|
|||
|
||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||
|
||||
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||
android:key="SETTINGS_EMAIL_NOTIFICATION_CATEGORY_PREFERENCE_KEY"
|
||||
android:title="@string/settings_notification_emails_category"/>
|
||||
|
||||
<im.vector.app.core.preference.VectorPreferenceCategory
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_notification_configuration">
|
||||
|
@ -114,7 +118,6 @@
|
|||
android:key="SETTINGS_START_ON_BOOT_PREFERENCE_KEY"
|
||||
android:title="@string/settings_start_on_boot" />
|
||||
|
||||
|
||||
</im.vector.app.core.preference.VectorPreferenceCategory>
|
||||
|
||||
<im.vector.app.core.preference.VectorPreferenceCategory android:title="@string/settings_troubleshoot_title">
|
||||
|
|
Loading…
Add table
Reference in a new issue