Skip to content

Commit 98621a9

Browse files
committed
Merge branch 'develop' into mo/chore/set-compile-sdk-to-35
# Conflicts: # kalium
2 parents 7d6e984 + 08e75c7 commit 98621a9

File tree

106 files changed

+14460
-1344
lines changed

Some content is hidden

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

106 files changed

+14460
-1344
lines changed

.github/actions/deploy-to-s3/action.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ runs:
5555
file-path: ${{ steps.path.outputs.apk_full_path }}
5656
output-file-url: 'false'
5757
public: false
58-
- name: Show URL
59-
if: github.event.pull_request.number != ''
60-
shell: bash
61-
env:
62-
EVENT_FILE_PATH: artifacts/Event File/event.json
63-
GITHUB_USER: ${{ github.actor }}
64-
GITHUB_TOKEN: ${{ inputs.github-token }}
65-
run: |
66-
gh pr comment "${{ github.event.pull_request.number }}" --body "Built [wire-android-${{ inputs.build-flavour }}-${{ inputs.build-variant }}-pr-${{ github.event.pull_request.number }}.apk](${{ steps.upload.outputs.file-url }}) is available for download"
58+
# - name: Show URL
59+
# if: github.event.pull_request.number != ''
60+
# shell: bash
61+
# env:
62+
# EVENT_FILE_PATH: artifacts/Event File/event.json
63+
# GITHUB_USER: ${{ github.actor }}
64+
# GITHUB_TOKEN: ${{ inputs.github-token }}
65+
# run: |
66+
# gh pr comment "${{ github.event.pull_request.number }}" --body "Built [wire-android-${{ inputs.build-flavour }}-${{ inputs.build-variant }}-pr-${{ github.event.pull_request.number }}.apk](${{ steps.upload.outputs.file-url }}) is available for download"
6767
- name: Extract version and version code from APK filename
6868
shell: bash
6969
run: |

.github/dependabot.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ updates:
1010
directory: "/"
1111
schedule:
1212
interval: "daily"
13-
reviewers:
14-
- "wireapp/android"
1513
commit-message:
1614
prefix: "chore(deps): [WPB-9777] "
1715

@@ -20,7 +18,5 @@ updates:
2018
directory: "/"
2119
schedule:
2220
interval: "weekly"
23-
reviewers:
24-
- "wireapp/android"
2521
commit-message:
2622
prefix: "chore(deps): [WPB-9777] "

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@wireapp/android

app/src/main/kotlin/com/wire/android/di/KaliumConfigsModule.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ class KaliumConfigsModule {
6060
isWebSocketEnabledByDefault = isWebsocketEnabledByDefault(context),
6161
certPinningConfig = BuildConfig.CERTIFICATE_PINNING_CONFIG,
6262
maxRemoteSearchResultCount = BuildConfig.MAX_REMOTE_SEARCH_RESULT_COUNT,
63-
limitTeamMembersFetchDuringSlowSync = BuildConfig.LIMIT_TEAM_MEMBERS_FETCH_DURING_SLOW_SYNC,
64-
enableAsyncNotifications = BuildConfig.ENABLE_ASYNC_NOTIFICATIONS
63+
limitTeamMembersFetchDuringSlowSync = BuildConfig.LIMIT_TEAM_MEMBERS_FETCH_DURING_SLOW_SYNC
6564
)
6665
}
6766
}

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ package com.wire.android.di.accountScoped
2020
import com.wire.android.di.CurrentAccount
2121
import com.wire.android.di.KaliumCoreLogic
2222
import com.wire.kalium.logic.CoreLogic
23+
import com.wire.kalium.logic.data.conversation.FetchConversationUseCase
24+
import com.wire.kalium.logic.data.conversation.ResetMLSConversationUseCase
2325
import com.wire.kalium.logic.data.user.UserId
2426
import com.wire.kalium.logic.feature.conversation.AddMemberToConversationUseCase
2527
import com.wire.kalium.logic.feature.conversation.AddServiceToConversationUseCase
@@ -59,6 +61,7 @@ import com.wire.kalium.logic.feature.conversation.UpdateConversationReadDateUseC
5961
import com.wire.kalium.logic.feature.conversation.UpdateConversationReceiptModeUseCase
6062
import com.wire.kalium.logic.feature.conversation.createconversation.CreateChannelUseCase
6163
import com.wire.kalium.logic.feature.conversation.createconversation.CreateRegularGroupUseCase
64+
import com.wire.kalium.logic.feature.conversation.delete.MarkConversationAsDeletedLocallyUseCase
6265
import com.wire.kalium.logic.feature.conversation.getPaginatedFlowOfConversationDetailsWithEventsBySearchQuery
6366
import com.wire.kalium.logic.feature.conversation.guestroomlink.CanCreatePasswordProtectedLinksUseCase
6467
import com.wire.kalium.logic.feature.conversation.guestroomlink.GenerateGuestRoomLinkUseCase
@@ -114,6 +117,11 @@ class ConversationModule {
114117
fun provideDeleteTeamConversationUseCase(conversationScope: ConversationScope): DeleteTeamConversationUseCase =
115118
conversationScope.deleteTeamConversation
116119

120+
@ViewModelScoped
121+
@Provides
122+
fun provideMarkConversationAsDeletedLocallyUseCase(conversationScope: ConversationScope): MarkConversationAsDeletedLocallyUseCase =
123+
conversationScope.markConversationAsDeletedLocallyUseCase
124+
117125
@ViewModelScoped
118126
@Provides
119127
fun provideObserveIsSelfConversationMemberUseCase(conversationScope: ConversationScope): ObserveIsSelfUserMemberUseCase =
@@ -363,4 +371,18 @@ class ConversationModule {
363371
@Provides
364372
fun provideCreateConversationFolderUseCase(conversationScope: ConversationScope) =
365373
conversationScope.createConversationFolder
374+
375+
@ViewModelScoped
376+
@Provides
377+
fun provideResetMlsConversationUseCase(
378+
@KaliumCoreLogic coreLogic: CoreLogic,
379+
@CurrentAccount currentAccount: UserId
380+
): ResetMLSConversationUseCase = coreLogic.getSessionScope(currentAccount).resetMlsConversation
381+
382+
@ViewModelScoped
383+
@Provides
384+
fun provideFetchConversationUseCase(
385+
@KaliumCoreLogic coreLogic: CoreLogic,
386+
@CurrentAccount currentAccount: UserId
387+
): FetchConversationUseCase = coreLogic.getSessionScope(currentAccount).fetchConversationUseCase
366388
}

app/src/main/kotlin/com/wire/android/notification/WireNotificationManager.kt

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ import kotlinx.coroutines.flow.StateFlow
5252
import kotlinx.coroutines.flow.cancellable
5353
import kotlinx.coroutines.flow.combine
5454
import kotlinx.coroutines.flow.distinctUntilChanged
55+
import kotlinx.coroutines.flow.filter
5556
import kotlinx.coroutines.flow.first
5657
import kotlinx.coroutines.flow.flatMapLatest
5758
import kotlinx.coroutines.flow.flowOf
5859
import kotlinx.coroutines.flow.map
59-
import kotlinx.coroutines.flow.onCompletion
6060
import kotlinx.coroutines.flow.onEach
6161
import kotlinx.coroutines.flow.stateIn
6262
import kotlinx.coroutines.launch
@@ -419,8 +419,9 @@ class WireNotificationManager @Inject constructor(
419419
}
420420

421421
/**
422-
* Infinitely listen for outgoing and established calls of a current user and run the call on foreground Service
423-
* to show corresponding notification and do not lose a call.
422+
* Infinitely listen for outgoing and established calls of a current user and run the call on foreground Service to show corresponding
423+
* notification and do not lose a call. The call service handles stopping itself when there are no calls so here only care about
424+
* starting the service when there are calls.
424425
*/
425426
private suspend fun observeOutgoingOngoingCalls() {
426427
coreLogic.getGlobalScope().session.currentSessionFlow()
@@ -429,29 +430,15 @@ class WireNotificationManager @Inject constructor(
429430
combine(
430431
coreLogic.getSessionScope(it.accountInfo.userId).calls.establishedCall(),
431432
coreLogic.getSessionScope(it.accountInfo.userId).calls.observeOutgoingCall()
432-
) { establishedCalls, outgoingCalls ->
433-
if (establishedCalls.isNotEmpty()) {
434-
return@combine true
435-
}
436-
if (outgoingCalls.isNotEmpty()) {
437-
return@combine true
438-
}
439-
return@combine false
440-
}
433+
) { establishedCalls, outgoingCalls -> (establishedCalls + outgoingCalls).isNotEmpty() }
441434
} else {
442-
flowOf(false)
435+
flowOf(null)
443436
}
444437
}
445438
.distinctUntilChanged()
446-
.onCompletion {
447-
servicesManager.stopCallService()
448-
}
449-
.collect { isOnCall ->
450-
if (isOnCall) {
451-
servicesManager.startCallService()
452-
} else {
453-
servicesManager.stopCallService()
454-
}
439+
.filter { it == true }
440+
.collect {
441+
servicesManager.startCallService()
455442
}
456443
}
457444

app/src/main/kotlin/com/wire/android/services/CallService.kt

Lines changed: 37 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,25 @@ import android.content.pm.ServiceInfo
2626
import android.os.IBinder
2727
import androidx.core.app.ServiceCompat
2828
import com.wire.android.appLogger
29-
import com.wire.android.di.KaliumCoreLogic
30-
import com.wire.android.di.NoSession
3129
import com.wire.android.notification.CallNotificationData
3230
import com.wire.android.notification.CallNotificationManager
3331
import com.wire.android.notification.NotificationIds
3432
import com.wire.android.services.CallService.Action
3533
import com.wire.android.util.dispatchers.DispatcherProvider
36-
import com.wire.android.util.logIfEmptyUserName
37-
import com.wire.kalium.common.functional.Either
3834
import com.wire.kalium.common.functional.fold
39-
import com.wire.kalium.logic.CoreLogic
4035
import com.wire.kalium.logic.data.call.CallStatus
4136
import com.wire.kalium.logic.data.id.ConversationId
42-
import com.wire.kalium.logic.data.id.QualifiedIdMapper
4337
import com.wire.kalium.logic.data.user.UserId
44-
import com.wire.kalium.logic.feature.call.CallsScope
45-
import com.wire.kalium.logic.feature.session.CurrentSessionResult
4638
import dagger.hilt.android.AndroidEntryPoint
4739
import dev.ahmedmourad.bundlizer.Bundlizer
4840
import kotlinx.coroutines.CoroutineScope
4941
import kotlinx.coroutines.SupervisorJob
5042
import kotlinx.coroutines.cancel
43+
import kotlinx.coroutines.flow.MutableStateFlow
44+
import kotlinx.coroutines.flow.StateFlow
5145
import kotlinx.coroutines.flow.collectLatest
52-
import kotlinx.coroutines.flow.combine
53-
import kotlinx.coroutines.flow.debounce
54-
import kotlinx.coroutines.flow.distinctUntilChanged
55-
import kotlinx.coroutines.flow.first
56-
import kotlinx.coroutines.flow.flatMapLatest
57-
import kotlinx.coroutines.flow.flowOf
58-
import kotlinx.coroutines.flow.map
5946
import kotlinx.coroutines.launch
6047
import kotlinx.serialization.Serializable
61-
import java.util.concurrent.atomic.AtomicReference
6248
import javax.inject.Inject
6349

