Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-02-23 19:15:17 +01:00
parent cf8261fe4c
commit 0e8b0b8db0
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
9 changed files with 63 additions and 27 deletions

View File

@ -251,7 +251,7 @@ class MessageNotificationWorker(
var notificationId = decryptedPushMessage.timestamp.toInt() var notificationId = decryptedPushMessage.timestamp.toInt()
val notificationInfoBundle = Bundle() val notificationInfoBundle = Bundle()
notificationInfoBundle.putLong(BundleKeys.KEY_INTERNAL_USER_ID, signatureVerification.userEntity!!.id!!) notificationInfoBundle.putLong(BundleKeys.KEY_INTERNAL_USER_ID, signatureVerification.userEntity!!.id)
notificationInfoBundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id) notificationInfoBundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id)
notificationInfoBundle.putLong(BundleKeys.KEY_NOTIFICATION_ID, decryptedPushMessage.notificationId!!) notificationInfoBundle.putLong(BundleKeys.KEY_NOTIFICATION_ID, decryptedPushMessage.notificationId!!)
notificationBuilder.extras = notificationInfoBundle notificationBuilder.extras = notificationInfoBundle

View File

@ -83,8 +83,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) :
} }
} }
override suspend fun getConversationForUserWithToken(userId: Long, token: String): Conversation? { override suspend fun getConversationForUserWithToken(internalUserId: Long, token: String): Conversation? {
val conversationEntity = conversationsDao.getConversationForUserWithToken(userId, token) val conversationEntity = conversationsDao.getConversationForUserWithToken(internalUserId, token)
if (conversationEntity != null) { if (conversationEntity != null) {
return conversationEntity.toConversation() return conversationEntity.toConversation()
} }

View File

@ -99,7 +99,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
return apiService.getNotification(user.getCredentials(), ApiUtils.getUrlForNotificationWithId(user.baseUrl, notificationId)) return apiService.getNotification(user.getCredentials(), ApiUtils.getUrlForNotificationWithId(user.baseUrl, notificationId))
} }
override suspend fun getPeersForCall(user: UserNgEntity, conversationToken: String): ParticipantsOverall { override suspend fun getParticipantsForCall(user: UserNgEntity, conversationToken: String): ParticipantsOverall {
return apiService.getPeersForCall(user.getCredentials(), ApiUtils.getUrlForCall(user.baseUrl, conversationToken)) return apiService.getPeersForCall(user.getCredentials(), ApiUtils.getUrlForCall(user.baseUrl, conversationToken))
} }

View File

@ -51,7 +51,7 @@ val UseCasesModule = module {
single { createCreateConversationUseCase(get(), get()) } single { createCreateConversationUseCase(get(), get()) }
single { createAddParticipantToConversationUseCase(get(), get()) } single { createAddParticipantToConversationUseCase(get(), get()) }
single { setConversationPasswordUseCase(get(), get()) } single { setConversationPasswordUseCase(get(), get()) }
factory { getPeersForCallUseCase(get(), get()) } factory { getParticipantsForCallUseCase(get(), get()) }
factory { getNotificationUseCase(get(), get()) } factory { getNotificationUseCase(get(), get()) }
factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) } factory { createChatViewModelFactory(get(), get(), get(), get(), get(), get()) }
} }
@ -61,9 +61,9 @@ fun getNotificationUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
return GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler) return GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler)
} }
fun getPeersForCallUseCase(nextcloudTalkRepository: NextcloudTalkRepository, fun getParticipantsForCallUseCase(nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler): GetPeersForCallUseCase { apiErrorHandler: ApiErrorHandler): GetParticipantsForCallUseCase {
return GetPeersForCallUseCase(nextcloudTalkRepository, apiErrorHandler) return GetParticipantsForCallUseCase(nextcloudTalkRepository, apiErrorHandler)
} }

View File

