Merge pull request #7217 from vector-im/feature/eric/msc3881

Implements MSC3881 (enabled and device_id fields for Pusher API)
This commit is contained in:
Eric Decanini 2022-10-10 12:37:47 -04:00 committed by GitHub
commit a096ff03c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 635 additions and 39 deletions

1
changelog.d/7217.wip Normal file
View File

@ -0,0 +1 @@
Implements MSC3881: Parses `enabled` and `device_id` fields from updated Pusher API

View File

@ -1702,13 +1702,15 @@
<string name="settings_push_rules_no_rules">No push rules defined</string>
<string name="settings_push_gateway_no_pushers">No registered push gateways</string>
<string name="push_gateway_item_app_id">app_id:</string>
<string name="push_gateway_item_push_key">push_key:</string>
<string name="push_gateway_item_app_display_name">app_display_name:</string>
<string name="push_gateway_item_device_name">session_name:</string>
<string name="push_gateway_item_app_id">App ID:</string>
<string name="push_gateway_item_push_key">Push Key:</string>
<string name="push_gateway_item_app_display_name">App Display Name:</string>
<string name="push_gateway_item_device_name">Session Display Name:</string>
<string name="push_gateway_item_device_id">Session ID:</string>
<string name="push_gateway_item_url">Url:</string>
<string name="push_gateway_item_format">Format:</string>
<string name="push_gateway_item_profile_tag">Profile tag:</string>
<string name="push_gateway_item_enabled">Enabled:</string>
<string name="preference_voice_and_video">Voice &amp; Video</string>
<string name="preference_root_help_about">Help &amp; About</string>

View File

@ -58,6 +58,16 @@ data class HttpPusher(
*/
val url: String,
/**
* Whether the pusher should actively create push notifications.
*/
val enabled: Boolean,
/**
* The device ID of the session that registered the pusher.
*/
val deviceId: String,
/**
* 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

View File

@ -24,8 +24,9 @@ data class Pusher(
val profileTag: String? = null,
val lang: String?,
val data: PusherData,
val state: PusherState
val enabled: Boolean,
val deviceId: String?,
val state: PusherState,
) {
companion object {

View File

@ -33,7 +33,9 @@ internal object PushersMapper {
profileTag = pushEntity.profileTag,
lang = pushEntity.lang,
data = PusherData(pushEntity.data?.url, pushEntity.data?.format),
state = pushEntity.state
enabled = pushEntity.enabled,
deviceId = pushEntity.deviceId,
state = pushEntity.state,
)
}
@ -46,7 +48,9 @@ internal object PushersMapper {
deviceDisplayName = pusher.deviceDisplayName,
profileTag = pusher.profileTag,
lang = pusher.lang,
data = PusherDataEntity(pusher.data?.url, pusher.data?.format)
data = PusherDataEntity(pusher.data?.url, pusher.data?.format),
enabled = pusher.enabled,
deviceId = pusher.deviceId,
)
}
}

View File

@ -18,15 +18,6 @@ package org.matrix.android.sdk.internal.database.model
import io.realm.RealmObject
import org.matrix.android.sdk.api.session.pushers.PusherState
// TODO
// at java.lang.Thread.run(Thread.java:764)
// Caused by: java.lang.IllegalArgumentException: 'value' is not a valid managed object.
// at io.realm.ProxyState.checkValidObject(ProxyState.java:213)
// at io.realm.im_vector_matrix_android_internal_database_model_PusherEntityRealmProxy
// .realmSet$data(im_vector_matrix_android_internal_database_model_PusherEntityRealmProxy.java:413)
// at org.matrix.android.sdk.internal.database.model.PusherEntity.setData(PusherEntity.kt:16)
// at org.matrix.android.sdk.internal.session.pushers.AddHttpPusherWorker$doWork$$inlined$fold$lambda$2.execute(AddHttpPusherWorker.kt:70)
// at io.realm.Realm.executeTransaction(Realm.java:1493)
internal open class PusherEntity(
var pushKey: String = "",
var kind: String? = null,
@ -35,7 +26,9 @@ internal open class PusherEntity(
var deviceDisplayName: String? = null,
var profileTag: String? = null,
var lang: String? = null,
var data: PusherDataEntity? = null
var data: PusherDataEntity? = null,
var enabled: Boolean = true,
var deviceId: String? = null,
) : RealmObject() {
private var stateStr: String = PusherState.UNREGISTERED.name

View File

@ -38,6 +38,7 @@ internal class DefaultAddPusherTask @Inject constructor(
private val requestExecutor: RequestExecutor,
private val globalErrorReceiver: GlobalErrorReceiver
) : AddPusherTask {
override suspend fun execute(params: AddPusherTask.Params) {
val pusher = params.pusher
try {
@ -71,6 +72,8 @@ internal class DefaultAddPusherTask @Inject constructor(
echo.profileTag = pusher.profileTag
echo.data?.format = pusher.data?.format
echo.data?.url = pusher.data?.url
echo.enabled = pusher.enabled
echo.deviceId = pusher.deviceId
echo.state = PusherState.REGISTERED
}
}

View File

@ -78,7 +78,9 @@ internal class DefaultPushersService @Inject constructor(
appDisplayName = appDisplayName,
deviceDisplayName = deviceDisplayName,
data = JsonPusherData(url, EVENT_ID_ONLY.takeIf { withEventIdOnly }),
append = append
append = append,
enabled = enabled,
deviceId = deviceId,
)
override suspend fun addEmailPusher(

View File

@ -33,6 +33,8 @@ import java.security.InvalidParameterException
* "device_display_name": "Alice's Phone",
* "profile_tag": "xyz",
* "lang": "en-US",
* "enabled": true,
* "device_id": "abc123",
* "data": {
* "url": "https://example.com/_matrix/push/v1/notify"
* }
@ -112,7 +114,19 @@ internal data class JsonPusher(
* The default is false.
*/
@Json(name = "append")
val append: Boolean? = false
val append: Boolean? = false,
/**
* Whether the pusher should actively create push notifications.
*/
@Json(name = "org.matrix.msc3881.enabled")
val enabled: Boolean = true,
/**
* The device_id of the session that registered the pusher.
*/
@Json(name = "org.matrix.msc3881.device_id")
val deviceId: String? = null,
) {
init {
// Do some parameter checks. It's ok to throw Exception, to inform developer of the problem

View File

@ -0,0 +1,64 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.database.mapper
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.test.fixtures.JsonPusherFixture.aJsonPusher
import org.matrix.android.sdk.test.fixtures.PusherEntityFixture.aPusherEntity
class PushersMapperTest {
@Test
fun `when mapping PusherEntity, then it is mapped into Pusher successfully`() {
val pusherEntity = aPusherEntity()
val mappedPusher = PushersMapper.map(pusherEntity)
mappedPusher.pushKey shouldBeEqualTo pusherEntity.pushKey
mappedPusher.kind shouldBeEqualTo pusherEntity.kind.orEmpty()
mappedPusher.appId shouldBeEqualTo pusherEntity.appId
mappedPusher.appDisplayName shouldBeEqualTo pusherEntity.appDisplayName
mappedPusher.deviceDisplayName shouldBeEqualTo pusherEntity.deviceDisplayName
mappedPusher.profileTag shouldBeEqualTo pusherEntity.profileTag
mappedPusher.lang shouldBeEqualTo pusherEntity.lang
mappedPusher.data.url shouldBeEqualTo pusherEntity.data?.url
mappedPusher.data.format shouldBeEqualTo pusherEntity.data?.format
mappedPusher.enabled shouldBeEqualTo pusherEntity.enabled
mappedPusher.deviceId shouldBeEqualTo pusherEntity.deviceId
mappedPusher.state shouldBeEqualTo pusherEntity.state
}
@Test
fun `when mapping JsonPusher, then it is mapped into Pusher successfully`() {
val jsonPusher = aJsonPusher()
val mappedPusherEntity = PushersMapper.map(jsonPusher)
mappedPusherEntity.pushKey shouldBeEqualTo jsonPusher.pushKey
mappedPusherEntity.kind shouldBeEqualTo jsonPusher.kind
mappedPusherEntity.appId shouldBeEqualTo jsonPusher.appId
mappedPusherEntity.appDisplayName shouldBeEqualTo jsonPusher.appDisplayName
mappedPusherEntity.deviceDisplayName shouldBeEqualTo jsonPusher.deviceDisplayName
mappedPusherEntity.profileTag shouldBeEqualTo jsonPusher.profileTag
mappedPusherEntity.lang shouldBeEqualTo jsonPusher.lang
mappedPusherEntity.data?.url shouldBeEqualTo jsonPusher.data?.url
mappedPusherEntity.data?.format shouldBeEqualTo jsonPusher.data?.format
mappedPusherEntity.enabled shouldBeEqualTo jsonPusher.enabled
mappedPusherEntity.deviceId shouldBeEqualTo jsonPusher.deviceId
}
}

View File

@ -71,7 +71,7 @@ class DefaultAddPusherTaskTest {
}
@Test
fun `given a persisted pusher when adding Pusher then updates api and mutates persisted result with Registered state`() {
fun `given a persisted pusher, when adding Pusher, then updates api and mutates persisted result with Registered state`() {
val realmResult = PusherEntity(appDisplayName = null)
monarchy.givenWhereReturns(result = realmResult)
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
@ -85,7 +85,7 @@ class DefaultAddPusherTaskTest {
}
@Test
fun `given a persisted push entity and SetPush API fails when adding Pusher then mutates persisted result with Failed registration state and rethrows`() {
fun `given a persisted push entity and SetPush API fails, when adding Pusher, then mutates persisted result with Failed registration state and rethrows`() {
val realmResult = PusherEntity()
monarchy.givenWhereReturns(result = realmResult)
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
@ -99,7 +99,7 @@ class DefaultAddPusherTaskTest {
}
@Test
fun `given no persisted push entity and SetPush API fails when adding Pusher then rethrows error`() {
fun `given no persisted push entity and SetPush API fails, when adding Pusher, then rethrows error`() {
monarchy.givenWhereReturns<PusherEntity>(result = null)
.givenEqualTo(PusherEntityFields.PUSH_KEY, A_JSON_PUSHER.pushKey)
pushersAPI.givenSetPusherErrors(SocketException())

View File

@ -0,0 +1,49 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.test.fixtures
import org.matrix.android.sdk.internal.session.pushers.JsonPusher
import org.matrix.android.sdk.internal.session.pushers.JsonPusherData
internal object JsonPusherFixture {
fun aJsonPusher(
pushKey: String = "",
kind: String? = null,
appId: String = "",
appDisplayName: String? = null,
deviceDisplayName: String? = null,
profileTag: String? = null,
lang: String? = null,
data: JsonPusherData? = null,
append: Boolean? = false,
enabled: Boolean = true,
deviceId: String? = null,
) = JsonPusher(
pushKey,
kind,
appId,
appDisplayName,
deviceDisplayName,
profileTag,
lang,
data,
append,
enabled,
deviceId,
)
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.test.fixtures
import org.matrix.android.sdk.internal.database.model.PusherDataEntity
import org.matrix.android.sdk.internal.database.model.PusherEntity
internal object PusherEntityFixture {
fun aPusherEntity(
pushKey: String = "",
kind: String? = null,
appId: String = "",
appDisplayName: String? = null,
deviceDisplayName: String? = null,
profileTag: String? = null,
lang: String? = null,
data: PusherDataEntity? = null,
enabled: Boolean = true,
deviceId: String? = null,
) = PusherEntity(
pushKey,
kind,
appId,
appDisplayName,
deviceDisplayName,
profileTag,
lang,
data,
enabled,
deviceId,
)
}

View File

@ -23,6 +23,10 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.DefaultAppNameProvider
import im.vector.app.core.resources.DefaultLocaleProvider
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.fdroid.service.FDroidGuardServiceStarter
import im.vector.app.features.home.NightlyProxy
@ -59,4 +63,10 @@ abstract class FlavorModule {
@Binds
abstract fun bindsFcmHelper(fcmHelper: FdroidFcmHelper): FcmHelper
@Binds
abstract fun bindsLocaleProvider(localeProvider: DefaultLocaleProvider): LocaleProvider
@Binds
abstract fun bindsAppNameProvider(appNameProvider: DefaultAppNameProvider): AppNameProvider
}

View File

@ -23,6 +23,10 @@ import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import im.vector.app.GoogleFlavorLegals
import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.DefaultAppNameProvider
import im.vector.app.core.resources.DefaultLocaleProvider
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.features.home.NightlyProxy
import im.vector.app.features.settings.legals.FlavorLegals
@ -46,6 +50,12 @@ abstract class FlavorModule {
@Binds
abstract fun bindsFcmHelper(fcmHelper: GoogleFcmHelper): FcmHelper
@Binds
abstract fun bindsLocaleProvider(localeProvider: DefaultLocaleProvider): LocaleProvider
@Binds
abstract fun bindsAppNameProvider(appNameProvider: DefaultAppNameProvider): AppNameProvider
@Binds
abstract fun bindsFlavorLegals(legals: GoogleFlavorLegals): FlavorLegals
}

View File

@ -34,6 +34,8 @@ import im.vector.app.SpaceStateHandler
import im.vector.app.SpaceStateHandlerImpl
import im.vector.app.config.Config
import im.vector.app.core.debug.FlipperProxy
import im.vector.app.core.device.DefaultGetDeviceInfoUseCase
import im.vector.app.core.device.GetDeviceInfoUseCase
import im.vector.app.core.dispatchers.CoroutineDispatchers
import im.vector.app.core.error.DefaultErrorFormatter
import im.vector.app.core.error.ErrorFormatter
@ -112,6 +114,9 @@ abstract class VectorBindModule {
@Binds
abstract fun bindSpaceStateHandler(spaceStateHandlerImpl: SpaceStateHandlerImpl): SpaceStateHandler
@Binds
abstract fun bindGetDeviceInfoUseCase(getDeviceInfoUseCase: DefaultGetDeviceInfoUseCase): GetDeviceInfoUseCase
}
@InstallIn(SingletonComponent::class)

View File

@ -0,0 +1,35 @@
/*
* 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.core.device
import im.vector.app.core.di.ActiveSessionHolder
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import javax.inject.Inject
interface GetDeviceInfoUseCase {
fun execute(): CryptoDeviceInfo
}
class DefaultGetDeviceInfoUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder
) : GetDeviceInfoUseCase {
override fun execute(): CryptoDeviceInfo {
return activeSessionHolder.getActiveSession().cryptoService().getMyDevice()
}
}

View File

@ -17,6 +17,7 @@
package im.vector.app.core.pushers
import im.vector.app.R
import im.vector.app.core.device.GetDeviceInfoUseCase
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.LocaleProvider
@ -26,7 +27,7 @@ import java.util.UUID
import javax.inject.Inject
import kotlin.math.abs
private const val DEFAULT_PUSHER_FILE_TAG = "mobile"
internal const val DEFAULT_PUSHER_FILE_TAG = "mobile"
class PushersManager @Inject constructor(
private val unifiedPushHelper: UnifiedPushHelper,
@ -34,6 +35,7 @@ class PushersManager @Inject constructor(
private val localeProvider: LocaleProvider,
private val stringProvider: StringProvider,
private val appNameProvider: AppNameProvider,
private val getDeviceInfoUseCase: GetDeviceInfoUseCase,
) {
suspend fun testPush() {
val currentSession = activeSessionHolder.getActiveSession()
@ -63,15 +65,17 @@ class PushersManager @Inject constructor(
pushKey: String,
gateway: String
) = HttpPusher(
pushKey,
stringProvider.getString(R.string.pusher_app_id),
pushkey = pushKey,
appId = stringProvider.getString(R.string.pusher_app_id),
profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(activeSessionHolder.getActiveSession().myUserId.hashCode()),
localeProvider.current().language,
appNameProvider.getAppName(),
activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE",
gateway,
lang = localeProvider.current().language,
appDisplayName = appNameProvider.getAppName(),
deviceDisplayName = getDeviceInfoUseCase.execute().displayName().orEmpty(),
url = gateway,
enabled = true,
deviceId = activeSessionHolder.getActiveSession().sessionParams.deviceId ?: "MOBILE",
append = false,
withEventIdOnly = true
withEventIdOnly = true,
)
suspend fun registerEmailForPush(email: String) {

View File

@ -21,9 +21,14 @@ import im.vector.app.core.utils.getApplicationLabel
import timber.log.Timber
import javax.inject.Inject
class AppNameProvider @Inject constructor(private val context: Context) {
interface AppNameProvider {
fun getAppName(): String {
fun getAppName(): String
}
class DefaultAppNameProvider @Inject constructor(private val context: Context) : AppNameProvider {
override fun getAppName(): String {
return try {
val appPackageName = context.applicationContext.packageName
var appName = context.getApplicationLabel(appPackageName)

View File

@ -23,9 +23,14 @@ import androidx.core.os.ConfigurationCompat
import java.util.Locale
import javax.inject.Inject
class LocaleProvider @Inject constructor(private val resources: Resources) {
interface LocaleProvider {
fun current(): Locale {
fun current(): Locale
}
class DefaultLocaleProvider @Inject constructor(private val resources: Resources) : LocaleProvider {
override fun current(): Locale {
return ConfigurationCompat.getLocales(resources.configuration).get(0) ?: Locale.getDefault()
}
}

View File

@ -27,7 +27,7 @@ import androidx.core.view.marginEnd
import androidx.core.view.marginStart
import androidx.core.view.marginTop
import im.vector.app.R
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.DefaultLocaleProvider
import im.vector.app.core.resources.getLayoutDirectionFromCurrentLocale
class MessageBubbleContentLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
@ -53,7 +53,7 @@ class MessageBubbleContentLayout @JvmOverloads constructor(context: Context, att
textViewStub.setOnInflateListener(null)
messageTextView = inflated.findViewById(R.id.messageTextView)
}
localeLayoutDirection = LocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
localeLayoutDirection = DefaultLocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {

View File

@ -33,7 +33,7 @@ import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import com.google.android.material.shape.MaterialShapeDrawable
import im.vector.app.R
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.DefaultLocaleProvider
import im.vector.app.core.resources.getLayoutDirectionFromCurrentLocale
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.databinding.ViewMessageBubbleBinding
@ -67,7 +67,7 @@ class MessageBubbleView @JvmOverloads constructor(
override fun onFinishInflate() {
super.onFinishInflate()
views = ViewMessageBubbleBinding.bind(this)
val currentLayoutDirection = LocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
val currentLayoutDirection = DefaultLocaleProvider(resources).getLayoutDirectionFromCurrentLocale()
val layoutDirectionToSet = if (isIncoming) {
currentLayoutDirection
} else {

View File

@ -50,6 +50,8 @@ abstract class PushGatewayItem : VectorEpoxyModel<PushGatewayItem.Holder>(R.layo
holder.format.setTextOrHide(pusher.data.format, hideWhenBlank = true, holder.formatTitle)
holder.profileTag.setTextOrHide(pusher.profileTag, hideWhenBlank = true, holder.profileTagTitle)
holder.deviceName.text = pusher.deviceDisplayName
holder.deviceId.text = pusher.deviceId ?: "null"
holder.enabled.text = pusher.enabled.toString()
holder.removeButton.setOnClickListener {
interactions.onRemovePushTapped(pusher)
}
@ -59,10 +61,12 @@ abstract class PushGatewayItem : VectorEpoxyModel<PushGatewayItem.Holder>(R.layo
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 deviceId by bind<TextView>(R.id.pushGatewayDeviceIdValue)
val formatTitle by bind<View>(R.id.pushGatewayFormat)
val format by bind<TextView>(R.id.pushGatewayFormatValue)
val profileTagTitle by bind<TextView>(R.id.pushGatewayProfileTag)
val profileTag by bind<TextView>(R.id.pushGatewayProfileTagValue)
val enabled by bind<TextView>(R.id.pushGatewayEnabledValue)
val urlTitle by bind<View>(R.id.pushGatewayURL)
val url by bind<TextView>(R.id.pushGatewayURLValue)
val appName by bind<TextView>(R.id.pushGatewayAppNameValue)

View File

@ -83,6 +83,23 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
tools:text="Pixel 6" />
<TextView
android:id="@+id/pushGatewayDeviceId"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/push_gateway_item_device_id"
android:textStyle="bold" />
<TextView
android:id="@+id/pushGatewayDeviceIdValue"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
tools:text="EBMDOLFJD" />
<TextView
@ -135,6 +152,23 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
<TextView
android:id="@+id/pushGatewayEnabled"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:text="@string/push_gateway_item_enabled"
android:textStyle="bold" />
<TextView
android:id="@+id/pushGatewayEnabledValue"
style="@style/Widget.Vector.TextView.Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
tools:text="true" />
<Button
android:id="@+id/pushGatewayDeleteButton"
android:layout_width="wrap_content"

View File

@ -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.core.device
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeCryptoService
import im.vector.app.test.fakes.FakeSession
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
class DefaultGetDeviceInfoUseCaseTest {
private val cryptoService = FakeCryptoService()
private val session = FakeSession(fakeCryptoService = cryptoService)
private val activeSessionHolder = FakeActiveSessionHolder(session)
private val getDeviceInfoUseCase = DefaultGetDeviceInfoUseCase(activeSessionHolder.instance)
@Test
fun `when execute, then get crypto device info`() {
val result = getDeviceInfoUseCase.execute()
result shouldBeEqualTo cryptoService.cryptoDeviceInfo
}
}

View File

@ -0,0 +1,85 @@
/*
* 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.core.pushers
import im.vector.app.R
import im.vector.app.test.fakes.FakeActiveSessionHolder
import im.vector.app.test.fakes.FakeAppNameProvider
import im.vector.app.test.fakes.FakeGetDeviceInfoUseCase
import im.vector.app.test.fakes.FakeLocaleProvider
import im.vector.app.test.fakes.FakePushersService
import im.vector.app.test.fakes.FakeSession
import im.vector.app.test.fakes.FakeStringProvider
import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
import io.mockk.mockk
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
import org.matrix.android.sdk.api.session.pushers.HttpPusher
import java.util.Locale
import kotlin.math.abs
class PushersManagerTest {
private val pushersService = FakePushersService()
private val session = FakeSession(fakePushersService = pushersService)
private val activeSessionHolder = FakeActiveSessionHolder(session)
private val stringProvider = FakeStringProvider()
private val localeProvider = FakeLocaleProvider()
private val appNameProvider = FakeAppNameProvider()
private val getDeviceInfoUseCase = FakeGetDeviceInfoUseCase()
private val pushersManager = PushersManager(
mockk(),
activeSessionHolder.instance,
localeProvider,
stringProvider.instance,
appNameProvider,
getDeviceInfoUseCase,
)
@Test
fun `when enqueueRegisterPusher, then HttpPusher created and enqueued`() {
val pushKey = "abc"
val gateway = "123"
val pusherAppId = "app-id"
val appName = "element"
val deviceDisplayName = "iPhone Lollipop"
stringProvider.given(R.string.pusher_app_id, pusherAppId)
localeProvider.givenCurrent(Locale.UK)
appNameProvider.givenAppName(appName)
getDeviceInfoUseCase.givenDeviceInfo(aCryptoDeviceInfo(unsigned = UnsignedDeviceInfo(deviceDisplayName)))
val expectedHttpPusher = HttpPusher(
pushkey = pushKey,
appId = pusherAppId,
profileTag = DEFAULT_PUSHER_FILE_TAG + "_" + abs(session.myUserId.hashCode()),
lang = Locale.UK.language,
appDisplayName = appName,
deviceDisplayName = deviceDisplayName,
url = gateway,
enabled = true,
deviceId = session.sessionParams.deviceId!!,
append = false,
withEventIdOnly = true,
)
pushersManager.enqueueRegisterPusher(pushKey, gateway)
val httpPusher = pushersService.verifyEnqueueAddHttpPusher()
httpPusher shouldBeEqualTo expectedHttpPusher
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.test.fakes
import im.vector.app.core.resources.AppNameProvider
import io.mockk.every
import io.mockk.mockk
class FakeAppNameProvider : AppNameProvider by mockk() {
fun givenAppName(appName: String) {
every { getAppName() } returns appName
}
}

View File

@ -17,6 +17,7 @@
package im.vector.app.test.fakes
import androidx.lifecycle.MutableLiveData
import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
@ -35,6 +36,7 @@ class FakeCryptoService(
var cryptoDeviceInfos = mutableMapOf<String, CryptoDeviceInfo>()
var cryptoDeviceInfoWithIdLiveData: MutableLiveData<Optional<CryptoDeviceInfo>> = MutableLiveData()
var myDevicesInfoWithIdLiveData: MutableLiveData<Optional<DeviceInfo>> = MutableLiveData()
var cryptoDeviceInfo = aCryptoDeviceInfo()
override fun crossSigningService() = fakeCrossSigningService
@ -81,4 +83,6 @@ class FakeCryptoService(
thirdArg<MatrixCallback<Unit>>().onFailure(error)
}
}
override fun getMyDevice() = cryptoDeviceInfo
}

View File

@ -0,0 +1,29 @@
/*
* 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.test.fakes
import im.vector.app.core.device.GetDeviceInfoUseCase
import io.mockk.every
import io.mockk.mockk
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
class FakeGetDeviceInfoUseCase : GetDeviceInfoUseCase by mockk() {
fun givenDeviceInfo(cryptoDeviceInfo: CryptoDeviceInfo) {
every { execute() } returns cryptoDeviceInfo
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.test.fakes
import im.vector.app.core.resources.LocaleProvider
import io.mockk.every
import io.mockk.mockk
import java.util.Locale
class FakeLocaleProvider : LocaleProvider by mockk() {
fun givenCurrent(locale: Locale) {
every { current() } returns locale
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.test.fakes
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.matrix.android.sdk.api.session.pushers.HttpPusher
import org.matrix.android.sdk.api.session.pushers.PushersService
class FakePushersService : PushersService by mockk(relaxed = true) {
fun verifyEnqueueAddHttpPusher(): HttpPusher {
val httpPusherSlot = slot<HttpPusher>()
verify { enqueueAddHttpPusher(capture(httpPusherSlot)) }
return httpPusherSlot.captured
}
}

View File

@ -41,6 +41,7 @@ class FakeSession(
val fakeHomeServerCapabilitiesService: FakeHomeServerCapabilitiesService = FakeHomeServerCapabilitiesService(),
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService(),
val fakeRoomService: FakeRoomService = FakeRoomService(),
val fakePushersService: FakePushersService = FakePushersService(),
private val fakeEventService: FakeEventService = FakeEventService(),
) : Session by mockk(relaxed = true) {
@ -58,6 +59,7 @@ class FakeSession(
override fun sharedSecretStorageService() = fakeSharedSecretStorageService
override fun roomService() = fakeRoomService
override fun eventService() = fakeEventService
override fun pushersService() = fakePushersService
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
coEvery {

View File

@ -0,0 +1,46 @@
/*
* 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.test.fixtures
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
object CryptoDeviceInfoFixture {
fun aCryptoDeviceInfo(
deviceId: String = "",
userId: String = "",
algorithms: List<String>? = null,
keys: Map<String, String>? = null,
signatures: Map<String, Map<String, String>>? = null,
unsigned: UnsignedDeviceInfo? = null,
trustLevel: DeviceTrustLevel? = null,
isBlocked: Boolean = false,
firstTimeSeenLocalTs: Long? = null,
) = CryptoDeviceInfo(
deviceId,
userId,
algorithms,
keys,
signatures,
unsigned,
trustLevel,
isBlocked,
firstTimeSeenLocalTs,
)
}