6450
/**
@@ -68,27 +54,23 @@ import javax.inject.Inject
6854
class CallService : Service() {
6955

7056
@Inject
71-
@KaliumCoreLogic
72-
lateinit var coreLogic: CoreLogic
57+
lateinit var lifecycleManager: CallServiceManager
7358

7459
@Inject
7560
lateinit var callNotificationManager: CallNotificationManager
7661

7762
@Inject
7863
lateinit var dispatcherProvider: DispatcherProvider
7964

80-
@Inject
81-
@NoSession
82-
lateinit var qualifiedIdMapper: QualifiedIdMapper
83-
8465
private val scope by lazy {
8566
// There's no UI, no need to run anything using the Main/UI Dispatcher
8667
CoroutineScope(SupervisorJob() + dispatcherProvider.default())
8768
}
8869

8970
override fun onCreate() {
90-
serviceState.set(ServiceState.STARTED)
71+
_serviceState.value = ServiceState.STARTED
9172
super.onCreate()
73+
handleActions()
9274
}
9375

9476
override fun onBind(intent: Intent?): IBinder? {
@@ -97,70 +79,35 @@ class CallService : Service() {
9779

9880
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
9981
appLogger.i("$TAG: onStartCommand")
100-
val action = intent?.getActionTypeExtra(EXTRA_ACTION_TYPE)
82+
val action = intent?.getActionTypeExtra(EXTRA_ACTION_TYPE) ?: Action.Start.Default
10183
generatePlaceholderForegroundNotification()
102-
serviceState.set(ServiceState.FOREGROUND)
103-
if (action is Action.Stop) {
104-
appLogger.i("$TAG: stopSelf. Reason: stopService was called")
105-
stopSelf()
106-
} else {
107-
scope.launch {
108-
if (action is Action.AnswerCall) {
109-
coreLogic.getSessionScope(action.userId).calls.answerCall(action.conversationId)
110-
}
111-
coreLogic.getGlobalScope().session.currentSessionFlow()
112-
.flatMapLatest {
113-
if (it is CurrentSessionResult.Success && it.accountInfo.isValid()) {
114-
val userId = it.accountInfo.userId
115-
val userSessionScope = coreLogic.getSessionScope(userId)
116-
val outgoingCallsFlow = userSessionScope.calls.observeOutgoingCall()
117-
val establishedCallsFlow = userSessionScope.calls.establishedCall()
118-
val callCurrentlyBeingAnsweredFlow = userSessionScope.calls.observeCallCurrentlyBeingAnswered(action)
119-
120-
combine(
121-
outgoingCallsFlow,
122-
establishedCallsFlow,
123-
callCurrentlyBeingAnsweredFlow
124-
) { outgoingCalls, establishedCalls, answeringCall ->
125-
val calls = outgoingCalls + establishedCalls + answeringCall
126-
calls.firstOrNull()?.let { call ->
127-
val userName = userSessionScope.users.observeSelfUser().first()
128-
.also { it.logIfEmptyUserName() }
129-
.let { it.handle ?: it.name ?: "" }
130-
Either.Right(CallNotificationData(userId, call, userName))
131-
} ?: Either.Left("no calls")
132-
}
133-
} else {
134-
flowOf(Either.Left("no valid current session"))
135-
}
136-
}
137-
.distinctUntilChanged()
138-
.debounce {
139-
if (it is Either.Left) ServicesManager.DEBOUNCE_TIME else 0L
140-
}
141-
.collectLatest {
142-
it.fold(
143-
{ reason ->
144-
appLogger.i("$TAG: stopSelf. Reason: $reason")
145-
stopSelf()
146-
},
147-
{ data ->
148-
updateForegroundNotification(data)
149-
}
150-
)
151-
}
152-
}
84+
_serviceState.value = ServiceState.FOREGROUND
85+
scope.launch {
86+
lifecycleManager.handleAction(action)
15387
}
15488
return START_STICKY
15589
}
15690

15791
override fun onDestroy() {
15892
appLogger.i("$TAG: onDestroy")
159-
serviceState.set(ServiceState.NOT_STARTED)
93+
_serviceState.value = ServiceState.NOT_STARTED
16094
super.onDestroy()
16195
scope.cancel()
16296
}
16397

98+
private fun handleActions() {
99+
scope.launch {
100+
lifecycleManager.handleActionsFlow().collectLatest {
101+
it.fold({ reason ->
102+
appLogger.i("$TAG: stopSelf. Reason: ${reason.message}")
103+
stopSelf()
104+
}, { data ->
105+
updateForegroundNotification(data)
106+
})
107+
}
108+
}
109+
}
110+
164111
private fun updateForegroundNotification(data: CallNotificationData) {
165112
// Updating service notification when notifications are disabled (or background work restricted)
166113
// causes app crash when putting app in background
@@ -190,40 +137,40 @@ class CallService : Service() {
190137
appLogger.i("$TAG: started foreground with placeholder notification")
191138
}
192139

193-
private suspend fun CallsScope.observeCallCurrentlyBeingAnswered(action: Action?) = when (action) {
194-
is Action.AnswerCall -> getIncomingCalls().map { it.filter { it.conversationId == action.conversationId } }
195-
else -> flowOf(emptyList())
196-
}
197-
198140
companion object {
199141
private const val TAG = "CallService"
200142
private const val EXTRA_ACTION_TYPE = "action_type"
201143

202-
fun newIntent(context: Context, actionType: Action = Action.Default): Intent = Intent(context, CallService::class.java)
144+
fun newIntent(context: Context, actionType: Action = Action.Start.Default): Intent = Intent(context, CallService::class.java)
203145
.putExtra(EXTRA_ACTION_TYPE, actionType)
204146

205-
val serviceState: AtomicReference<ServiceState> = AtomicReference(ServiceState.NOT_STARTED)
147+
private val _serviceState: MutableStateFlow<ServiceState> = MutableStateFlow(ServiceState.NOT_STARTED)
148+
val serviceState: StateFlow<ServiceState> = _serviceState
206149
}
207150

208151
enum class ServiceState {
209152
NOT_STARTED, STARTED, FOREGROUND
210153
}
211154

212155
@Serializable
213-
sealed class Action {
156+
sealed interface Action {
214157
@Serializable
215-
data object Default : Action()
158+
sealed interface Start : Action {
216159

217-
@Serializable
218-
data class AnswerCall(val userId: UserId, val conversationId: ConversationId) : Action()
160+
@Serializable
161+
data object Default : Start
162+
163+
@Serializable
164+
data class AnswerCall(val userId: UserId, val conversationId: ConversationId) : Start
165+
}
219166

220167
@Serializable
221-
data object Stop : Action()
168+
data object Stop : Action
222169
}
223170
}
224171

225172
private fun Intent.putExtra(name: String, actionType: Action): Intent = putExtra(name, Bundlizer.bundle(Action.serializer(), actionType))
226173

227174
private fun Intent.getActionTypeExtra(name: String): Action? = getBundleExtra(name)?.let {
228-
Bundlizer.unbundle(Action.serializer(), it)
229-
}
175+
Bundlizer.unbundle(Action.serializer(), it)
176+
}

0 commit comments

Comments
 (0)