@ -28,7 +28,7 @@ import com.nextcloud.talk.models.json.conversations.Conversation
interface ConversationsRepository { interface ConversationsRepository {
fun getConversationsForUser(userId: Long, filter: CharSequence?): LiveData<List<Conversation>> fun getConversationsForUser(userId: Long, filter: CharSequence?): LiveData<List<Conversation>>
fun getShortcutTargetConversations(userId: Long): LiveData<List<Conversation>> fun getShortcutTargetConversations(userId: Long): LiveData<List<Conversation>>
suspend fun getConversationForUserWithToken(userId: Long, token: String): Conversation? suspend fun getConversationForUserWithToken(internalUserId: Long, token: String): Conversation?
suspend fun clearConversationsForUser(userId: Long) suspend fun clearConversationsForUser(userId: Long)
suspend fun saveConversationsForUser( suspend fun saveConversationsForUser(
userId: Long, userId: Long,

View File

@ -37,7 +37,7 @@ import com.nextcloud.talk.newarch.local.models.UserNgEntity
interface NextcloudTalkRepository { interface NextcloudTalkRepository {
suspend fun getNotificationForUser(user: UserNgEntity, notificationId: String): NotificationOverall suspend fun getNotificationForUser(user: UserNgEntity, notificationId: String): NotificationOverall
suspend fun getPeersForCall(user: UserNgEntity, conversationToken: String): ParticipantsOverall suspend fun getParticipantsForCall(user: UserNgEntity, conversationToken: String): ParticipantsOverall
suspend fun setPasswordForConversation(user: UserNgEntity, conversationToken: String, password: String): GenericOverall suspend fun setPasswordForConversation(user: UserNgEntity, conversationToken: String, password: String): GenericOverall
suspend fun addParticipantToConversation(user: UserNgEntity, conversationToken: String, participantId: String, source: String): AddParticipantOverall suspend fun addParticipantToConversation(user: UserNgEntity, conversationToken: String, participantId: String, source: String): AddParticipantOverall
suspend fun createConversationForUser(user: UserNgEntity, conversationType: Int, invite: String?, source: String?, conversationName: String?): ConversationOverall suspend fun createConversationForUser(user: UserNgEntity, conversationType: Int, invite: String?, source: String?, conversationName: String?): ConversationOverall

View File

@ -28,12 +28,12 @@ import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkReposito
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
import org.koin.core.parameter.DefinitionParameters import org.koin.core.parameter.DefinitionParameters
class GetPeersForCallUseCase constructor( class GetParticipantsForCallUseCase constructor(
private val nextcloudTalkRepository: NextcloudTalkRepository, private val nextcloudTalkRepository: NextcloudTalkRepository,
apiErrorHandler: ApiErrorHandler? apiErrorHandler: ApiErrorHandler?
) : UseCase<ParticipantsOverall, Any?>(apiErrorHandler) { ) : UseCase<ParticipantsOverall, Any?>(apiErrorHandler) {
override suspend fun run(params: Any?): ParticipantsOverall { override suspend fun run(params: Any?): ParticipantsOverall {
val definitionParameters = params as DefinitionParameters val definitionParameters = params as DefinitionParameters
return nextcloudTalkRepository.getPeersForCall(definitionParameters[0], definitionParameters[1]) return nextcloudTalkRepository.getParticipantsForCall(definitionParameters[0], definitionParameters[1])
} }
} }

View File

@ -80,8 +80,8 @@ abstract class ConversationsDao {
timestamp: Long timestamp: Long
) )
@Query("SELECT * FROM conversations where id = :userId AND token = :token") @Query("SELECT * FROM conversations where id = :internalUserId AND token = :token")
abstract suspend fun getConversationForUserWithToken(userId: Long, token: String): ConversationEntity? abstract suspend fun getConversationForUserWithToken(internalUserId: Long, token: String): ConversationEntity?
@Transaction @Transaction
open suspend fun updateConversationsForUser( open suspend fun updateConversationsForUser(

View File

@ -10,6 +10,7 @@ import android.media.AudioAttributes
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.IBinder import android.os.IBinder
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
@ -30,12 +31,16 @@ import com.nextcloud.talk.jobs.MessageNotificationWorker
import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.SignatureVerification
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.ConversationOverall import com.nextcloud.talk.models.json.conversations.ConversationOverall
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.models.json.push.DecryptedPushMessage import com.nextcloud.talk.models.json.push.DecryptedPushMessage
import com.nextcloud.talk.newarch.data.model.ErrorModel import com.nextcloud.talk.newarch.data.model.ErrorModel
import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler import com.nextcloud.talk.newarch.data.source.remote.ApiErrorHandler
import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository import com.nextcloud.talk.newarch.domain.repository.offline.ConversationsRepository
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
import com.nextcloud.talk.newarch.domain.usecases.GetConversationUseCase import com.nextcloud.talk.newarch.domain.usecases.GetConversationUseCase
import com.nextcloud.talk.newarch.domain.usecases.GetParticipantsForCallUseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.utils.ComponentsWithEmptyCookieJar import com.nextcloud.talk.newarch.utils.ComponentsWithEmptyCookieJar
@ -57,8 +62,10 @@ import retrofit2.Retrofit
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.security.PrivateKey import java.security.PrivateKey
import java.util.*
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException import javax.crypto.NoSuchPaddingException
import kotlin.concurrent.timerTask
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
class CallService : Service(), KoinComponent, CoroutineScope { class CallService : Service(), KoinComponent, CoroutineScope {
@ -83,12 +90,7 @@ class CallService : Service(), KoinComponent, CoroutineScope {
decryptMessage(it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT), it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE)) decryptMessage(it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT), it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE))
} else if (it.action == BundleKeys.KEY_REJECT_INCOMING_CALL || it.action == BundleKeys.DISMISS_CALL_NOTIFICATION) { } else if (it.action == BundleKeys.KEY_REJECT_INCOMING_CALL || it.action == BundleKeys.DISMISS_CALL_NOTIFICATION) {
if (it.getStringExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION) == activeNotification) { if (it.getStringExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION) == activeNotification) {
stopForeground(true) endIncomingConversation(it.action != BundleKeys.DISMISS_CALL_NOTIFICATION)
if (it.action != BundleKeys.DISMISS_CALL_NOTIFICATION) {
eventBus.post(CallEvent())
} else {
// do nothing
}
} else { } else {
// do nothing? :D // do nothing? :D
} }
@ -184,7 +186,6 @@ class CallService : Service(), KoinComponent, CoroutineScope {
.setAutoCancel(true) .setAutoCancel(true)
.setOngoing(true) .setOngoing(true)
.addAction(R.drawable.ic_call_end_white_24px, resources.getString(R.string.reject_call), rejectCallPendingIntent) .addAction(R.drawable.ic_call_end_white_24px, resources.getString(R.string.reject_call), rejectCallPendingIntent)
//.setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent) .setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true) .setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING) .setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING)
@ -193,20 +194,18 @@ class CallService : Service(), KoinComponent, CoroutineScope {
notificationBuilder.setVibrate(vibrationEffect) notificationBuilder.setVibrate(vibrationEffect)
} }
//checkIfCallIsActive(signatureVerification, decryptedPushMessage)
if (conversation.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION) { if (conversation.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION) {
val target = object : Target { val target = object : Target {
override fun onSuccess(result: Drawable) { override fun onSuccess(result: Drawable) {
super.onSuccess(result) super.onSuccess(result)
largeIcon = result.toBitmap() largeIcon = result.toBitmap()
notificationBuilder.setLargeIcon(largeIcon) notificationBuilder.setLargeIcon(largeIcon)
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId) showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
} }
override fun onError(error: Drawable?) { override fun onError(error: Drawable?) {
super.onError(error) super.onError(error)
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId) showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
} }
} }
@ -219,7 +218,7 @@ class CallService : Service(), KoinComponent, CoroutineScope {
imageLoader.load(request) imageLoader.load(request)
} else { } else {
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId) showNotification(notificationBuilder, signatureVerification.userEntity!!, conversation.token!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
} }
} }
} }
@ -253,6 +252,41 @@ class CallService : Service(), KoinComponent, CoroutineScope {
} }
} }
private fun checkIsConversationActive(user: UserNgEntity, conversationToken: String, activeNotificationArgument: String) {
if (activeNotificationArgument == activeNotification) {
val getParticipantsForCallUseCase = GetParticipantsForCallUseCase(componentsWithEmptyCookieJar.getRepository(), apiErrorHandler)
getParticipantsForCallUseCase.invoke(this, parametersOf(user, conversationToken), object : UseCaseResponse<ParticipantsOverall> {
override suspend fun onSuccess(result: ParticipantsOverall) {
val participants = result.ocs.data
if (participants.size > 0 && activeNotificationArgument == activeNotification) {
val activeParticipants = participants.filter { it.participantFlags != Participant.ParticipantFlags.NOT_IN_CALL }
val activeOnAnotherDevice = activeParticipants.filter { it.userId == user.userId }
if (activeParticipants.isNotEmpty() && activeOnAnotherDevice.isEmpty()) {
delay(5000)
checkIsConversationActive(user, conversationToken, activeNotificationArgument)
} else {
endIncomingConversation(true)
}
} else if (activeNotificationArgument == activeNotification) {
endIncomingConversation(true)
}
}
override suspend fun onError(errorModel: ErrorModel?) {
endIncomingConversation(true)
}
})
}
}
private fun endIncomingConversation(triggerEventBus : Boolean) {
activeNotification = ""
stopForeground(true)
if (triggerEventBus) {
eventBus.post(CallEvent())
}
}
private suspend fun getConversationForTokenAndUser(user: UserNgEntity, conversationToken: String): Conversation? { private suspend fun getConversationForTokenAndUser(user: UserNgEntity, conversationToken: String): Conversation? {
var conversation = conversationsRepository.getConversationForUserWithToken(user.id, conversationToken) var conversation = conversationsRepository.getConversationForUserWithToken(user.id, conversationToken)
if (conversation == null) { if (conversation == null) {
@ -275,13 +309,15 @@ class CallService : Service(), KoinComponent, CoroutineScope {
return conversation return conversation
} }
private fun showNotification(builder: NotificationCompat.Builder, user: UserNgEntity, internalNotificationId: Long, generatedNotificationId: String) { private fun showNotification(builder: NotificationCompat.Builder, user: UserNgEntity, conversationToken: String, internalNotificationId: Long, generatedNotificationId: String) {
endIncomingConversation(true)
activeNotification = generatedNotificationId activeNotification = generatedNotificationId
val notification = builder.build() val notification = builder.build()
notification.extras.putLong(BundleKeys.KEY_INTERNAL_USER_ID, user.id) notification.extras.putLong(BundleKeys.KEY_INTERNAL_USER_ID, user.id)
notification.extras.putLong(BundleKeys.KEY_NOTIFICATION_ID, internalNotificationId) notification.extras.putLong(BundleKeys.KEY_NOTIFICATION_ID, internalNotificationId)
notification.flags = notification.flags or Notification.FLAG_INSISTENT notification.flags = notification.flags or Notification.FLAG_INSISTENT
startForeground(generatedNotificationId.hashCode(), notification) startForeground(generatedNotificationId.hashCode(), notification)
checkIsConversationActive(user, conversationToken, generatedNotificationId)
} }
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {