Adds Push Notification toggle to Device Manager (#7261)
* Adds push notifications switch * Adds functionality to Push notification toggle * Adds DefaultPushersServiceTest for togglePusher * Adds DefaultTogglePusherTaskTest * Adds SessionOverviewViewModelTest for toggling pusher * Hides pusher toggle if there are no pushers of the device * Adds changelog file * Edits changelog file * Fixes copyrights * Unregisters checkedChangelistener in onDetachedFromWindow for switch view * Fixes post merge errors * Fixes legal copies * Removes unused imports * Fixes lint errors * Fixes test errors * Fixes error * Fixes error * Fixes error * Fixes error * Fixes error
This commit is contained in:
parent
a096ff03c8
commit
2fe636e93b
|
@ -0,0 +1 @@
|
||||||
|
Adds pusher toggle setting to device manager v2
|
|
@ -3304,6 +3304,8 @@
|
||||||
<string name="device_manager_session_overview_signout">Sign out of this session</string>
|
<string name="device_manager_session_overview_signout">Sign out of this session</string>
|
||||||
<string name="device_manager_session_details_title">Session details</string>
|
<string name="device_manager_session_details_title">Session details</string>
|
||||||
<string name="device_manager_session_details_description">Application, device, and activity information.</string>
|
<string name="device_manager_session_details_description">Application, device, and activity information.</string>
|
||||||
|
<string name="device_manager_push_notifications_title">Push notifications</string>
|
||||||
|
<string name="device_manager_push_notifications_description">Receive push notifications on this session.</string>
|
||||||
<string name="device_manager_session_details_session_name">Session name</string>
|
<string name="device_manager_session_details_session_name">Session name</string>
|
||||||
<string name="device_manager_session_details_session_id">Session ID</string>
|
<string name="device_manager_session_details_session_id">Session ID</string>
|
||||||
<string name="device_manager_session_details_session_last_activity">Last activity</string>
|
<string name="device_manager_session_details_session_last_activity">Last activity</string>
|
||||||
|
|
|
@ -6,4 +6,10 @@
|
||||||
<attr name="sessionOverviewEntryDescription" format="string" />
|
<attr name="sessionOverviewEntryDescription" format="string" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="SessionOverviewEntrySwitchView">
|
||||||
|
<attr name="sessionOverviewEntrySwitchTitle" format="string" />
|
||||||
|
<attr name="sessionOverviewEntrySwitchDescription" format="string" />
|
||||||
|
<attr name="sessionOverviewEntrySwitchEnabled" format="boolean" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -67,6 +67,14 @@ interface PushersService {
|
||||||
append: Boolean = true
|
append: Boolean = true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables a registered pusher.
|
||||||
|
*
|
||||||
|
* @param pusher The pusher being toggled
|
||||||
|
* @param enable Whether the pusher should be enabled or disabled
|
||||||
|
*/
|
||||||
|
suspend fun togglePusher(pusher: Pusher, enable: Boolean)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directly ask the push gateway to send a push to this device.
|
* 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.
|
* If successful, the push gateway has accepted the request. In this case, the app should receive a Push with the provided eventId.
|
||||||
|
|
|
@ -42,6 +42,7 @@ internal class DefaultPushersService @Inject constructor(
|
||||||
private val getPusherTask: GetPushersTask,
|
private val getPusherTask: GetPushersTask,
|
||||||
private val pushGatewayNotifyTask: PushGatewayNotifyTask,
|
private val pushGatewayNotifyTask: PushGatewayNotifyTask,
|
||||||
private val addPusherTask: AddPusherTask,
|
private val addPusherTask: AddPusherTask,
|
||||||
|
private val togglePusherTask: TogglePusherTask,
|
||||||
private val removePusherTask: RemovePusherTask,
|
private val removePusherTask: RemovePusherTask,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
) : PushersService {
|
) : PushersService {
|
||||||
|
@ -108,6 +109,24 @@ internal class DefaultPushersService @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override suspend fun togglePusher(pusher: Pusher, enable: Boolean) {
|
||||||
|
togglePusherTask.execute(TogglePusherTask.Params(pusher.toJsonPusher(), enable))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Pusher.toJsonPusher() = JsonPusher(
|
||||||
|
pushKey = pushKey,
|
||||||
|
kind = kind,
|
||||||
|
appId = appId,
|
||||||
|
appDisplayName = appDisplayName,
|
||||||
|
deviceDisplayName = deviceDisplayName,
|
||||||
|
profileTag = profileTag,
|
||||||
|
lang = lang,
|
||||||
|
data = JsonPusherData(data.url, data.format),
|
||||||
|
append = false,
|
||||||
|
enabled = enabled,
|
||||||
|
deviceId = deviceId,
|
||||||
|
)
|
||||||
|
|
||||||
private fun enqueueAddPusher(pusher: JsonPusher): UUID {
|
private fun enqueueAddPusher(pusher: JsonPusher): UUID {
|
||||||
val params = AddPusherWorker.Params(sessionId, pusher)
|
val params = AddPusherWorker.Params(sessionId, pusher)
|
||||||
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
|
val request = workManagerProvider.matrixOneTimeWorkRequestBuilder<AddPusherWorker>()
|
||||||
|
|
|
@ -68,6 +68,9 @@ internal abstract class PushersModule {
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindAddPusherTask(task: DefaultAddPusherTask): AddPusherTask
|
abstract fun bindAddPusherTask(task: DefaultAddPusherTask): AddPusherTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindTogglePusherTask(task: DefaultTogglePusherTask): TogglePusherTask
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask
|
abstract fun bindRemovePusherTask(task: DefaultRemovePusherTask): RemovePusherTask
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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.session.pushers
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.query.where
|
||||||
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
|
import org.matrix.android.sdk.internal.network.GlobalErrorReceiver
|
||||||
|
import org.matrix.android.sdk.internal.network.RequestExecutor
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import org.matrix.android.sdk.internal.util.awaitTransaction
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface TogglePusherTask : Task<TogglePusherTask.Params, Unit> {
|
||||||
|
data class Params(val pusher: JsonPusher, val enable: Boolean)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultTogglePusherTask @Inject constructor(
|
||||||
|
private val pushersAPI: PushersAPI,
|
||||||
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
|
private val requestExecutor: RequestExecutor,
|
||||||
|
private val globalErrorReceiver: GlobalErrorReceiver
|
||||||
|
) : TogglePusherTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: TogglePusherTask.Params) {
|
||||||
|
val pusher = params.pusher.copy(enabled = params.enable)
|
||||||
|
|
||||||
|
requestExecutor.executeRequest(globalErrorReceiver) {
|
||||||
|
pushersAPI.setPusher(pusher)
|
||||||
|
}
|
||||||
|
|
||||||
|
monarchy.awaitTransaction { realm ->
|
||||||
|
val entity = PusherEntity.where(realm, params.pusher.pushKey).findFirst()
|
||||||
|
entity?.apply { enabled = params.enable }?.let { realm.insertOrUpdate(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* 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.session.pushers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeAddPusherTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeGetPushersTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeRemovePusherTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeTaskExecutor
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeTogglePusherTask
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeWorkManagerProvider
|
||||||
|
import org.matrix.android.sdk.test.fakes.internal.FakePushGatewayNotifyTask
|
||||||
|
import org.matrix.android.sdk.test.fixtures.PusherFixture
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class DefaultPushersServiceTest {
|
||||||
|
|
||||||
|
private val workManagerProvider = FakeWorkManagerProvider()
|
||||||
|
private val monarchy = FakeMonarchy()
|
||||||
|
private val sessionId = ""
|
||||||
|
private val getPushersTask = FakeGetPushersTask()
|
||||||
|
private val pushGatewayNotifyTask = FakePushGatewayNotifyTask()
|
||||||
|
private val addPusherTask = FakeAddPusherTask()
|
||||||
|
private val togglePusherTask = FakeTogglePusherTask()
|
||||||
|
private val removePusherTask = FakeRemovePusherTask()
|
||||||
|
private val taskExecutor = FakeTaskExecutor()
|
||||||
|
|
||||||
|
private val pushersService = DefaultPushersService(
|
||||||
|
workManagerProvider.instance,
|
||||||
|
monarchy.instance,
|
||||||
|
sessionId,
|
||||||
|
getPushersTask,
|
||||||
|
pushGatewayNotifyTask,
|
||||||
|
addPusherTask,
|
||||||
|
togglePusherTask,
|
||||||
|
removePusherTask,
|
||||||
|
taskExecutor.instance,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when togglePusher, then execute task`() = runTest {
|
||||||
|
val pusher = PusherFixture.aPusher()
|
||||||
|
val enable = true
|
||||||
|
|
||||||
|
pushersService.togglePusher(pusher, enable)
|
||||||
|
|
||||||
|
togglePusherTask.verifyExecution(pusher, enable)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.session.pushers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.Test
|
||||||
|
import org.matrix.android.sdk.internal.database.model.PusherEntity
|
||||||
|
import org.matrix.android.sdk.internal.database.model.PusherEntityFields
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeGlobalErrorReceiver
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeMonarchy
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakePushersAPI
|
||||||
|
import org.matrix.android.sdk.test.fakes.FakeRequestExecutor
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenEqualTo
|
||||||
|
import org.matrix.android.sdk.test.fakes.givenFindFirst
|
||||||
|
import org.matrix.android.sdk.test.fixtures.JsonPusherFixture.aJsonPusher
|
||||||
|
import org.matrix.android.sdk.test.fixtures.PusherEntityFixture.aPusherEntity
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class DefaultTogglePusherTaskTest {
|
||||||
|
|
||||||
|
private val pushersAPI = FakePushersAPI()
|
||||||
|
private val monarchy = FakeMonarchy()
|
||||||
|
private val requestExecutor = FakeRequestExecutor()
|
||||||
|
private val globalErrorReceiver = FakeGlobalErrorReceiver()
|
||||||
|
|
||||||
|
private val togglePusherTask = DefaultTogglePusherTask(pushersAPI, monarchy.instance, requestExecutor, globalErrorReceiver)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `execution toggles enable on both local and remote`() = runTest {
|
||||||
|
val jsonPusher = aJsonPusher(enabled = false)
|
||||||
|
val params = TogglePusherTask.Params(aJsonPusher(), true)
|
||||||
|
|
||||||
|
val pusherEntity = aPusherEntity(enabled = false)
|
||||||
|
monarchy.givenWhere<PusherEntity>()
|
||||||
|
.givenEqualTo(PusherEntityFields.PUSH_KEY, jsonPusher.pushKey)
|
||||||
|
.givenFindFirst(pusherEntity)
|
||||||
|
|
||||||
|
togglePusherTask.execute(params)
|
||||||
|
|
||||||
|
val expectedPayload = jsonPusher.copy(enabled = true)
|
||||||
|
pushersAPI.verifySetPusher(expectedPayload)
|
||||||
|
monarchy.verifyInsertOrUpdate<PusherEntity> {
|
||||||
|
withArg { actual ->
|
||||||
|
actual.enabled shouldBeEqualTo true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.AddPusherTask
|
||||||
|
|
||||||
|
class FakeAddPusherTask : AddPusherTask by mockk()
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.GetPushersTask
|
||||||
|
|
||||||
|
class FakeGetPushersTask : GetPushersTask by mockk()
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.RemovePusherTask
|
||||||
|
|
||||||
|
class FakeRemovePusherTask : RemovePusherTask by mockk()
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
|
|
||||||
|
internal class FakeTaskExecutor {
|
||||||
|
|
||||||
|
val instance: TaskExecutor = mockk()
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes
|
||||||
|
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.slot
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.TogglePusherTask
|
||||||
|
|
||||||
|
class FakeTogglePusherTask : TogglePusherTask by mockk(relaxed = true) {
|
||||||
|
|
||||||
|
fun verifyExecution(pusher: Pusher, enable: Boolean) {
|
||||||
|
val slot = slot<TogglePusherTask.Params>()
|
||||||
|
coVerify { execute(capture(slot)) }
|
||||||
|
val params = slot.captured
|
||||||
|
params.pusher.pushKey shouldBeEqualTo pusher.pushKey
|
||||||
|
params.enable shouldBeEqualTo enable
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* 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.fakes.internal
|
||||||
|
|
||||||
|
import io.mockk.mockk
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotifyTask
|
||||||
|
|
||||||
|
class FakePushGatewayNotifyTask : PushGatewayNotifyTask by mockk()
|
50
matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/PusherFixture.kt
vendored
Normal file
50
matrix-sdk-android/src/test/java/org/matrix/android/sdk/test/fixtures/PusherFixture.kt
vendored
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.api.session.pushers.Pusher
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PusherData
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||||
|
|
||||||
|
object PusherFixture {
|
||||||
|
|
||||||
|
fun aPusher(
|
||||||
|
pushKey: String = "",
|
||||||
|
kind: String = "",
|
||||||
|
appId: String = "",
|
||||||
|
appDisplayName: String? = "",
|
||||||
|
deviceDisplayName: String? = "",
|
||||||
|
profileTag: String? = null,
|
||||||
|
lang: String? = "",
|
||||||
|
data: PusherData = PusherData("f.o/_matrix/push/v1/notify", ""),
|
||||||
|
enabled: Boolean = true,
|
||||||
|
deviceId: String? = "",
|
||||||
|
state: PusherState = PusherState.REGISTERED,
|
||||||
|
) = Pusher(
|
||||||
|
pushKey,
|
||||||
|
kind,
|
||||||
|
appId,
|
||||||
|
appDisplayName,
|
||||||
|
deviceDisplayName,
|
||||||
|
profileTag,
|
||||||
|
lang,
|
||||||
|
data,
|
||||||
|
enabled,
|
||||||
|
deviceId,
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
}
|
|
@ -296,6 +296,7 @@ dependencies {
|
||||||
// Plant Timber tree for test
|
// Plant Timber tree for test
|
||||||
testImplementation libs.tests.timberJunitRule
|
testImplementation libs.tests.timberJunitRule
|
||||||
testImplementation libs.airbnb.mavericksTesting
|
testImplementation libs.airbnb.mavericksTesting
|
||||||
|
testImplementation libs.androidx.coreTesting
|
||||||
testImplementation(libs.jetbrains.coroutinesTest) {
|
testImplementation(libs.jetbrains.coroutinesTest) {
|
||||||
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
|
exclude group: "org.jetbrains.kotlinx", module: "kotlinx-coroutines-debug"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,14 @@ package im.vector.app.features.settings.devices.v2.overview
|
||||||
import im.vector.app.core.platform.VectorViewModelAction
|
import im.vector.app.core.platform.VectorViewModelAction
|
||||||
|
|
||||||
sealed class SessionOverviewAction : VectorViewModelAction {
|
sealed class SessionOverviewAction : VectorViewModelAction {
|
||||||
|
|
||||||
object VerifySession : SessionOverviewAction()
|
object VerifySession : SessionOverviewAction()
|
||||||
object SignoutOtherSession : SessionOverviewAction()
|
object SignoutOtherSession : SessionOverviewAction()
|
||||||
object SsoAuthDone : SessionOverviewAction()
|
object SsoAuthDone : SessionOverviewAction()
|
||||||
data class PasswordAuthDone(val password: String) : SessionOverviewAction()
|
data class PasswordAuthDone(val password: String) : SessionOverviewAction()
|
||||||
object ReAuthCancelled : SessionOverviewAction()
|
object ReAuthCancelled : SessionOverviewAction()
|
||||||
|
data class TogglePushNotifications(
|
||||||
|
val deviceId: String,
|
||||||
|
val enabled: Boolean,
|
||||||
|
) : SessionOverviewAction()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* 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.features.settings.devices.v2.overview
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.TypedArray
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.content.res.use
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.extensions.setAttributeBackground
|
||||||
|
import im.vector.app.databinding.ViewSessionOverviewEntrySwitchBinding
|
||||||
|
|
||||||
|
class SessionOverviewEntrySwitchView @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private val binding = ViewSessionOverviewEntrySwitchBinding.inflate(
|
||||||
|
LayoutInflater.from(context),
|
||||||
|
this
|
||||||
|
)
|
||||||
|
|
||||||
|
init {
|
||||||
|
initBackground()
|
||||||
|
context.obtainStyledAttributes(
|
||||||
|
attrs,
|
||||||
|
R.styleable.SessionOverviewEntrySwitchView,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
).use {
|
||||||
|
setTitle(it)
|
||||||
|
setDescription(it)
|
||||||
|
setSwitchedEnabled(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initBackground() {
|
||||||
|
binding.root.setAttributeBackground(android.R.attr.selectableItemBackground)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTitle(typedArray: TypedArray) {
|
||||||
|
val title = typedArray.getString(R.styleable.SessionOverviewEntrySwitchView_sessionOverviewEntrySwitchTitle)
|
||||||
|
binding.sessionsOverviewEntryTitle.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDescription(typedArray: TypedArray) {
|
||||||
|
val description = typedArray.getString(R.styleable.SessionOverviewEntrySwitchView_sessionOverviewEntrySwitchDescription)
|
||||||
|
binding.sessionsOverviewEntryDescription.text = description
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setSwitchedEnabled(typedArray: TypedArray) {
|
||||||
|
val enabled = typedArray.getBoolean(R.styleable.SessionOverviewEntrySwitchView_sessionOverviewEntrySwitchEnabled, true)
|
||||||
|
binding.sessionsOverviewEntrySwitch.isChecked = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChecked(checked: Boolean) {
|
||||||
|
binding.sessionsOverviewEntrySwitch.isChecked = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnCheckedChangeListener(listener: CompoundButton.OnCheckedChangeListener?) {
|
||||||
|
binding.sessionsOverviewEntrySwitch.setOnCheckedChangeListener(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetachedFromWindow() {
|
||||||
|
super.onDetachedFromWindow()
|
||||||
|
binding.sessionsOverviewEntrySwitch.setOnCheckedChangeListener(null)
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.fragmentViewModel
|
import com.airbnb.mvrx.fragmentViewModel
|
||||||
|
@ -41,12 +42,14 @@ import im.vector.app.core.resources.StringProvider
|
||||||
import im.vector.app.databinding.FragmentSessionOverviewBinding
|
import im.vector.app.databinding.FragmentSessionOverviewBinding
|
||||||
import im.vector.app.features.auth.ReAuthActivity
|
import im.vector.app.features.auth.ReAuthActivity
|
||||||
import im.vector.app.features.crypto.recover.SetupMode
|
import im.vector.app.features.crypto.recover.SetupMode
|
||||||
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
import im.vector.app.features.settings.devices.v2.list.SessionInfoViewState
|
||||||
import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
|
import im.vector.app.features.settings.devices.v2.more.SessionLearnMoreBottomSheet
|
||||||
import im.vector.app.features.workers.signout.SignOutUiWorker
|
import im.vector.app.features.workers.signout.SignOutUiWorker
|
||||||
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -174,9 +177,14 @@ class SessionOverviewFragment :
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
updateToolbar(state)
|
updateToolbar(state)
|
||||||
updateEntryDetails(state.deviceId)
|
|
||||||
updateSessionInfo(state)
|
updateSessionInfo(state)
|
||||||
updateLoading(state.isLoading)
|
updateLoading(state.isLoading)
|
||||||
|
updatePushNotificationToggle(state.deviceId, state.pushers.invoke().orEmpty())
|
||||||
|
if (state.deviceInfo is Success) {
|
||||||
|
renderSessionInfo(state.isCurrentSessionTrusted, state.deviceInfo.invoke())
|
||||||
|
} else {
|
||||||
|
hideSessionInfo()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
private fun updateToolbar(viewState: SessionOverviewViewState) {
|
||||||
|
@ -189,12 +197,6 @@ class SessionOverviewFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateEntryDetails(deviceId: String) {
|
|
||||||
views.sessionOverviewEntryDetails.setOnClickListener {
|
|
||||||
viewNavigator.goToSessionDetails(requireContext(), deviceId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateSessionInfo(viewState: SessionOverviewViewState) {
|
private fun updateSessionInfo(viewState: SessionOverviewViewState) {
|
||||||
if (viewState.deviceInfo is Success) {
|
if (viewState.deviceInfo is Success) {
|
||||||
views.sessionOverviewInfo.isVisible = true
|
views.sessionOverviewInfo.isVisible = true
|
||||||
|
@ -217,6 +219,35 @@ class SessionOverviewFragment :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updatePushNotificationToggle(deviceId: String, pushers: List<Pusher>) {
|
||||||
|
views.sessionOverviewPushNotifications.apply {
|
||||||
|
if (pushers.isEmpty()) {
|
||||||
|
isVisible = false
|
||||||
|
} else {
|
||||||
|
val allPushersAreEnabled = pushers.all { it.enabled }
|
||||||
|
setOnCheckedChangeListener(null)
|
||||||
|
setChecked(allPushersAreEnabled)
|
||||||
|
post {
|
||||||
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
viewModel.handle(SessionOverviewAction.TogglePushNotifications(deviceId, isChecked))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderSessionInfo(isCurrentSession: Boolean, deviceFullInfo: DeviceFullInfo) {
|
||||||
|
views.sessionOverviewInfo.isVisible = true
|
||||||
|
val viewState = SessionInfoViewState(
|
||||||
|
isCurrentSession = isCurrentSession,
|
||||||
|
deviceFullInfo = deviceFullInfo,
|
||||||
|
isDetailsButtonVisible = false,
|
||||||
|
isLearnMoreLinkVisible = true,
|
||||||
|
isLastSeenDetailsVisible = true,
|
||||||
|
)
|
||||||
|
views.sessionOverviewInfo.render(viewState, dateFormatter, drawableProvider, colorProvider, stringProvider)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateLoading(isLoading: Boolean) {
|
private fun updateLoading(isLoading: Boolean) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
showLoading(null)
|
showLoading(null)
|
||||||
|
@ -275,4 +306,8 @@ class SessionOverviewFragment :
|
||||||
)
|
)
|
||||||
SessionLearnMoreBottomSheet.show(childFragmentManager, args)
|
SessionLearnMoreBottomSheet.show(childFragmentManager, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hideSessionInfo() {
|
||||||
|
views.sessionOverviewInfo.isGone = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,14 +43,17 @@ import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
|
import org.matrix.android.sdk.flow.flow
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
|
||||||
class SessionOverviewViewModel @AssistedInject constructor(
|
class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
@Assisted val initialState: SessionOverviewViewState,
|
@Assisted val initialState: SessionOverviewViewState,
|
||||||
|
private val session: Session,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
private val getDeviceFullInfoUseCase: GetDeviceFullInfoUseCase,
|
||||||
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
private val checkIfCurrentSessionCanBeVerifiedUseCase: CheckIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
|
@ -73,6 +76,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
init {
|
init {
|
||||||
observeSessionInfo(initialState.deviceId)
|
observeSessionInfo(initialState.deviceId)
|
||||||
observeCurrentSessionInfo()
|
observeCurrentSessionInfo()
|
||||||
|
observePushers(initialState.deviceId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun observeSessionInfo(deviceId: String) {
|
private fun observeSessionInfo(deviceId: String) {
|
||||||
|
@ -94,6 +98,13 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun observePushers(deviceId: String) {
|
||||||
|
session.flow()
|
||||||
|
.livePushers()
|
||||||
|
.map { it.filter { pusher -> pusher.deviceId == deviceId } }
|
||||||
|
.execute { copy(pushers = it) }
|
||||||
|
}
|
||||||
|
|
||||||
override fun handle(action: SessionOverviewAction) {
|
override fun handle(action: SessionOverviewAction) {
|
||||||
when (action) {
|
when (action) {
|
||||||
is SessionOverviewAction.VerifySession -> handleVerifySessionAction()
|
is SessionOverviewAction.VerifySession -> handleVerifySessionAction()
|
||||||
|
@ -101,6 +112,7 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
SessionOverviewAction.SsoAuthDone -> handleSsoAuthDone()
|
SessionOverviewAction.SsoAuthDone -> handleSsoAuthDone()
|
||||||
is SessionOverviewAction.PasswordAuthDone -> handlePasswordAuthDone(action)
|
is SessionOverviewAction.PasswordAuthDone -> handlePasswordAuthDone(action)
|
||||||
SessionOverviewAction.ReAuthCancelled -> handleReAuthCancelled()
|
SessionOverviewAction.ReAuthCancelled -> handleReAuthCancelled()
|
||||||
|
is SessionOverviewAction.TogglePushNotifications -> handleTogglePusherAction(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,4 +210,13 @@ class SessionOverviewViewModel @AssistedInject constructor(
|
||||||
private fun handleReAuthCancelled() {
|
private fun handleReAuthCancelled() {
|
||||||
pendingAuthHandler.reAuthCancelled()
|
pendingAuthHandler.reAuthCancelled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleTogglePusherAction(action: SessionOverviewAction.TogglePushNotifications) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val devicePushers = awaitState().pushers.invoke()?.filter { it.deviceId == action.deviceId }
|
||||||
|
devicePushers?.forEach { pusher ->
|
||||||
|
session.pushersService().togglePusher(pusher, action.enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,14 @@ import com.airbnb.mvrx.Async
|
||||||
import com.airbnb.mvrx.MavericksState
|
import com.airbnb.mvrx.MavericksState
|
||||||
import com.airbnb.mvrx.Uninitialized
|
import com.airbnb.mvrx.Uninitialized
|
||||||
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||||
|
|
||||||
data class SessionOverviewViewState(
|
data class SessionOverviewViewState(
|
||||||
val deviceId: String,
|
val deviceId: String,
|
||||||
val isCurrentSessionTrusted: Boolean = false,
|
val isCurrentSessionTrusted: Boolean = false,
|
||||||
val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
|
val deviceInfo: Async<DeviceFullInfo> = Uninitialized,
|
||||||
val isLoading: Boolean = false,
|
val isLoading: Boolean = false,
|
||||||
|
val pushers: Async<List<Pusher>> = Uninitialized,
|
||||||
) : MavericksState {
|
) : MavericksState {
|
||||||
constructor(args: SessionOverviewArgs) : this(
|
constructor(args: SessionOverviewArgs) : this(
|
||||||
deviceId = args.deviceId
|
deviceId = args.deviceId
|
||||||
|
|
|
@ -31,6 +31,16 @@
|
||||||
app:sessionOverviewEntryDescription="@string/device_manager_session_details_description"
|
app:sessionOverviewEntryDescription="@string/device_manager_session_details_description"
|
||||||
app:sessionOverviewEntryTitle="@string/device_manager_session_details_title" />
|
app:sessionOverviewEntryTitle="@string/device_manager_session_details_title" />
|
||||||
|
|
||||||
|
<im.vector.app.features.settings.devices.v2.overview.SessionOverviewEntrySwitchView
|
||||||
|
android:id="@+id/sessionOverviewPushNotifications"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/sessionOverviewEntryDetails"
|
||||||
|
app:sessionOverviewEntrySwitchDescription="@string/device_manager_push_notifications_description"
|
||||||
|
app:sessionOverviewEntrySwitchTitle="@string/device_manager_push_notifications_title" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/sessionOverviewSignout"
|
android:id="@+id/sessionOverviewSignout"
|
||||||
style="@style/Widget.Vector.Button.Text.Destructive"
|
style="@style/Widget.Vector.Button.Text.Destructive"
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/sessionsOverviewEntryTitle"
|
||||||
|
style="@style/TextAppearance.Vector.Subtitle.DevicesManagement"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="@dimen/layout_horizontal_margin"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:text="Push notifications" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/sessionsOverviewEntryDescription"
|
||||||
|
style="@style/TextAppearance.Vector.Body.DevicesManagement"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/sessionsOverviewEntryTitle"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/sessionsOverviewEntryTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/sessionsOverviewEntryTitle"
|
||||||
|
tools:text="Receive push notifications on this session." />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.SwitchCompat
|
||||||
|
android:id="@+id/sessionsOverviewEntrySwitch"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/sessionsOverviewEntryDivider"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="20dp"
|
||||||
|
android:background="@drawable/divider_horizontal"
|
||||||
|
app:layout_constraintEnd_toEndOf="@id/sessionsOverviewEntryTitle"
|
||||||
|
app:layout_constraintStart_toStartOf="@id/sessionsOverviewEntryTitle"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/sessionsOverviewEntryDescription" />
|
||||||
|
|
||||||
|
</merge>
|
|
@ -17,9 +17,10 @@
|
||||||
package im.vector.app.features.settings.devices.v2.overview
|
package im.vector.app.features.settings.devices.v2.overview
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
|
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||||
|
import com.airbnb.mvrx.Loading
|
||||||
import com.airbnb.mvrx.Success
|
import com.airbnb.mvrx.Success
|
||||||
import com.airbnb.mvrx.test.MavericksTestRule
|
import com.airbnb.mvrx.test.MavericksTestRule
|
||||||
import im.vector.app.R
|
|
||||||
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
import im.vector.app.features.settings.devices.v2.DeviceFullInfo
|
||||||
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
import im.vector.app.features.settings.devices.v2.RefreshDevicesUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
|
||||||
|
@ -28,8 +29,10 @@ import im.vector.app.features.settings.devices.v2.signout.SignoutSessionUseCase
|
||||||
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
import im.vector.app.features.settings.devices.v2.verification.CheckIfCurrentSessionCanBeVerifiedUseCase
|
||||||
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
import im.vector.app.test.fakes.FakeActiveSessionHolder
|
||||||
import im.vector.app.test.fakes.FakePendingAuthHandler
|
import im.vector.app.test.fakes.FakePendingAuthHandler
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
import im.vector.app.test.fakes.FakeStringProvider
|
import im.vector.app.test.fakes.FakeStringProvider
|
||||||
import im.vector.app.test.fakes.FakeVerificationService
|
import im.vector.app.test.fakes.FakeVerificationService
|
||||||
|
import im.vector.app.test.fixtures.PusherFixture.aPusher
|
||||||
import im.vector.app.test.test
|
import im.vector.app.test.test
|
||||||
import im.vector.app.test.testDispatcher
|
import im.vector.app.test.testDispatcher
|
||||||
import io.mockk.coEvery
|
import io.mockk.coEvery
|
||||||
|
@ -52,10 +55,8 @@ import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
import org.matrix.android.sdk.api.failure.Failure
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
|
||||||
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
|
||||||
import javax.net.ssl.HttpsURLConnection
|
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
|
||||||
private const val A_SESSION_ID_1 = "session-id-1"
|
private const val A_SESSION_ID_1 = "session-id-1"
|
||||||
|
@ -69,12 +70,16 @@ class SessionOverviewViewModelTest {
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
val mavericksTestRule = MavericksTestRule(testDispatcher = testDispatcher)
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||||
|
|
||||||
private val args = SessionOverviewArgs(
|
private val args = SessionOverviewArgs(
|
||||||
deviceId = A_SESSION_ID_1
|
deviceId = A_SESSION_ID_1
|
||||||
)
|
)
|
||||||
|
private val fakeSession = FakeSession()
|
||||||
|
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>(relaxed = true)
|
||||||
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
private val fakeActiveSessionHolder = FakeActiveSessionHolder()
|
||||||
private val fakeStringProvider = FakeStringProvider()
|
private val fakeStringProvider = FakeStringProvider()
|
||||||
private val getDeviceFullInfoUseCase = mockk<GetDeviceFullInfoUseCase>()
|
|
||||||
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>()
|
private val checkIfCurrentSessionCanBeVerifiedUseCase = mockk<CheckIfCurrentSessionCanBeVerifiedUseCase>()
|
||||||
private val signoutSessionUseCase = mockk<SignoutSessionUseCase>()
|
private val signoutSessionUseCase = mockk<SignoutSessionUseCase>()
|
||||||
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
private val interceptSignoutFlowResponseUseCase = mockk<InterceptSignoutFlowResponseUseCase>()
|
||||||
|
@ -83,6 +88,7 @@ class SessionOverviewViewModelTest {
|
||||||
|
|
||||||
private fun createViewModel() = SessionOverviewViewModel(
|
private fun createViewModel() = SessionOverviewViewModel(
|
||||||
initialState = SessionOverviewViewState(args),
|
initialState = SessionOverviewViewState(args),
|
||||||
|
session = fakeSession,
|
||||||
stringProvider = fakeStringProvider.instance,
|
stringProvider = fakeStringProvider.instance,
|
||||||
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
getDeviceFullInfoUseCase = getDeviceFullInfoUseCase,
|
||||||
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
checkIfCurrentSessionCanBeVerifiedUseCase = checkIfCurrentSessionCanBeVerifiedUseCase,
|
||||||
|
@ -108,8 +114,7 @@ class SessionOverviewViewModelTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given the viewModel has been initialized then viewState is updated with session info and current session verification status`() {
|
fun `given the viewModel has been initialized then viewState is updated with session info`() {
|
||||||
// Given
|
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
val deviceFullInfo = mockk<DeviceFullInfo>()
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
||||||
givenCurrentSessionIsTrusted()
|
givenCurrentSessionIsTrusted()
|
||||||
|
@ -117,12 +122,11 @@ class SessionOverviewViewModelTest {
|
||||||
deviceId = A_SESSION_ID_1,
|
deviceId = A_SESSION_ID_1,
|
||||||
deviceInfo = Success(deviceFullInfo),
|
deviceInfo = Success(deviceFullInfo),
|
||||||
isCurrentSessionTrusted = true,
|
isCurrentSessionTrusted = true,
|
||||||
|
pushers = Loading(),
|
||||||
)
|
)
|
||||||
|
|
||||||
// When
|
|
||||||
val viewModel = createViewModel()
|
val viewModel = createViewModel()
|
||||||
|
|
||||||
// Then
|
|
||||||
viewModel.test()
|
viewModel.test()
|
||||||
.assertLatestState { state -> state == expectedState }
|
.assertLatestState { state -> state == expectedState }
|
||||||
.finish()
|
.finish()
|
||||||
|
@ -199,110 +203,6 @@ class SessionOverviewViewModelTest {
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given another session and no reAuth is needed when handling signout action then signout process is performed`() {
|
|
||||||
// Given
|
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
|
||||||
givenSignoutSuccess(A_SESSION_ID_1)
|
|
||||||
every { refreshDevicesUseCase.execute() } just runs
|
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
|
||||||
givenCurrentSessionIsTrusted()
|
|
||||||
val expectedViewState = SessionOverviewViewState(
|
|
||||||
deviceId = A_SESSION_ID_1,
|
|
||||||
isCurrentSessionTrusted = true,
|
|
||||||
deviceInfo = Success(deviceFullInfo),
|
|
||||||
isLoading = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
// When
|
|
||||||
val viewModel = createViewModel()
|
|
||||||
val viewModelTest = viewModel.test()
|
|
||||||
viewModel.handle(signoutAction)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
viewModelTest
|
|
||||||
.assertStatesChanges(
|
|
||||||
expectedViewState,
|
|
||||||
{ copy(isLoading = true) },
|
|
||||||
{ copy(isLoading = false) }
|
|
||||||
)
|
|
||||||
.assertEvent { it is SessionOverviewViewEvent.SignoutSuccess }
|
|
||||||
.finish()
|
|
||||||
verify {
|
|
||||||
refreshDevicesUseCase.execute()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given another session and server error during signout when handling signout action then signout process is performed`() {
|
|
||||||
// Given
|
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
|
||||||
val serverError = Failure.OtherServerError(errorBody = "", httpCode = HttpsURLConnection.HTTP_UNAUTHORIZED)
|
|
||||||
givenSignoutError(A_SESSION_ID_1, serverError)
|
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
|
||||||
givenCurrentSessionIsTrusted()
|
|
||||||
val expectedViewState = SessionOverviewViewState(
|
|
||||||
deviceId = A_SESSION_ID_1,
|
|
||||||
isCurrentSessionTrusted = true,
|
|
||||||
deviceInfo = Success(deviceFullInfo),
|
|
||||||
isLoading = false,
|
|
||||||
)
|
|
||||||
fakeStringProvider.given(R.string.authentication_error, AUTH_ERROR_MESSAGE)
|
|
||||||
|
|
||||||
// When
|
|
||||||
val viewModel = createViewModel()
|
|
||||||
val viewModelTest = viewModel.test()
|
|
||||||
viewModel.handle(signoutAction)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
viewModelTest
|
|
||||||
.assertStatesChanges(
|
|
||||||
expectedViewState,
|
|
||||||
{ copy(isLoading = true) },
|
|
||||||
{ copy(isLoading = false) }
|
|
||||||
)
|
|
||||||
.assertEvent { it is SessionOverviewViewEvent.SignoutError && it.error.message == AUTH_ERROR_MESSAGE }
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `given another session and unexpected error during signout when handling signout action then signout process is performed`() {
|
|
||||||
// Given
|
|
||||||
val deviceFullInfo = mockk<DeviceFullInfo>()
|
|
||||||
every { deviceFullInfo.isCurrentDevice } returns false
|
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_1) } returns flowOf(deviceFullInfo)
|
|
||||||
val error = Exception()
|
|
||||||
givenSignoutError(A_SESSION_ID_1, error)
|
|
||||||
val signoutAction = SessionOverviewAction.SignoutOtherSession
|
|
||||||
givenCurrentSessionIsTrusted()
|
|
||||||
val expectedViewState = SessionOverviewViewState(
|
|
||||||
deviceId = A_SESSION_ID_1,
|
|
||||||
isCurrentSessionTrusted = true,
|
|
||||||
deviceInfo = Success(deviceFullInfo),
|
|
||||||
isLoading = false,
|
|
||||||
)
|
|
||||||
fakeStringProvider.given(R.string.matrix_error, AN_ERROR_MESSAGE)
|
|
||||||
|
|
||||||
// When
|
|
||||||
val viewModel = createViewModel()
|
|
||||||
val viewModelTest = viewModel.test()
|
|
||||||
viewModel.handle(signoutAction)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
viewModelTest
|
|
||||||
.assertStatesChanges(
|
|
||||||
expectedViewState,
|
|
||||||
{ copy(isLoading = true) },
|
|
||||||
{ copy(isLoading = false) }
|
|
||||||
)
|
|
||||||
.assertEvent { it is SessionOverviewViewEvent.SignoutError && it.error.message == AN_ERROR_MESSAGE }
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given another session and reAuth is needed during signout when handling signout action then requestReAuth is sent and pending auth is stored`() {
|
fun `given another session and reAuth is needed during signout when handling signout action then requestReAuth is sent and pending auth is stored`() {
|
||||||
// Given
|
// Given
|
||||||
|
@ -447,4 +347,30 @@ class SessionOverviewViewModelTest {
|
||||||
every { deviceFullInfo.roomEncryptionTrustLevel } returns RoomEncryptionTrustLevel.Trusted
|
every { deviceFullInfo.roomEncryptionTrustLevel } returns RoomEncryptionTrustLevel.Trusted
|
||||||
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_2) } returns flowOf(deviceFullInfo)
|
every { getDeviceFullInfoUseCase.execute(A_SESSION_ID_2) } returns flowOf(deviceFullInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when viewModel init, then observe pushers and emit to state`() {
|
||||||
|
val pushers = listOf(aPusher(deviceId = A_SESSION_ID_1))
|
||||||
|
fakeSession.pushersService().givenPushersLive(pushers)
|
||||||
|
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
|
||||||
|
viewModel.test()
|
||||||
|
.assertLatestState { state -> state.pushers.invoke() == pushers }
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when handle TogglePushNotifications, then toggle enabled for device pushers`() {
|
||||||
|
val pushers = listOf(
|
||||||
|
aPusher(deviceId = A_SESSION_ID_1, enabled = false),
|
||||||
|
aPusher(deviceId = "another id", enabled = false)
|
||||||
|
)
|
||||||
|
fakeSession.pushersService().givenPushersLive(pushers)
|
||||||
|
|
||||||
|
val viewModel = createViewModel()
|
||||||
|
viewModel.handle(SessionOverviewAction.TogglePushNotifications(A_SESSION_ID_1, true))
|
||||||
|
|
||||||
|
fakeSession.pushersService().verifyOnlyTogglePusherCalled(pushers.first(), true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,30 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import androidx.lifecycle.liveData
|
||||||
|
import io.mockk.Ordering
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.slot
|
import io.mockk.slot
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import org.matrix.android.sdk.api.session.pushers.HttpPusher
|
import org.matrix.android.sdk.api.session.pushers.HttpPusher
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.Pusher
|
||||||
import org.matrix.android.sdk.api.session.pushers.PushersService
|
import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||||
|
|
||||||
class FakePushersService : PushersService by mockk(relaxed = true) {
|
class FakePushersService : PushersService by mockk(relaxed = true) {
|
||||||
|
|
||||||
|
fun givenPushersLive(pushers: List<Pusher>) {
|
||||||
|
every { getPushersLive() } returns liveData { emit(pushers) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyOnlyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
|
||||||
|
coVerify(ordering = Ordering.ALL) {
|
||||||
|
getPushersLive() // verifies only getPushersLive and the following togglePusher was called
|
||||||
|
togglePusher(pusher, enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun verifyEnqueueAddHttpPusher(): HttpPusher {
|
fun verifyEnqueueAddHttpPusher(): HttpPusher {
|
||||||
val httpPusherSlot = slot<HttpPusher>()
|
val httpPusherSlot = slot<HttpPusher>()
|
||||||
verify { enqueueAddHttpPusher(capture(httpPusherSlot)) }
|
verify { enqueueAddHttpPusher(capture(httpPusherSlot)) }
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.pushers.Pusher
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PusherData
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PusherState
|
||||||
|
|
||||||
|
object PusherFixture {
|
||||||
|
|
||||||
|
fun aPusher(
|
||||||
|
pushKey: String = "",
|
||||||
|
kind: String = "",
|
||||||
|
appId: String = "",
|
||||||
|
appDisplayName: String? = "",
|
||||||
|
deviceDisplayName: String? = "",
|
||||||
|
profileTag: String? = null,
|
||||||
|
lang: String? = "",
|
||||||
|
data: PusherData = PusherData("f.o/_matrix/push/v1/notify", ""),
|
||||||
|
enabled: Boolean = true,
|
||||||
|
deviceId: String? = "",
|
||||||
|
state: PusherState = PusherState.REGISTERED,
|
||||||
|
) = Pusher(
|
||||||
|
pushKey,
|
||||||
|
kind,
|
||||||
|
appId,
|
||||||
|
appDisplayName,
|
||||||
|
deviceDisplayName,
|
||||||
|
profileTag,
|
||||||
|
lang,
|
||||||
|
data,
|
||||||
|
enabled,
|
||||||
|
deviceId,
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
}
|
Loading…
Reference in New Issue