Skip to content

Commit 04e5031

Browse files
authored
Notifications: simplify the flow by removing persistence (#2924)
* Notifications: simplify the flow by removing persistence. * Bump of minSdk to `24` (Android 7). * Add migration to remove `notification.bin` file
1 parent 17678ad commit 04e5031

File tree

62 files changed

+2017
-2607
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2017
-2607
lines changed

appconfig/src/main/kotlin/io/element/android/appconfig/NotificationConfig.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ object NotificationConfig {
2020
// TODO EAx Implement and set to true at some point
2121
const val SUPPORT_MARK_AS_READ_ACTION = false
2222

23+
// TODO EAx Implement and set to true at some point
24+
const val SUPPORT_JOIN_DECLINE_INVITE = false
25+
2326
// TODO EAx Implement and set to true at some point
2427
const val SUPPORT_QUICK_REPLY_ACTION = false
2528
}

changelog.d/2924.misc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Simplify notifications by removing the custom persistence layer.
2+
3+
Bump minSdk to 24 (Android 7).

features/call/src/main/kotlin/io/element/android/features/call/CallForegroundService.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,10 @@ class CallForegroundService : Service() {
7272
startForeground(1, notification)
7373
}
7474

75-
@Suppress("DEPRECATION")
7675
override fun onDestroy() {
7776
super.onDestroy()
7877

79-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
80-
stopForeground(STOP_FOREGROUND_REMOVE)
81-
} else {
82-
stopForeground(true)
83-
}
78+
stopForeground(STOP_FOREGROUND_REMOVE)
8479
}
8580

8681
override fun onBind(intent: Intent?): IBinder? {

features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class AcceptDeclineInvitePresenter @Inject constructor(
112112
trigger = JoinedRoom.Trigger.Invite,
113113
)
114114
.onSuccess {
115-
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
115+
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
116116
}
117117
.map { roomId }
118118
}
@@ -122,7 +122,7 @@ class AcceptDeclineInvitePresenter @Inject constructor(
122122
suspend {
123123
client.getRoom(roomId)?.use {
124124
it.leave().getOrThrow()
125-
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId, doRender = true)
125+
notificationDrawerManager.clearMembershipNotificationForRoom(client.sessionId, roomId)
126126
}
127127
roomId
128128
}.runCatchingUpdatingState(declinedAction)

features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@
1616

1717
package io.element.android.features.login.impl.oidc.webview
1818

19-
import android.annotation.TargetApi
20-
import android.os.Build
2119
import android.webkit.WebResourceRequest
2220
import android.webkit.WebView
2321
import android.webkit.WebViewClient
2422

2523
class OidcWebViewClient(
2624
private val eventListener: WebViewEventListener,
2725
) : WebViewClient() {
28-
@TargetApi(Build.VERSION_CODES.N)
2926
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
3027
return shouldOverrideUrl(request.url.toString())
3128
}
@@ -36,7 +33,6 @@ class OidcWebViewClient(
3633
}
3734

3835
private fun shouldOverrideUrl(url: String): Boolean {
39-
// Timber.d("shouldOverrideUrl: $url")
4036
return eventListener.shouldOverrideUrlLoading(url)
4137
}
4238
}

