Dismiss notification via action

Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-02-23 17:41:51 +01:00
parent 8252240b3b
commit aff8595224
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
14 changed files with 220 additions and 134 deletions

View File

@ -71,7 +71,7 @@ class MagicCallActivity : BaseActivity() {
val hideIncomingCallNotificationIntent = Intent(applicationContext, CallService::class.java) val hideIncomingCallNotificationIntent = Intent(applicationContext, CallService::class.java)
hideIncomingCallNotificationIntent.action = BundleKeys.KEY_SHOW_INCOMING_CALL hideIncomingCallNotificationIntent.action = BundleKeys.KEY_SHOW_INCOMING_CALL
hideIncomingCallNotificationIntent.putExtra(BundleKeys.KEY_NOTIFICATION_ID, intent.getLongExtra(BundleKeys.KEY_NOTIFICATION_ID, -1)) hideIncomingCallNotificationIntent.putExtra(BundleKeys.KEY_NOTIFICATION_ID, intent.getLongExtra(BundleKeys.KEY_NOTIFICATION_ID, -1))
applicationContext?.startService(hideIncomingCallNotificationIntent) //applicationContext?.startService(hideIncomingCallNotificationIntent)
router!!.setRoot( router!!.setRoot(
RouterTransaction.with(CallNotificationController(intent.extras!!)) RouterTransaction.with(CallNotificationController(intent.extras!!))

View File

@ -21,18 +21,14 @@
package com.nextcloud.talk.controllers package com.nextcloud.talk.controllers
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.media.AudioAttributes
import android.media.MediaPlayer import android.media.MediaPlayer
import android.net.Uri import android.os.Bundle
import android.os.* import android.os.Handler
import android.text.TextUtils import android.os.Vibrator
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -40,7 +36,6 @@ import android.widget.ImageView
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import butterknife.BindView import butterknife.BindView
import butterknife.OnClick import butterknife.OnClick
import coil.api.load import coil.api.load
@ -51,28 +46,21 @@ import coil.transform.BlurTransformation
import coil.transform.CircleCropTransformation import coil.transform.CircleCropTransformation
import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler
import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.controllers.base.BaseController import com.nextcloud.talk.controllers.base.BaseController
import com.nextcloud.talk.events.ConfigurationChangeEvent import com.nextcloud.talk.events.ConfigurationChangeEvent
import com.nextcloud.talk.models.RingtoneSettings
import com.nextcloud.talk.models.database.ArbitraryStorageEntity
import com.nextcloud.talk.models.json.conversations.Conversation import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.RoomsOverall
import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.newarch.local.models.UserNgEntity import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.local.models.getCredentials import com.nextcloud.talk.newarch.local.models.getCredentials
import com.nextcloud.talk.newarch.services.CallService
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DoNotDisturbUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils
import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder
import com.uber.autodispose.AutoDispose import com.uber.autodispose.AutoDispose
import io.reactivex.Observer import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -83,7 +71,6 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.michaelevans.colorart.library.ColorArt import org.michaelevans.colorart.library.ColorArt
import java.io.IOException
class CallNotificationController(private val originalBundle: Bundle) : BaseController() { class CallNotificationController(private val originalBundle: Bundle) : BaseController() {
@ -116,6 +103,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
private val conversationToken: String private val conversationToken: String
private val userBeingCalled: UserNgEntity? private val userBeingCalled: UserNgEntity?
private val credentials: String? private val credentials: String?
private val notificationId: Long?
private var currentConversation: Conversation? = null private var currentConversation: Conversation? = null
private var mediaPlayer: MediaPlayer? = null private var mediaPlayer: MediaPlayer? = null
private var leavingScreen = false private var leavingScreen = false
@ -125,6 +113,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
init { init {
this.conversationToken = originalBundle.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!! this.conversationToken = originalBundle.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!!
this.userBeingCalled = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)!! this.userBeingCalled = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)!!
this.notificationId = originalBundle.getLong(BundleKeys.KEY_NOTIFICATION_ID)
credentials = userBeingCalled.getCredentials() credentials = userBeingCalled.getCredentials()
} }
@ -233,43 +222,14 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
}) })
} }
private fun handleFromNotification() {
ncApi.getRooms(credentials, ApiUtils.getUrlForRoomEndpoint(userBeingCalled!!.baseUrl))
.subscribeOn(Schedulers.io())
.retry(3)
.observeOn(AndroidSchedulers.mainThread())
.`as`(AutoDispose.autoDisposable(scopeProvider))
.subscribe(object : Observer<RoomsOverall> {
override fun onSubscribe(d: Disposable) {}
override fun onNext(roomsOverall: RoomsOverall) {
for (conversation in roomsOverall.ocs.data) {
if (roomId == conversation.conversationId) {
currentConversation = conversation
runAllThings()
break
}
}
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
})
}
private fun runAllThings() { private fun runAllThings() {
if (conversationNameTextView != null) { /*if (conversationNameTextView != null) {
conversationNameTextView!!.text = currentConversation!!.displayName conversationNameTextView!!.text = currentConversation!!.displayName
} }
loadAvatar() loadAvatar()
checkIfAnyParticipantsRemainInRoom() checkIfAnyParticipantsRemainInRoom()
showAnswerControls() showAnswerControls()*/
} }
@SuppressLint("LongLogTag") @SuppressLint("LongLogTag")

View File

@ -55,7 +55,7 @@ import com.nextcloud.talk.newarch.domain.usecases.GetNotificationUseCase
import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse import com.nextcloud.talk.newarch.domain.usecases.base.UseCaseResponse
import com.nextcloud.talk.newarch.utils.Images import com.nextcloud.talk.newarch.utils.Images
import com.nextcloud.talk.newarch.utils.MagicJson import com.nextcloud.talk.newarch.utils.MagicJson
import com.nextcloud.talk.newarch.utils.NextcloudRepositoryWithNoCookies import com.nextcloud.talk.newarch.utils.ComponentsWithEmptyCookieJar
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
@ -73,7 +73,7 @@ class MessageNotificationWorker(
workerParams: WorkerParameters workerParams: WorkerParameters
) : CoroutineWorker(context, workerParams), KoinComponent { ) : CoroutineWorker(context, workerParams), KoinComponent {
val appPreferences: AppPreferences by inject() val appPreferences: AppPreferences by inject()
private val nextcloudRepositoryWithNoCookies: NextcloudRepositoryWithNoCookies by inject() private val componentsWithEmptyCookieJar: ComponentsWithEmptyCookieJar by inject()
private val apiErrorHandler: ApiErrorHandler by inject() private val apiErrorHandler: ApiErrorHandler by inject()
override suspend fun doWork(): Result = coroutineScope { override suspend fun doWork(): Result = coroutineScope {
@ -107,7 +107,7 @@ class MessageNotificationWorker(
} }
private fun showNotificationWithObjectData(coroutineScope: CoroutineScope, decryptedPushMessage: DecryptedPushMessage, signatureVerification: SignatureVerification, intent: Intent) { private fun showNotificationWithObjectData(coroutineScope: CoroutineScope, decryptedPushMessage: DecryptedPushMessage, signatureVerification: SignatureVerification, intent: Intent) {
val nextcloudTalkRepository = nextcloudRepositoryWithNoCookies.getRepository() val nextcloudTalkRepository = componentsWithEmptyCookieJar.getRepository()
val getNotificationUseCase = GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler) val getNotificationUseCase = GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler)
getNotificationUseCase.invoke(coroutineScope, parametersOf(signatureVerification.userEntity, decryptedPushMessage.notificationId.toString()), object : UseCaseResponse<NotificationOverall> { getNotificationUseCase.invoke(coroutineScope, parametersOf(signatureVerification.userEntity, decryptedPushMessage.notificationId.toString()), object : UseCaseResponse<NotificationOverall> {
override suspend fun onSuccess(result: NotificationOverall) { override suspend fun onSuccess(result: NotificationOverall) {
@ -193,7 +193,6 @@ class MessageNotificationWorker(
else -> { else -> {
// one to one and unknown // one to one and unknown
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_chat_black_24dp) BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_chat_black_24dp)
} }
} }

View File

@ -99,7 +99,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) :
override suspend fun saveConversationsForUser( override suspend fun saveConversationsForUser(
userId: Long, userId: Long,
conversations: List<Conversation> conversations: List<Conversation>,
deleteOutdated: Boolean
): List<Long> { ): List<Long> {
val map = conversations.map { val map = conversations.map {
it.toConversationEntity() it.toConversationEntity()
@ -108,7 +109,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) :
return conversationsDao return conversationsDao
.updateConversationsForUser( .updateConversationsForUser(
userId, userId,
map.toTypedArray() map.toTypedArray(),
deleteOutdated
) )
} }

View File

@ -62,7 +62,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou
} }
override suspend fun getConversationForUser(user: UserNgEntity, conversationToken: String): ConversationOverall { override suspend fun getConversationForUser(user: UserNgEntity, conversationToken: String): ConversationOverall {
return apiService.getConversation(user.getCredentials(), conversationToken) return apiService.getConversation(user.getCredentials(), ApiUtils.getRoom(user.baseUrl, conversationToken))
} }
override suspend fun joinConversationForUser(user: UserNgEntity, conversationToken: String, conversationPassword: String?): ConversationOverall { override suspend fun joinConversationForUser(user: UserNgEntity, conversationToken: String, conversationPassword: String?): ConversationOverall {

View File

@ -46,7 +46,7 @@ import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkReposito
import com.nextcloud.talk.newarch.utils.NetworkUtils import com.nextcloud.talk.newarch.utils.NetworkUtils
import com.nextcloud.talk.newarch.utils.NetworkUtils.GetProxyRunnable import com.nextcloud.talk.newarch.utils.NetworkUtils.GetProxyRunnable
import com.nextcloud.talk.newarch.utils.NetworkUtils.MagicAuthenticator import com.nextcloud.talk.newarch.utils.NetworkUtils.MagicAuthenticator
import com.nextcloud.talk.newarch.utils.NextcloudRepositoryWithNoCookies import com.nextcloud.talk.newarch.utils.ComponentsWithEmptyCookieJar
import com.nextcloud.talk.utils.LoggingUtils import com.nextcloud.talk.utils.LoggingUtils
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder
@ -88,13 +88,13 @@ val NetworkModule = module {
single { createOkHttpClient(androidContext(), get(), get(), get(), get(), get(), get(), get()) } single { createOkHttpClient(androidContext(), get(), get(), get(), get(), get(), get(), get()) }
factory { createApiErrorHandler() } factory { createApiErrorHandler() }
single { createNextcloudTalkRepository(get()) } single { createNextcloudTalkRepository(get()) }
single { createNexcloudRepositoryWithNoCookies(get(), get()) } single { createComponentsWithEmptyCookieJar(get(), get(), androidApplication()) }
single { createImageLoader(androidApplication(), get()) } single { createImageLoader(androidApplication(), get()) }
} }
fun createNexcloudRepositoryWithNoCookies(okHttpClient: OkHttpClient, retrofit: Retrofit): NextcloudRepositoryWithNoCookies { fun createComponentsWithEmptyCookieJar(okHttpClient: OkHttpClient, retrofit: Retrofit, androidApplication: Application): ComponentsWithEmptyCookieJar {
return NextcloudRepositoryWithNoCookies(okHttpClient, retrofit) return ComponentsWithEmptyCookieJar(okHttpClient, retrofit, androidApplication)
} }
fun createCookieManager(): CookieManager { fun createCookieManager(): CookieManager {

View File

@ -28,12 +28,12 @@ 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(userId: Long, token: String): Conversation?
suspend fun clearConversationsForUser(userId: Long) suspend fun clearConversationsForUser(userId: Long)
suspend fun saveConversationsForUser( suspend fun saveConversationsForUser(
userId: Long, userId: Long,
conversations: List<Conversation> conversations: List<Conversation>,
deleteOutdated: Boolean
): List<Long> ): List<Long>
suspend fun setChangingValueForConversation( suspend fun setChangingValueForConversation(

View File

@ -203,7 +203,7 @@ class ConversationsListViewModel constructor(
conversationsRepository.saveConversationsForUser( conversationsRepository.saveConversationsForUser(
internalUserId, internalUserId,
mutableList) mutableList, true)
messageData = "" messageData = ""
conversationsLoadingLock.unlock() conversationsLoadingLock.unlock()
} }

View File

@ -86,7 +86,8 @@ abstract class ConversationsDao {
@Transaction @Transaction
open suspend fun updateConversationsForUser( open suspend fun updateConversationsForUser(
userId: Long, userId: Long,
newConversations: Array<ConversationEntity> newConversations: Array<ConversationEntity>,
deleteOutdated: Boolean
): List<Long> { ): List<Long> {
val timestamp = System.currentTimeMillis() val timestamp = System.currentTimeMillis()
@ -96,7 +97,9 @@ abstract class ConversationsDao {
} }
val list = saveConversationsWithInsert(*conversationsWithTimestampApplied.toTypedArray()) val list = saveConversationsWithInsert(*conversationsWithTimestampApplied.toTypedArray())
if (deleteOutdated) {
deleteConversationsForUserWithTimestamp(userId, timestamp) deleteConversationsForUserWithTimestamp(userId, timestamp)
}
return list return list
} }
} }

View File

@ -4,6 +4,8 @@ import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory
import android.graphics.drawable.Drawable
import android.media.AudioAttributes import android.media.AudioAttributes
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
@ -13,32 +15,47 @@ import android.util.Base64
import android.util.Log import android.util.Log
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.emoji.text.EmojiCompat import androidx.emoji.text.EmojiCompat
import androidx.work.Data import androidx.work.Data
import androidx.work.OneTimeWorkRequest import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager import androidx.work.WorkManager
import coil.target.Target
import coil.transform.CircleCropTransformation
import com.bluelinelabs.logansquare.LoganSquare import com.bluelinelabs.logansquare.LoganSquare
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.activities.MagicCallActivity import com.nextcloud.talk.activities.MagicCallActivity
import com.nextcloud.talk.jobs.MessageNotificationWorker 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.ConversationOverall
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.source.remote.ApiErrorHandler
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.base.UseCaseResponse
import com.nextcloud.talk.newarch.local.models.UserNgEntity
import com.nextcloud.talk.newarch.utils.ComponentsWithEmptyCookieJar
import com.nextcloud.talk.newarch.utils.Images
import com.nextcloud.talk.newarch.utils.MagicJson import com.nextcloud.talk.newarch.utils.MagicJson
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.PushUtils import com.nextcloud.talk.utils.PushUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.toUtf8Bytes
import org.koin.core.KoinComponent import org.koin.core.KoinComponent
import org.koin.core.inject import org.koin.core.inject
import org.koin.core.parameter.parametersOf
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.zip.CRC32
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException import javax.crypto.NoSuchPaddingException
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -51,15 +68,19 @@ class CallService : Service(), KoinComponent, CoroutineScope {
val appPreferences: AppPreferences by inject() val appPreferences: AppPreferences by inject()
val usersRepository: UsersRepository by inject() val usersRepository: UsersRepository by inject()
val conversationsRepository: ConversationsRepository by inject()
val retrofit: Retrofit by inject()
val componentsWithEmptyCookieJar: ComponentsWithEmptyCookieJar by inject()
val apiErrorHandler: ApiErrorHandler by inject()
var currentlyActiveNotificationId = 0L private var activeNotification = ""
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent?.let { intent?.let {
if (intent.action == BundleKeys.KEY_INCOMING_PUSH_MESSSAGE) { if (it.action == BundleKeys.KEY_INCOMING_PUSH_MESSSAGE) {
decryptMessage(intent.getStringExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT), intent.getStringExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE)) decryptMessage(it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT), it.getStringExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE))
} else if (intent.action == BundleKeys.KEY_REJECT_INCOMING_CALL || intent.action == BundleKeys.KEY_SHOW_INCOMING_CALL) { } else if (it.action == BundleKeys.KEY_REJECT_INCOMING_CALL || it.action == BundleKeys.KEY_SHOW_INCOMING_CALL) {
if (intent.getLongExtra(BundleKeys.KEY_NOTIFICATION_ID, -1L) == currentlyActiveNotificationId) { if (it.getStringExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION) == activeNotification) {
stopForeground(true) stopForeground(true)
} else { } else {
// do nothing? :D // do nothing? :D
@ -89,6 +110,8 @@ class CallService : Service(), KoinComponent, CoroutineScope {
cipher.init(Cipher.DECRYPT_MODE, privateKey) cipher.init(Cipher.DECRYPT_MODE, privateKey)
val decryptedSubject = cipher.doFinal(base64DecodedSubject) val decryptedSubject = cipher.doFinal(base64DecodedSubject)
decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), DecryptedPushMessage::class.java) decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), DecryptedPushMessage::class.java)
val conversation = getConversationForTokenAndUser(signatureVerification.userEntity!!, decryptedPushMessage.id!!)
decryptedPushMessage.apply { decryptedPushMessage.apply {
when { when {
delete -> { delete -> {
@ -98,14 +121,14 @@ class CallService : Service(), KoinComponent, CoroutineScope {
NotificationUtils.cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!) NotificationUtils.cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!)
} }
type == "call" -> { type == "call" -> {
val timestamp = System.currentTimeMillis() if (conversation != null) {
val generatedActiveNotificationId = signatureVerification.userEntity!!.id.toString() + "@" + decryptedPushMessage.notificationId!!.toString()
val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java) val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java)
fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL
val bundle = Bundle() val bundle = Bundle()
bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id) bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id)
bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, signatureVerification.userEntity) bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, signatureVerification.userEntity)
bundle.putLong(BundleKeys.KEY_NOTIFICATION_ID, timestamp) bundle.putString(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId)
fullScreenIntent.putExtras(bundle) fullScreenIntent.putExtras(bundle)
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
@ -125,22 +148,37 @@ class CallService : Service(), KoinComponent, CoroutineScope {
val userBaseUrl = Uri.parse(signatureVerification.userEntity!!.baseUrl).toString() val userBaseUrl = Uri.parse(signatureVerification.userEntity!!.baseUrl).toString()
var largeIcon = when (conversation.type) {
Conversation.ConversationType.PUBLIC_CONVERSATION -> {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_link_black_24px)
}
Conversation.ConversationType.GROUP_CONVERSATION -> {
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_people_group_black_24px)
}
else -> {
// one to one and unknown
BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_user)
}
}
val rejectCallIntent = Intent(this@CallService, CallService::class.java) val rejectCallIntent = Intent(this@CallService, CallService::class.java)
rejectCallIntent.action = BundleKeys.KEY_REJECT_INCOMING_CALL rejectCallIntent.action = BundleKeys.KEY_REJECT_INCOMING_CALL
rejectCallIntent.putExtra(BundleKeys.KEY_NOTIFICATION_ID, timestamp) rejectCallIntent.putExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId)
val rejectCallPendingIntent = PendingIntent.getService(this@CallService, 0, rejectCallIntent, 0) val rejectCallPendingIntent = PendingIntent.getService(this@CallService, 0, rejectCallIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationBuilder = NotificationCompat.Builder(this@CallService, notificationChannelId) val notificationBuilder = NotificationCompat.Builder(this@CallService, notificationChannelId)
.setPriority(NotificationCompat.PRIORITY_HIGH) .setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_CALL) .setCategory(NotificationCompat.CATEGORY_CALL)
.setSmallIcon(R.drawable.ic_call_black_24dp) .setSmallIcon(R.drawable.ic_call_black_24dp)
.setLargeIcon(largeIcon)
.setSubText(userBaseUrl) .setSubText(userBaseUrl)
.setShowWhen(true) .setShowWhen(true)
.setWhen(timestamp) .setWhen(System.currentTimeMillis())
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString())) .setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString()))
.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) //.setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true) .setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING) .setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING)
@ -148,13 +186,38 @@ class CallService : Service(), KoinComponent, CoroutineScope {
notificationBuilder.setVibrate(vibrationEffect) notificationBuilder.setVibrate(vibrationEffect)
} }
val notification = notificationBuilder.build()
notification.flags = notification.flags or Notification.FLAG_INSISTENT
//checkIfCallIsActive(signatureVerification, decryptedPushMessage) //checkIfCallIsActive(signatureVerification, decryptedPushMessage)
currentlyActiveNotificationId = timestamp
startForeground(timestamp.toInt(), notification) if (conversation.type == Conversation.ConversationType.ONE_TO_ONE_CONVERSATION) {
val target = object : Target {
override fun onSuccess(result: Drawable) {
super.onSuccess(result)
largeIcon = result.toBitmap()
notificationBuilder.setLargeIcon(largeIcon)
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
override fun onError(error: Drawable?) {
super.onError(error)
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
}
val avatarUrl = ApiUtils.getUrlForAvatarWithName(signatureVerification.userEntity!!.baseUrl, conversation.name, R.dimen.avatar_size)
val imageLoader = componentsWithEmptyCookieJar.getImageLoader()
val request = Images().getRequestForUrl(
imageLoader, applicationContext, avatarUrl, signatureVerification.userEntity,
target, null, CircleCropTransformation())
imageLoader.load(request)
} else {
showNotification(notificationBuilder, signatureVerification.userEntity!!, decryptedPushMessage.notificationId!!, generatedActiveNotificationId)
}
}
} }
else -> { else -> {
if (conversation != null) {
val json = Json(MagicJson.customJsonConfiguration) val json = Json(MagicJson.customJsonConfiguration)
val messageData = Data.Builder() val messageData = Data.Builder()
@ -166,6 +229,7 @@ class CallService : Service(), KoinComponent, CoroutineScope {
} }
} }
} }
}
} else { } else {
// do absolutely nothing // do absolutely nothing
} }
@ -182,6 +246,37 @@ class CallService : Service(), KoinComponent, CoroutineScope {
} }
} }
private suspend fun getConversationForTokenAndUser(user: UserNgEntity, conversationToken: String): Conversation? {
var conversation = conversationsRepository.getConversationForUserWithToken(user.id, conversationToken)
if (conversation == null) {
val getConversationUseCase = GetConversationUseCase(componentsWithEmptyCookieJar.getRepository(), apiErrorHandler)
runBlocking {
getConversationUseCase.invoke(this, parametersOf(user, conversationToken), object : UseCaseResponse<ConversationOverall> {
override suspend fun onSuccess(result: ConversationOverall) {
val internalConversation = result.ocs.data
conversationsRepository.saveConversationsForUser(user.id, listOf(internalConversation), false)
conversation = result.ocs.data
}
override suspend fun onError(errorModel: ErrorModel?) {
conversation = null
}
})
}
}
return conversation
}
private fun showNotification(builder: NotificationCompat.Builder, user: UserNgEntity, internalNotificationId: Long, generatedNotificationId: String) {
activeNotification = generatedNotificationId
val notification = builder.build()
notification.extras.putLong(BundleKeys.KEY_INTERNAL_USER_ID, user.id)
notification.extras.putLong(BundleKeys.KEY_NOTIFICATION_ID, internalNotificationId)
notification.flags = notification.flags or Notification.FLAG_INSISTENT
startForeground(generatedNotificationId.hashCode(), notification)
}
override fun onBind(intent: Intent?): IBinder? { override fun onBind(intent: Intent?): IBinder? {
return null return null
} }

