Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Makes "Enable Notifications for this session" respond to enabled value in pusher #7281

Merged
merged 51 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
5ced831
Adds push notifications switch
ericdecanini Sep 28, 2022
e1b78b9
Adds functionality to Push notification toggle
ericdecanini Sep 28, 2022
233d07e
Adds DefaultPushersServiceTest for togglePusher
ericdecanini Sep 29, 2022
da80c08
Adds DefaultTogglePusherTaskTest
ericdecanini Sep 29, 2022
670cceb
Adds SessionOverviewViewModelTest for toggling pusher
ericdecanini Sep 29, 2022
71558ed
Hides pusher toggle if there are no pushers of the device
ericdecanini Sep 29, 2022
3cc22c5
Adds changelog file
ericdecanini Sep 29, 2022
66b8ebd
Edits changelog file
ericdecanini Sep 29, 2022
ae2a02c
Fixes copyrights
ericdecanini Oct 3, 2022
47e5491
Unregisters checkedChangelistener in onDetachedFromWindow for switch …
ericdecanini Oct 3, 2022
d91bbfd
Links notification settings toggle to pusher service
ericdecanini Oct 3, 2022
ac33ac1
Adds changelog file
ericdecanini Oct 3, 2022
757ee1a
Adds error handling to VectorSettingsNotificationPreferenceFragment
ericdecanini Oct 4, 2022
121415d
Removes comment in FakePushersService
ericdecanini Oct 4, 2022
225050d
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 5, 2022
c038540
Fixes post merge errors
ericdecanini Oct 5, 2022
2289fde
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 5, 2022
1639b71
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 7, 2022
a1908b0
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 7, 2022
3398646
Fixes imports and improves string name
ericdecanini Oct 7, 2022
9c533bc
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 9, 2022
4f65d5d
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 9, 2022
c3a70b5
Fixes legal copies
ericdecanini Oct 9, 2022
a951a80
Fixes kdoc punctuation
ericdecanini Oct 9, 2022
a9dd794
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
e999aab
Fixes string error
ericdecanini Oct 9, 2022
b4c15d3
Removes unused imports
ericdecanini Oct 9, 2022
c356b8c
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 9, 2022
ddc0021
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
4a8eb51
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 9, 2022
91e902f
Fixes lint errors
ericdecanini Oct 9, 2022
a996c8c
Fixes test errors
ericdecanini Oct 9, 2022
3ecd04f
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
be16bad
Fixes test errors
ericdecanini Oct 9, 2022
f397a62
Fixes error
ericdecanini Oct 9, 2022
6895e14
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
971fe8e
Fixes error
ericdecanini Oct 9, 2022
476aab3
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
e44bca3
Fixes error
ericdecanini Oct 9, 2022
1e3dbba
Fixes error
ericdecanini Oct 9, 2022
49c6240
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
9de170a
Fixes error
ericdecanini Oct 9, 2022
f93674d
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 9, 2022
110600c
Merge branch 'feature/eric/msc3881' into feature/eric/push-toggle-not…
ericdecanini Oct 10, 2022
2f2dc4e
Merge branch 'feature/eric/push-toggle-notifications' into feature/er…
ericdecanini Oct 10, 2022
cb81451
Merge remote-tracking branch 'origin/develop' into feature/eric/notif…
ericdecanini Oct 11, 2022
b02b4a3
Adds lost tests
ericdecanini Oct 11, 2022
c6224b1
Adds PusherEntity migration
ericdecanini Oct 11, 2022
d1562d3
Fixes session overview layout overlap
ericdecanini Oct 11, 2022
992d7d3
Fixes switch being enabled by default
ericdecanini Oct 11, 2022
5a1eac6
Binds entire view to toggle switch
ericdecanini Oct 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/7281.wip
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Links "Enable Notifications for this session" setting to enabled value in pusher
1 change: 1 addition & 0 deletions library/ui-strings/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1663,6 +1663,7 @@
<string name="create_new_room">Create New Room</string>
<string name="create_new_space">Create New Space</string>
<string name="error_no_network">No network. Please check your Internet connection.</string>
<string name="error_check_network">Something went wrong. Please check your network connection and try again.</string>
<string name="change_room_directory_network">"Change network"</string>
<string name="please_wait">"Please wait…"</string>
<string name="updating_your_data">Updating your data…</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo034
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo035
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo036
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo037
import org.matrix.android.sdk.internal.database.migration.MigrateSessionTo038
import org.matrix.android.sdk.internal.util.Normalizer
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import javax.inject.Inject
Expand All @@ -62,7 +63,7 @@ internal class RealmSessionStoreMigration @Inject constructor(
private val normalizer: Normalizer
) : MatrixRealmMigration(
dbName = "Session",
schemaVersion = 37L,
schemaVersion = 38L,
) {
/**
* Forces all RealmSessionStoreMigration instances to be equal.
Expand Down Expand Up @@ -109,5 +110,6 @@ internal class RealmSessionStoreMigration @Inject constructor(
if (oldVersion < 35) MigrateSessionTo035(realm).perform()
if (oldVersion < 36) MigrateSessionTo036(realm).perform()
if (oldVersion < 37) MigrateSessionTo037(realm).perform()
if (oldVersion < 38) MigrateSessionTo038(realm).perform()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 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.migration

import io.realm.DynamicRealm
import org.matrix.android.sdk.internal.database.model.PusherEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator

internal class MigrateSessionTo038(realm: DynamicRealm) : RealmMigrator(realm, 38) {

override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("PusherEntity")
?.addField(PusherEntityFields.ENABLED, Boolean::class.java)
?.addField(PusherEntityFields.DEVICE_ID, String::class.java)
?.transform { obj -> obj.set(PusherEntityFields.ENABLED, true) }
}
}
13 changes: 13 additions & 0 deletions vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.session.pushers.HttpPusher
import org.matrix.android.sdk.api.session.pushers.Pusher
import java.util.UUID
import javax.inject.Inject
import kotlin.math.abs
Expand Down Expand Up @@ -90,6 +91,18 @@ class PushersManager @Inject constructor(
)
}

fun getPusherForCurrentSession(): Pusher? {
val session = activeSessionHolder.getSafeActiveSession() ?: return null
val deviceId = session.sessionParams.deviceId
return session.pushersService().getPushers().firstOrNull { it.deviceId == deviceId }
}

suspend fun togglePusherForCurrentSession(enable: Boolean) {
val session = activeSessionHolder.getSafeActiveSession() ?: return
val pusher = getPusherForCurrentSession() ?: return
session.pushersService().togglePusher(pusher, enable)
}

suspend fun unregisterEmailPusher(email: String) {
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.pushersService().removeEmailPusher(email)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class SessionOverviewEntrySwitchView @JvmOverloads constructor(
setTitle(it)
setDescription(it)
setSwitchedEnabled(it)
setClickListener()
}
}

Expand All @@ -67,10 +68,16 @@ class SessionOverviewEntrySwitchView @JvmOverloads constructor(
}

private fun setSwitchedEnabled(typedArray: TypedArray) {
val enabled = typedArray.getBoolean(R.styleable.SessionOverviewEntrySwitchView_sessionOverviewEntrySwitchEnabled, true)
val enabled = typedArray.getBoolean(R.styleable.SessionOverviewEntrySwitchView_sessionOverviewEntrySwitchEnabled, false)
binding.sessionsOverviewEntrySwitch.isChecked = enabled
}

private fun setClickListener() {
binding.root.setOnClickListener {
setChecked(!binding.sessionsOverviewEntrySwitch.isChecked)
}
}

fun setChecked(checked: Boolean) {
binding.sessionsOverviewEntrySwitch.isChecked = checked
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsBaseFragment
import im.vector.app.features.settings.VectorSettingsFragmentInteractionListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session
Expand Down Expand Up @@ -104,6 +105,10 @@ class VectorSettingsNotificationPreferenceFragment :
}

findPreference<SwitchPreference>(VectorPreferences.SETTINGS_ENABLE_THIS_DEVICE_PREFERENCE_KEY)?.let {
pushersManager.getPusherForCurrentSession()?.let { pusher ->
it.isChecked = pusher.enabled
}

it.setTransactionalSwitchChangeListener(lifecycleScope) { isChecked ->
if (isChecked) {
unifiedPushHelper.register(requireActivity()) {
Expand All @@ -117,6 +122,16 @@ class VectorSettingsNotificationPreferenceFragment :
}
findPreference<VectorPreference>(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY)
?.summary = unifiedPushHelper.getCurrentDistributorName()
lifecycleScope.launch {
val result = runCatching {
pushersManager.togglePusherForCurrentSession(true)
}

result.exceptionOrNull()?.let { _ ->
Toast.makeText(context, R.string.error_check_network, Toast.LENGTH_SHORT).show()
it.isChecked = false
}
}
}
} else {
unifiedPushHelper.unregister(pushersManager)
Expand Down
2 changes: 1 addition & 1 deletion vector/src/main/res/layout/fragment_session_overview.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/sessionOverviewEntryDetails"
app:layout_constraintTop_toBottomOf="@id/sessionOverviewPushNotifications"
app:layout_constraintWidth="wrap_content" />

</androidx.constraintlayout.widget.ConstraintLayout>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,12 @@ 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.CredentialsFixture
import im.vector.app.test.fixtures.CryptoDeviceInfoFixture.aCryptoDeviceInfo
import im.vector.app.test.fixtures.PusherFixture
import im.vector.app.test.fixtures.SessionParamsFixture
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
Expand Down Expand Up @@ -82,4 +86,34 @@ class PushersManagerTest {
val httpPusher = pushersService.verifyEnqueueAddHttpPusher()
httpPusher shouldBeEqualTo expectedHttpPusher
}

@Test
fun `when getPusherForCurrentSession, then return pusher`() {
val deviceId = "device_id"
val sessionParams = SessionParamsFixture.aSessionParams(
credentials = CredentialsFixture.aCredentials(deviceId = deviceId)
)
session.givenSessionParams(sessionParams)
val expectedPusher = PusherFixture.aPusher(deviceId = deviceId)
pushersService.givenGetPushers(listOf(expectedPusher))

val pusher = pushersManager.getPusherForCurrentSession()

pusher shouldBeEqualTo expectedPusher
}

@Test
fun `when togglePusherForCurrentSession, then do service toggle pusher`() = runTest {
val deviceId = "device_id"
val sessionParams = SessionParamsFixture.aSessionParams(
credentials = CredentialsFixture.aCredentials(deviceId = deviceId)
)
session.givenSessionParams(sessionParams)
val pusher = PusherFixture.aPusher(deviceId = deviceId)
pushersService.givenGetPushers(listOf(pusher))

pushersManager.togglePusherForCurrentSession(true)

pushersService.verifyOnlyGetPushersAndTogglePusherCalled(pusher, true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
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.RefreshDevicesUseCase
import im.vector.app.features.settings.devices.v2.signout.InterceptSignoutFlowResponseUseCase
Expand Down Expand Up @@ -55,8 +56,10 @@ import org.junit.Test
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
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.uia.DefaultBaseAuth
import javax.net.ssl.HttpsURLConnection
import kotlin.coroutines.Continuation

private const val A_SESSION_ID_1 = "session-id-1"
Expand Down Expand Up @@ -203,6 +206,113 @@ class SessionOverviewViewModelTest {
.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,
pushers = Loading(),
)

// 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,
pushers = Loading(),
)
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,
pushers = Loading(),
)
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
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason to having removed all tests on SignoutAction ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think they were lost during a merge conflict. Good catch

fun `given another session and reAuth is needed during signout when handling signout action then requestReAuth is sent and pending auth is stored`() {
// Given
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,21 @@ import org.matrix.android.sdk.api.session.pushers.PushersService

class FakePushersService : PushersService by mockk(relaxed = true) {

fun givenGetPushers(pushers: List<Pusher>) {
every { getPushers() } returns pushers
}

fun givenPushersLive(pushers: List<Pusher>) {
every { getPushersLive() } returns liveData { emit(pushers) }
}

fun verifyOnlyGetPushersAndTogglePusherCalled(pusher: Pusher, enable: Boolean) {
coVerify(ordering = Ordering.ALL) {
getPushers()
togglePusher(pusher, enable)
}
}

fun verifyOnlyTogglePusherCalled(pusher: Pusher, enable: Boolean) {
coVerify(ordering = Ordering.ALL) {
getPushersLive() // verifies only getPushersLive and the following togglePusher was called
Expand Down
Loading