Skip to content

Commit d5cb966

Browse files
authored
Merge branch 'develop' into mo/feat/improved-pipeline
2 parents e164fa4 + 5d8037e commit d5cb966

File tree

30 files changed

+711
-38
lines changed

30 files changed

+711
-38
lines changed

.github/workflows/cherry-pick-pr-to-newer-release-cycle.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
uses: actions/checkout@v4
3939
with:
4040
fetch-depth: 0
41+
token: ${{ secrets.ANDROID_BOB_GH_TOKEN }}
4142

4243
- name: Cherry pick to `develop`
4344
uses: wireapp/[email protected]

app/src/main/kotlin/com/wire/android/di/accountScoped/CellsModule.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ import com.wire.kalium.cells.domain.usecase.CreateFolderUseCase
2626
import com.wire.kalium.cells.domain.usecase.DeleteCellAssetUseCase
2727
import com.wire.kalium.cells.domain.usecase.DownloadCellFileUseCase
2828
import com.wire.kalium.cells.domain.usecase.GetFoldersUseCase
29-
import com.wire.kalium.cells.domain.usecase.GetPaginatedNodesUseCase
3029
import com.wire.kalium.cells.domain.usecase.GetPaginatedFilesFlowUseCase
30+
import com.wire.kalium.cells.domain.usecase.GetPaginatedNodesUseCase
3131
import com.wire.kalium.cells.domain.usecase.MoveNodeUseCase
3232
import com.wire.kalium.cells.domain.usecase.ObserveAttachmentDraftsUseCase
3333
import com.wire.kalium.cells.domain.usecase.PublishAttachmentsUseCase
3434
import com.wire.kalium.cells.domain.usecase.RefreshCellAssetStateUseCase
3535
import com.wire.kalium.cells.domain.usecase.RemoveAttachmentDraftUseCase
3636
import com.wire.kalium.cells.domain.usecase.RemoveAttachmentDraftsUseCase
37+
import com.wire.kalium.cells.domain.usecase.RenameNodeUseCase
3738
import com.wire.kalium.cells.domain.usecase.RestoreNodeFromRecycleBinUseCase
3839
import com.wire.kalium.cells.domain.usecase.RetryAttachmentUploadUseCase
3940
import com.wire.kalium.cells.domain.usecase.SetWireCellForConversationUseCase
@@ -141,4 +142,9 @@ class CellsModule {
141142
@Provides
142143
fun provideRestoreNodeFromRecycleBinUseCase(cellsScope: CellsScope): RestoreNodeFromRecycleBinUseCase =
143144
cellsScope.restoreNodeFromRecycleBin
145+
146+
@ViewModelScoped
147+
@Provides
148+
fun provideRenameNodeUseCase(cellsScope: CellsScope): RenameNodeUseCase =
149+
cellsScope.renameNodeUseCase
144150
}

app/src/main/kotlin/com/wire/android/navigation/WireMainNavGraph.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.wire.android.feature.cells.ui.destinations.CreateFolderScreenDestinat
2626
import com.wire.android.feature.cells.ui.destinations.MoveToFolderScreenDestination
2727
import com.wire.android.feature.cells.ui.destinations.PublicLinkScreenDestination
2828
import com.wire.android.feature.cells.ui.destinations.RecycleBinScreenDestination
29+
import com.wire.android.feature.cells.ui.destinations.RenameNodeScreenDestination
2930
import com.wire.android.feature.sketch.destinations.DrawingCanvasScreenDestination
3031
import com.wire.android.ui.NavGraphs
3132