View File

@ -71,7 +71,7 @@ class GlobalService constructor(usersRepository: UsersRepository,
object : UseCaseResponse<ConversationOverall> { object : UseCaseResponse<ConversationOverall> {
override suspend fun onSuccess(result: ConversationOverall) { override suspend fun onSuccess(result: ConversationOverall) {
currentUser?.let { currentUser?.let {
conversationsRepository.saveConversationsForUser(it.id, listOf(result.ocs.data)) conversationsRepository.saveConversationsForUser(it.id, listOf(result.ocs.data), false)
globalServiceInterface.gotConversationInfoForUser(it, result.ocs.data, GlobalServiceInterface.OperationStatus.STATUS_OK) globalServiceInterface.gotConversationInfoForUser(it, result.ocs.data, GlobalServiceInterface.OperationStatus.STATUS_OK)
} }
} }
@ -94,7 +94,7 @@ class GlobalService constructor(usersRepository: UsersRepository,
object : UseCaseResponse<ConversationOverall> { object : UseCaseResponse<ConversationOverall> {
override suspend fun onSuccess(result: ConversationOverall) { override suspend fun onSuccess(result: ConversationOverall) {
currentUser?.let { currentUser?.let {
conversationsRepository.saveConversationsForUser(it.id, listOf(result.ocs.data)) conversationsRepository.saveConversationsForUser(it.id, listOf(result.ocs.data), false)
currentConversation = conversationsRepository.getConversationForUserWithToken(it.id, result.ocs!!.data!!.token!!) currentConversation = conversationsRepository.getConversationForUserWithToken(it.id, result.ocs!!.data!!.token!!)
globalServiceInterface.joinedConversationForUser(it, currentConversation, GlobalServiceInterface.OperationStatus.STATUS_OK) globalServiceInterface.joinedConversationForUser(it, currentConversation, GlobalServiceInterface.OperationStatus.STATUS_OK)
} }

View File

@ -22,6 +22,12 @@
package com.nextcloud.talk.newarch.utils package com.nextcloud.talk.newarch.utils
import android.app.Application
import android.os.Build
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import com.nextcloud.talk.newarch.data.repository.online.NextcloudTalkRepositoryImpl import com.nextcloud.talk.newarch.data.repository.online.NextcloudTalkRepositoryImpl
import com.nextcloud.talk.newarch.data.source.remote.ApiService import com.nextcloud.talk.newarch.data.source.remote.ApiService
import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository
@ -31,14 +37,35 @@ import org.koin.core.KoinComponent
import retrofit2.Retrofit import retrofit2.Retrofit
import java.net.CookieManager import java.net.CookieManager
class NextcloudRepositoryWithNoCookies( class ComponentsWithEmptyCookieJar(
private val okHttpClient: OkHttpClient, private val okHttpClient: OkHttpClient,
private val retrofit: Retrofit private val retrofit: Retrofit,
private val androidApplication: Application
) : KoinComponent { ) : KoinComponent {
fun getRepository(): NextcloudTalkRepository { fun getRepository(): NextcloudTalkRepository {
return NextcloudTalkRepositoryImpl(retrofit.newBuilder().client( return NextcloudTalkRepositoryImpl(retrofit.newBuilder().client(getOkHttpClient())
okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build())
.build().create(ApiService::class.java)) .build().create(ApiService::class.java))
} }
fun getOkHttpClient(): OkHttpClient {
return okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()
}
fun getImageLoader(): ImageLoader {
return ImageLoader(androidApplication) {
availableMemoryPercentage(0.5)
bitmapPoolPercentage(0.5)
crossfade(false)
okHttpClient(getOkHttpClient())
componentRegistry {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
add(ImageDecoderDecoder())
} else {
add(GifDecoder())
}
add(SvgDecoder(androidApplication))
}
}
}
} }

View File

@ -187,8 +187,7 @@ object NotificationUtils {
if (notification != null && !notification.extras.isEmpty) { if (notification != null && !notification.extras.isEmpty) {
if (conversationUser.id == notification.extras.getLong( if (conversationUser.id == notification.extras.getLong(
BundleKeys.KEY_INTERNAL_USER_ID BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)
) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID)
) { ) {
notificationManager.cancel(statusBarNotification.id) notificationManager.cancel(statusBarNotification.id)
} }

View File

@ -59,6 +59,7 @@ object BundleKeys {
val KEY_ACCOUNT = "KEY_ACCOUNT" val KEY_ACCOUNT = "KEY_ACCOUNT"
val KEY_FILE_ID = "KEY_FILE_ID" val KEY_FILE_ID = "KEY_FILE_ID"
val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID" val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
val KEY_ACTIVE_NOTIFICATION = "KEY_ACTIVE_NOTIFICATION"
val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID" val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID"
val KEY_ENCRYPTED_SUBJECT = "KEY_ENCRYPTED_SUBJECT" val KEY_ENCRYPTED_SUBJECT = "KEY_ENCRYPTED_SUBJECT"