features/migration/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies {
4040
testImplementation(libs.test.junit)
4141
testImplementation(libs.coroutines.test)
4242
testImplementation(libs.molecule.runtime)
43+
testImplementation(libs.test.robolectric)
4344
testImplementation(libs.test.truth)
4445
testImplementation(libs.test.turbine)
4546
testImplementation(projects.libraries.sessionStorage.implMemory)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.migration.impl.migrations
18+
19+
import android.content.Context
20+
import com.squareup.anvil.annotations.ContributesMultibinding
21+
import io.element.android.libraries.di.AppScope
22+
import io.element.android.libraries.di.ApplicationContext
23+
import javax.inject.Inject
24+
25+
/**
26+
* Remove notifications.bin file, used to store notification data locally.
27+
*/
28+
@ContributesMultibinding(AppScope::class)
29+
class AppMigration04 @Inject constructor(
30+
@ApplicationContext private val context: Context,
31+
) : AppMigration {
32+
companion object {
33+
internal const val NOTIFICATION_FILE_NAME = "notifications.bin"
34+
}
35+
override val order: Int = 4
36+
37+
override suspend fun migrate() {
38+
runCatching { context.getDatabasePath(NOTIFICATION_FILE_NAME).delete() }
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.features.migration.impl.migrations
18+
19+
import androidx.test.platform.app.InstrumentationRegistry
20+
import com.google.common.truth.Truth.assertThat
21+
import kotlinx.coroutines.test.runTest
22+
import org.junit.Test
23+
import org.junit.runner.RunWith
24+
import org.robolectric.RobolectricTestRunner
25+
26+
@RunWith(RobolectricTestRunner::class)
27+
class AppMigration04Test {
28+
@Test
29+
fun `test migration`() = runTest {
30+
val context = InstrumentationRegistry.getInstrumentation().context
31+
32+
// Create fake temporary file at the path to be deleted
33+
val file = context.getDatabasePath(AppMigration04.NOTIFICATION_FILE_NAME)
34+
file.parentFile?.mkdirs()
35+
file.createNewFile()
36+
assertThat(file.exists()).isTrue()
37+
38+
val migration = AppMigration04(context)
39+
40+
migration.migrate()
41+
42+
// Check that the file has been deleted
43+
assertThat(file.exists()).isFalse()
44+
}
45+
}

features/preferences/impl/src/main/kotlin/io/element/android/features/preferences/impl/notifications/SystemNotificationsEnabledProvider.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,9 @@
1616

1717
package io.element.android.features.preferences.impl.notifications
1818

19-
import android.content.Context
2019
import androidx.core.app.NotificationManagerCompat
2120
import com.squareup.anvil.annotations.ContributesBinding
2221
import io.element.android.libraries.di.AppScope
23-
import io.element.android.libraries.di.ApplicationContext
2422
import io.element.android.libraries.di.SingleIn
2523
import javax.inject.Inject
2624

@@ -31,9 +29,9 @@ interface SystemNotificationsEnabledProvider {
3129
@SingleIn(AppScope::class)
3230
@ContributesBinding(AppScope::class)
3331
class DefaultSystemNotificationsEnabledProvider @Inject constructor(
34-
@ApplicationContext private val context: Context,
32+
private val notificationManager: NotificationManagerCompat,
3533
) : SystemNotificationsEnabledProvider {
3634
override fun notificationsEnabled(): Boolean {
37-
return NotificationManagerCompat.from(context).areNotificationsEnabled()
35+
return notificationManager.areNotificationsEnabled()
3836
}
3937
}

libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessorTest.kt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,16 @@ class AndroidMediaPreProcessorTest {
5858
val data = result.getOrThrow()
5959
assertThat(data.file.path).endsWith("image.png")
6060
val info = data as MediaUploadInfo.Image
61-
// Computing thumbnailFile is failing with Robolectric
62-
assertThat(info.thumbnailFile).isNull()
61+
assertThat(info.thumbnailFile).isNotNull()
6362
assertThat(info.imageInfo).isEqualTo(
6463
ImageInfo(
6564
height = 1_178,
6665
width = 1_818,
6766
mimetype = MimeTypes.Png,
6867
size = 114_867,
69-
thumbnailInfo = null,
68+
ThumbnailInfo(height = 294, width = 454, mimetype = "image/jpeg", size = 4567),
7069
thumbnailSource = null,
71-
blurhash = null,
70+
blurhash = "K13]7q%zWC00R4of%\$baad"
7271
)
7372
)
7473
assertThat(file.exists()).isTrue()
@@ -88,7 +87,6 @@ class AndroidMediaPreProcessorTest {
8887
val data = result.getOrThrow()
8988
assertThat(data.file.path).endsWith("image.png")
9089
val info = data as MediaUploadInfo.Image
91-
// Computing thumbnailFile is failing with Robolectric
9290
assertThat(info.thumbnailFile).isNull()
9391
assertThat(info.imageInfo).isEqualTo(
9492
ImageInfo(

libraries/push/api/src/main/kotlin/io/element/android/libraries/push/api/notifications/NotificationDrawerManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.SessionId
2121

2222
interface NotificationDrawerManager {
2323
fun clearMembershipNotificationForSession(sessionId: SessionId)
24-
fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId, doRender: Boolean)
24+
fun clearMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId)
2525
}

libraries/push/impl/build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dependencies {
5252
implementation(projects.libraries.network)
5353
implementation(projects.libraries.matrix.api)
5454
implementation(projects.libraries.matrixui)
55+
implementation(projects.libraries.preferences.api)
5556
implementation(projects.libraries.uiStrings)
5657
implementation(projects.libraries.troubleshoot.api)
5758
api(projects.libraries.pushproviders.api)
@@ -63,8 +64,8 @@ dependencies {
6364
implementation(projects.services.toolbox.api)
6465

6566
testImplementation(libs.test.junit)
66-
testImplementation(libs.test.robolectric)
6767
testImplementation(libs.test.mockk)
68+
testImplementation(libs.test.robolectric)
6869
testImplementation(libs.test.truth)
6970
testImplementation(libs.test.turbine)
7071
testImplementation(libs.coil.test)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.push.impl.di
18+
19+
import android.content.Context
20+
import androidx.core.app.NotificationManagerCompat
21+
import com.squareup.anvil.annotations.ContributesTo
22+
import dagger.Module
23+
import dagger.Provides
24+
import io.element.android.libraries.di.AppScope
25+
import io.element.android.libraries.di.ApplicationContext
26+
27+
@Module
28+
@ContributesTo(AppScope::class)
29+
object PushModule {
30+
@Provides
31+
fun provideNotificationCompatManager(@ApplicationContext context: Context): NotificationManagerCompat {
32+
return NotificationManagerCompat.from(context)
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright (c) 2024 New Vector Ltd
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.element.android.libraries.push.impl.notifications
18+
19+
import android.service.notification.StatusBarNotification
20+
import androidx.core.app.NotificationManagerCompat
21+
import com.squareup.anvil.annotations.ContributesBinding
22+
import io.element.android.libraries.di.AppScope
23+
import io.element.android.libraries.matrix.api.core.RoomId
24+
import io.element.android.libraries.matrix.api.core.SessionId
25+
import javax.inject.Inject
26+
27+
interface ActiveNotificationsProvider {
28+
fun getAllNotifications(): List<StatusBarNotification>
29+
fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification>
30+
fun getNotificationsForSession(sessionId: SessionId): List<StatusBarNotification>
31+
fun getMembershipNotificationForSession(sessionId: SessionId): List<StatusBarNotification>
32+
fun getMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification>
33+
fun getSummaryNotification(sessionId: SessionId): StatusBarNotification?
34+
fun count(sessionId: SessionId): Int
35+
}
36+
37+
@ContributesBinding(AppScope::class)
38+
class DefaultActiveNotificationsProvider @Inject constructor(
39+
private val notificationManager: NotificationManagerCompat,
40+
private val notificationIdProvider: NotificationIdProvider,
41+
) : ActiveNotificationsProvider {
42+
override fun getAllNotifications(): List<StatusBarNotification> {
43+
return notificationManager.activeNotifications
44+
}
45+
46+
override fun getNotificationsForSession(sessionId: SessionId): List<StatusBarNotification> {
47+
return notificationManager.activeNotifications.filter { it.groupKey == sessionId.value }
48+
}
49+
50+
override fun getMembershipNotificationForSession(sessionId: SessionId): List<StatusBarNotification> {
51+
val notificationId = notificationIdProvider.getRoomInvitationNotificationId(sessionId)
52+
return getNotificationsForSession(sessionId).filter { it.id == notificationId }
53+
}
54+
55+
override fun getMessageNotificationsForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification> {
56+
val notificationId = notificationIdProvider.getRoomMessagesNotificationId(sessionId)
57+
return getNotificationsForSession(sessionId).filter { it.id == notificationId && it.tag == roomId.value }
58+
}
59+
60+
override fun getMembershipNotificationForRoom(sessionId: SessionId, roomId: RoomId): List<StatusBarNotification> {
61+
val notificationId = notificationIdProvider.getRoomInvitationNotificationId(sessionId)
62+
return getNotificationsForSession(sessionId).filter { it.id == notificationId && it.tag == roomId.value }
63+
}
64+
65+
override fun getSummaryNotification(sessionId: SessionId): StatusBarNotification? {
66+
val summaryId = notificationIdProvider.getSummaryNotificationId(sessionId)
67+
return getNotificationsForSession(sessionId).find { it.id == summaryId }
68+
}
69+
70+
override fun count(sessionId: SessionId): Int {
71+
return getNotificationsForSession(sessionId).size
72+
}
73+
}

0 commit comments

Comments
 (0)