@@ -40,6 +41,7 @@ object WireMainNavGraph : NavGraphSpec {
4041
.plus(CreateFolderScreenDestination)
4142
.plus(MoveToFolderScreenDestination)
4243
.plus(RecycleBinScreenDestination)
44+
.plus(RenameNodeScreenDestination)
4345
override val destinationsByRoute = destinations.associateBy { it.route }
4446
override val nestedNavGraphs = NavGraphs.wireRoot.nestedNavGraphs
4547
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2025 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*/
18+
package com.wire.android.notification.broadcastreceivers
19+
20+
import android.content.BroadcastReceiver
21+
import android.content.Context
22+
import android.content.Intent
23+
import com.wire.android.appLogger
24+
import kotlinx.coroutines.CoroutineScope
25+
import kotlinx.coroutines.Dispatchers
26+
import kotlinx.coroutines.SupervisorJob
27+
import kotlinx.coroutines.launch
28+
import kotlinx.coroutines.withTimeoutOrNull
29+
import kotlin.time.Duration.Companion.seconds
30+
31+
/**
32+
* A [BroadcastReceiver] that cancels execution if it takes too long to avoid ANR.
33+
*/
34+
abstract class CoroutineReceiver : BroadcastReceiver() {
35+
36+
private companion object {
37+
// Maximum duration for which the receiver can be executed before ANR is triggered.
38+
private val MaxDuration = 9.seconds
39+
}
40+
41+
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
42+
43+
override fun onReceive(context: Context, intent: Intent?) {
44+
if (intent == null) return
45+
46+
val result = goAsync()
47+
48+
scope.launch {
49+
try {
50+
withTimeoutOrNull(MaxDuration) {
51+
receive(context, intent)
52+
} ?: onTimeout(
53+
context = context,
54+
intent = intent,
55+
exception = IllegalStateException(timeoutMessage())
56+
)
57+
} finally {
58+
result.finish()
59+
}
60+
}
61+
}
62+
63+
private fun timeoutMessage() = "${this::class.java.simpleName} has been suspended for more than $MaxDuration"
64+
65+
protected abstract suspend fun receive(context: Context, intent: Intent)
66+
67+
protected open fun onTimeout(context: Context, intent: Intent, exception: Exception) {
68+
appLogger.e(timeoutMessage(), exception)
69+
}
70+
}

app/src/main/kotlin/com/wire/android/notification/broadcastreceivers/NotificationReplyReceiver.kt

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,26 @@
1818

1919
package com.wire.android.notification.broadcastreceivers
2020

21-
import android.content.BroadcastReceiver
2221
import android.content.Context
2322
import android.content.Intent
23+
import android.widget.Toast
2424
import androidx.core.app.RemoteInput
25+
import com.wire.android.R
2526
import com.wire.android.di.KaliumCoreLogic
2627
import com.wire.android.di.NoSession
2728
import com.wire.android.notification.MessageNotificationManager
2829
import com.wire.android.notification.NotificationConstants
2930
import com.wire.android.util.dispatchers.DispatcherProvider
31+
import com.wire.kalium.common.functional.fold
3032
import com.wire.kalium.logic.CoreLogic
3133
import com.wire.kalium.logic.data.id.QualifiedID
3234
import com.wire.kalium.logic.data.id.QualifiedIdMapper
33-
import com.wire.kalium.common.functional.fold
3435
import dagger.hilt.android.AndroidEntryPoint
35-
import kotlinx.coroutines.CoroutineScope
36-
import kotlinx.coroutines.launch
3736
import kotlinx.datetime.Clock
3837
import javax.inject.Inject
3938

4039
@AndroidEntryPoint
41-
class NotificationReplyReceiver : BroadcastReceiver() { // requires zero argument constructor
40+
class NotificationReplyReceiver : CoroutineReceiver() { // requires zero argument constructor
4241

4342
@Inject
4443
@KaliumCoreLogic
@@ -51,7 +50,7 @@ class NotificationReplyReceiver : BroadcastReceiver() { // requires zero argumen
5150
@NoSession
5251
lateinit var qualifiedIdMapper: QualifiedIdMapper
5352

54-
override fun onReceive(context: Context, intent: Intent) {
53+
override suspend fun receive(context: Context, intent: Intent) {
5554
val remoteInput = RemoteInput.getResultsFromIntent(intent)
5655
val conversationId: String? = intent.getStringExtra(EXTRA_CONVERSATION_ID)
5756
val userId: String? = intent.getStringExtra(EXTRA_USER_ID)
@@ -62,26 +61,35 @@ class NotificationReplyReceiver : BroadcastReceiver() { // requires zero argumen
6261
val qualifiedConversationId = qualifiedIdMapper.fromStringToQualifiedID(conversationId)
6362

6463
with(coreLogic.getSessionScope(qualifiedUserId)) {
65-
// TODO better to move dispatcher logic into UseCase
66-
CoroutineScope(coroutineContext + dispatcherProvider.io()).launch {
67-
launch {
68-
messages.sendTextMessage(qualifiedConversationId, replyText)
69-
.fold(
70-
{ updateNotification(context, conversationId, qualifiedUserId, null) },
71-
{ updateNotification(context, conversationId, qualifiedUserId, replyText) }
72-
)
73-
}
74-
launch {
75-
conversations.updateConversationReadDateUseCase(
76-
qualifiedConversationId,
77-
Clock.System.now()
64+
syncExecutor.request {
65+
messages.sendTextMessage(qualifiedConversationId, replyText)
66+
.fold(
67+
{ updateNotification(context, conversationId, qualifiedUserId, null) },
68+
{ updateNotification(context, conversationId, qualifiedUserId, replyText) }
7869
)
79-
}
70+
conversations.updateConversationReadDateUseCase(
71+
qualifiedConversationId,
72+
Clock.System.now()
73+
)
8074
}
8175
}
8276
}
8377
}
8478

79+
override fun onTimeout(context: Context, intent: Intent, exception: Exception) {
80+
super.onTimeout(context, intent, exception)
81+
82+
val conversationId: String? = intent.getStringExtra(EXTRA_CONVERSATION_ID)
83+
val userId: String? = intent.getStringExtra(EXTRA_USER_ID)
84+
85+
if (conversationId != null && userId != null) {
86+
val qualifiedUserId = qualifiedIdMapper.fromStringToQualifiedID(userId)
87+
updateNotification(context, conversationId, qualifiedUserId, null)
88+
}
89+
90+
Toast.makeText(context, R.string.label_general_error, Toast.LENGTH_SHORT).show()
91+
}
92+
8593
private fun updateNotification(context: Context, conversationId: String, userId: QualifiedID, replyText: String?) =
8694
MessageNotificationManager.updateNotificationAfterQuickReply(context, conversationId, userId, replyText)
8795

app/src/main/kotlin/com/wire/android/ui/authentication/create/common/handle/UsernameTextField.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
2929
import androidx.compose.ui.res.painterResource
3030
import androidx.compose.ui.res.stringResource
3131
import com.wire.android.R
32-
import com.wire.android.ui.common.ShakeAnimation
32+
import com.wire.android.ui.common.animation.ShakeAnimation
3333
import com.wire.android.ui.common.error.CoreFailureErrorDialog
3434
import com.wire.android.ui.common.textfield.DefaultEmailDone
3535
import com.wire.android.ui.common.textfield.WireTextField

app/src/main/kotlin/com/wire/android/ui/common/groupname/GroupConversationNameComponent.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ import androidx.compose.ui.text.input.ImeAction
5050
import androidx.compose.ui.text.input.KeyboardType
5151
import com.wire.android.R
5252
import com.wire.android.ui.common.Icon
53-
import com.wire.android.ui.common.ShakeAnimation
53+
import com.wire.android.ui.common.animation.ShakeAnimation
5454
import com.wire.android.ui.common.button.WireButtonState
5555
import com.wire.android.ui.common.button.WirePrimaryButton
5656
import com.wire.android.ui.common.dimensions

app/src/main/kotlin/com/wire/android/ui/home/conversations/folder/NewConversationFolderScreen.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import com.ramcosta.composedestinations.spec.DestinationStyle
4141
import com.wire.android.R
4242
import com.wire.android.navigation.Navigator
4343
import com.wire.android.navigation.annotation.app.WireDestination
44-
import com.wire.android.ui.common.ShakeAnimation
44+
import com.wire.android.ui.common.animation.ShakeAnimation
4545
import com.wire.android.ui.common.button.WireButtonState.Default
4646
import com.wire.android.ui.common.button.WireButtonState.Disabled
4747
import com.wire.android.ui.common.button.WirePrimaryButton

app/src/main/kotlin/com/wire/android/ui/home/settings/account/displayname/ChangeDisplayNameScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@ import androidx.hilt.navigation.compose.hiltViewModel
4444
import com.ramcosta.composedestinations.result.ResultBackNavigator
4545
import com.ramcosta.composedestinations.spec.DestinationStyle
4646
import com.wire.android.R
47+
import com.wire.android.model.DisplayNameState
4748
import com.wire.android.navigation.Navigator
4849
import com.wire.android.navigation.annotation.app.WireDestination
4950
import com.wire.android.ui.common.Icon
50-
import com.wire.android.ui.common.ShakeAnimation
51+
import com.wire.android.ui.common.animation.ShakeAnimation
5152
import com.wire.android.ui.common.button.WireButtonState.Default
5253
import com.wire.android.ui.common.button.WireButtonState.Disabled
5354
import com.wire.android.ui.common.button.WirePrimaryButton

app/src/main/kotlin/com/wire/android/ui/home/settings/account/displayname/ChangeDisplayNameViewModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.runtime.mutableStateOf
2525
import androidx.compose.runtime.setValue
2626
import androidx.lifecycle.ViewModel
2727
import androidx.lifecycle.viewModelScope
28+
import com.wire.android.model.DisplayNameState
2829
import com.wire.android.ui.common.textfield.textAsFlow
2930
import com.wire.kalium.logic.feature.user.DisplayNameUpdateResult
3031
import com.wire.kalium.logic.feature.user.GetSelfUserUseCase

0 commit comments

Comments
 (0)