diff --git a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt index 32d16e419..bc19b1b46 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MagicCallActivity.kt @@ -71,7 +71,7 @@ class MagicCallActivity : BaseActivity() { val hideIncomingCallNotificationIntent = Intent(applicationContext, CallService::class.java) hideIncomingCallNotificationIntent.action = BundleKeys.KEY_SHOW_INCOMING_CALL hideIncomingCallNotificationIntent.putExtra(BundleKeys.KEY_NOTIFICATION_ID, intent.getLongExtra(BundleKeys.KEY_NOTIFICATION_ID, -1)) - applicationContext?.startService(hideIncomingCallNotificationIntent) + //applicationContext?.startService(hideIncomingCallNotificationIntent) router!!.setRoot( RouterTransaction.with(CallNotificationController(intent.extras!!)) diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt index 1d6bfc725..b3a9c7c69 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.kt @@ -21,18 +21,14 @@ package com.nextcloud.talk.controllers import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent import android.graphics.Bitmap import android.graphics.Color import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable -import android.media.AudioAttributes import android.media.MediaPlayer -import android.net.Uri -import android.os.* -import android.text.TextUtils -import android.util.Log +import android.os.Bundle +import android.os.Handler +import android.os.Vibrator import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -40,7 +36,6 @@ import android.widget.ImageView import android.widget.RelativeLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout -import androidx.core.content.ContextCompat import butterknife.BindView import butterknife.OnClick import coil.api.load @@ -51,28 +46,21 @@ import coil.transform.BlurTransformation import coil.transform.CircleCropTransformation import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler -import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.R import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.controllers.base.BaseController 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.RoomsOverall import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.newarch.local.models.UserNgEntity 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.DoNotDisturbUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.database.arbitrarystorage.ArbitraryStorageUtils import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder import com.uber.autodispose.AutoDispose import io.reactivex.Observer -import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.Dispatchers @@ -83,7 +71,6 @@ import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode import org.koin.android.ext.android.inject import org.michaelevans.colorart.library.ColorArt -import java.io.IOException class CallNotificationController(private val originalBundle: Bundle) : BaseController() { @@ -116,6 +103,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr private val conversationToken: String private val userBeingCalled: UserNgEntity? private val credentials: String? + private val notificationId: Long? private var currentConversation: Conversation? = null private var mediaPlayer: MediaPlayer? = null private var leavingScreen = false @@ -125,6 +113,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr init { this.conversationToken = originalBundle.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!! this.userBeingCalled = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)!! + this.notificationId = originalBundle.getLong(BundleKeys.KEY_NOTIFICATION_ID) 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 { - 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() { - if (conversationNameTextView != null) { + /*if (conversationNameTextView != null) { conversationNameTextView!!.text = currentConversation!!.displayName } loadAvatar() checkIfAnyParticipantsRemainInRoom() - showAnswerControls() + showAnswerControls()*/ } @SuppressLint("LongLogTag") diff --git a/app/src/main/java/com/nextcloud/talk/jobs/MessageNotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/MessageNotificationWorker.kt index 4fb18d4d0..67e72697d 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/MessageNotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/MessageNotificationWorker.kt @@ -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.utils.Images 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.NotificationUtils import com.nextcloud.talk.utils.bundle.BundleKeys @@ -73,7 +73,7 @@ class MessageNotificationWorker( workerParams: WorkerParameters ) : CoroutineWorker(context, workerParams), KoinComponent { val appPreferences: AppPreferences by inject() - private val nextcloudRepositoryWithNoCookies: NextcloudRepositoryWithNoCookies by inject() + private val componentsWithEmptyCookieJar: ComponentsWithEmptyCookieJar by inject() private val apiErrorHandler: ApiErrorHandler by inject() override suspend fun doWork(): Result = coroutineScope { @@ -107,7 +107,7 @@ class MessageNotificationWorker( } private fun showNotificationWithObjectData(coroutineScope: CoroutineScope, decryptedPushMessage: DecryptedPushMessage, signatureVerification: SignatureVerification, intent: Intent) { - val nextcloudTalkRepository = nextcloudRepositoryWithNoCookies.getRepository() + val nextcloudTalkRepository = componentsWithEmptyCookieJar.getRepository() val getNotificationUseCase = GetNotificationUseCase(nextcloudTalkRepository, apiErrorHandler) getNotificationUseCase.invoke(coroutineScope, parametersOf(signatureVerification.userEntity, decryptedPushMessage.notificationId.toString()), object : UseCaseResponse { override suspend fun onSuccess(result: NotificationOverall) { @@ -193,7 +193,6 @@ class MessageNotificationWorker( else -> { // one to one and unknown BitmapFactory.decodeResource(applicationContext.resources, R.drawable.ic_chat_black_24dp) - } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt index f8395c726..0ac61c2c6 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/offline/ConversationsRepositoryImpl.kt @@ -99,7 +99,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) : override suspend fun saveConversationsForUser( userId: Long, - conversations: List + conversations: List, + deleteOutdated: Boolean ): List { val map = conversations.map { it.toConversationEntity() @@ -108,7 +109,8 @@ class ConversationsRepositoryImpl(val conversationsDao: ConversationsDao) : return conversationsDao .updateConversationsForUser( userId, - map.toTypedArray() + map.toTypedArray(), + deleteOutdated ) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt index e451ffeda..188d1d6aa 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/data/repository/online/NextcloudTalkRepositoryImpl.kt @@ -62,7 +62,7 @@ class NextcloudTalkRepositoryImpl(private val apiService: ApiService) : Nextclou } 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 { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt index 5a8457f47..ee820e802 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/di/module/NetworkModule.kt @@ -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.GetProxyRunnable 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.preferences.AppPreferences import com.nextcloud.talk.utils.singletons.AvatarStatusCodeHolder @@ -88,13 +88,13 @@ val NetworkModule = module { single { createOkHttpClient(androidContext(), get(), get(), get(), get(), get(), get(), get()) } factory { createApiErrorHandler() } single { createNextcloudTalkRepository(get()) } - single { createNexcloudRepositoryWithNoCookies(get(), get()) } + single { createComponentsWithEmptyCookieJar(get(), get(), androidApplication()) } single { createImageLoader(androidApplication(), get()) } } -fun createNexcloudRepositoryWithNoCookies(okHttpClient: OkHttpClient, retrofit: Retrofit): NextcloudRepositoryWithNoCookies { - return NextcloudRepositoryWithNoCookies(okHttpClient, retrofit) +fun createComponentsWithEmptyCookieJar(okHttpClient: OkHttpClient, retrofit: Retrofit, androidApplication: Application): ComponentsWithEmptyCookieJar { + return ComponentsWithEmptyCookieJar(okHttpClient, retrofit, androidApplication) } fun createCookieManager(): CookieManager { diff --git a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt index 7ff2b588e..e70ff7602 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/domain/repository/offline/ConversationsRepository.kt @@ -28,12 +28,12 @@ import com.nextcloud.talk.models.json.conversations.Conversation interface ConversationsRepository { fun getConversationsForUser(userId: Long, filter: CharSequence?): LiveData> fun getShortcutTargetConversations(userId: Long): LiveData> - suspend fun getConversationForUserWithToken(userId: Long, token: String): Conversation? suspend fun clearConversationsForUser(userId: Long) suspend fun saveConversationsForUser( userId: Long, - conversations: List + conversations: List, + deleteOutdated: Boolean ): List suspend fun setChangingValueForConversation( diff --git a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt index 641671361..3ef96f4c4 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/features/conversationsList/ConversationsListViewModel.kt @@ -203,7 +203,7 @@ class ConversationsListViewModel constructor( conversationsRepository.saveConversationsForUser( internalUserId, - mutableList) + mutableList, true) messageData = "" conversationsLoadingLock.unlock() } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt index 6b27b957b..923ed9ede 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/local/dao/ConversationsDao.kt @@ -86,7 +86,8 @@ abstract class ConversationsDao { @Transaction open suspend fun updateConversationsForUser( userId: Long, - newConversations: Array + newConversations: Array, + deleteOutdated: Boolean ): List { val timestamp = System.currentTimeMillis() @@ -96,7 +97,9 @@ abstract class ConversationsDao { } val list = saveConversationsWithInsert(*conversationsWithTimestampApplied.toTypedArray()) - deleteConversationsForUserWithTimestamp(userId, timestamp) + if (deleteOutdated) { + deleteConversationsForUserWithTimestamp(userId, timestamp) + } return list } } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/services/CallService.kt b/app/src/main/java/com/nextcloud/talk/newarch/services/CallService.kt index 91ee0ca3c..60d96f47f 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/services/CallService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/services/CallService.kt @@ -4,6 +4,8 @@ import android.app.Notification import android.app.PendingIntent import android.app.Service import android.content.Intent +import android.graphics.BitmapFactory +import android.graphics.drawable.Drawable import android.media.AudioAttributes import android.media.AudioManager import android.net.Uri @@ -13,32 +15,47 @@ import android.util.Base64 import android.util.Log import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat +import androidx.core.graphics.drawable.toBitmap import androidx.emoji.text.EmojiCompat import androidx.work.Data import androidx.work.OneTimeWorkRequest import androidx.work.WorkManager +import coil.target.Target +import coil.transform.CircleCropTransformation import com.bluelinelabs.logansquare.LoganSquare import com.nextcloud.talk.R import com.nextcloud.talk.activities.MagicCallActivity import com.nextcloud.talk.jobs.MessageNotificationWorker 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.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.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.utils.ApiUtils import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.PushUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.preferences.AppPreferences -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch +import kotlinx.coroutines.* import kotlinx.serialization.json.Json +import kotlinx.serialization.toUtf8Bytes import org.koin.core.KoinComponent import org.koin.core.inject +import org.koin.core.parameter.parametersOf +import retrofit2.Retrofit import java.security.InvalidKeyException import java.security.NoSuchAlgorithmException import java.security.PrivateKey +import java.util.zip.CRC32 import javax.crypto.Cipher import javax.crypto.NoSuchPaddingException import kotlin.coroutines.CoroutineContext @@ -51,15 +68,19 @@ class CallService : Service(), KoinComponent, CoroutineScope { val appPreferences: AppPreferences 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 { intent?.let { - if (intent.action == BundleKeys.KEY_INCOMING_PUSH_MESSSAGE) { - decryptMessage(intent.getStringExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT), intent.getStringExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE)) - } else if (intent.action == BundleKeys.KEY_REJECT_INCOMING_CALL || intent.action == BundleKeys.KEY_SHOW_INCOMING_CALL) { - if (intent.getLongExtra(BundleKeys.KEY_NOTIFICATION_ID, -1L) == currentlyActiveNotificationId) { + if (it.action == BundleKeys.KEY_INCOMING_PUSH_MESSSAGE) { + 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.KEY_SHOW_INCOMING_CALL) { + if (it.getStringExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION) == activeNotification) { stopForeground(true) } else { // do nothing? :D @@ -89,6 +110,8 @@ class CallService : Service(), KoinComponent, CoroutineScope { cipher.init(Cipher.DECRYPT_MODE, privateKey) val decryptedSubject = cipher.doFinal(base64DecodedSubject) decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), DecryptedPushMessage::class.java) + val conversation = getConversationForTokenAndUser(signatureVerification.userEntity!!, decryptedPushMessage.id!!) + decryptedPushMessage.apply { when { delete -> { @@ -98,71 +121,112 @@ class CallService : Service(), KoinComponent, CoroutineScope { NotificationUtils.cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!) } 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) + fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id) + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, signatureVerification.userEntity) + bundle.putString(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId) + fullScreenIntent.putExtras(bundle) - val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java) - fullScreenIntent.action = BundleKeys.KEY_OPEN_INCOMING_CALL - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_CONVERSATION_TOKEN, decryptedPushMessage.id) - bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, signatureVerification.userEntity) - bundle.putLong(BundleKeys.KEY_NOTIFICATION_ID, timestamp) - fullScreenIntent.putExtras(bundle) + fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + val fullScreenPendingIntent = PendingIntent.getActivity(this@CallService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT) - fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - val fullScreenPendingIntent = PendingIntent.getActivity(this@CallService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT) + val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) - val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) + val soundUri = NotificationUtils.getCallSoundUri(applicationContext, appPreferences) + val vibrationEffect = NotificationUtils.getVibrationEffect(appPreferences) - val soundUri = NotificationUtils.getCallSoundUri(applicationContext, appPreferences) - val vibrationEffect = NotificationUtils.getVibrationEffect(appPreferences) + val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext, applicationContext.resources + .getString(R.string.nc_notification_channel_calls), applicationContext.resources + .getString(R.string.nc_notification_channel_calls_description), true, + NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, + audioAttributesBuilder.build(), vibrationEffect, false, null) - val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext, applicationContext.resources - .getString(R.string.nc_notification_channel_calls), applicationContext.resources - .getString(R.string.nc_notification_channel_calls_description), true, - NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, - audioAttributesBuilder.build(), vibrationEffect, false, null) + 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) - rejectCallIntent.action = BundleKeys.KEY_REJECT_INCOMING_CALL - rejectCallIntent.putExtra(BundleKeys.KEY_NOTIFICATION_ID, timestamp) - val rejectCallPendingIntent = PendingIntent.getService(this@CallService, 0, rejectCallIntent, 0) - val notificationBuilder = NotificationCompat.Builder(this@CallService, notificationChannelId) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(NotificationCompat.CATEGORY_CALL) - .setSmallIcon(R.drawable.ic_call_black_24dp) - .setSubText(userBaseUrl) - .setShowWhen(true) - .setWhen(timestamp) - .setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString())) - .setAutoCancel(true) - .setOngoing(true) - .addAction(R.drawable.ic_call_end_white_24px, resources.getString(R.string.reject_call), rejectCallPendingIntent) - //.setTimeoutAfter(45000L) - .setFullScreenIntent(fullScreenPendingIntent, true) - .setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING) + val rejectCallIntent = Intent(this@CallService, CallService::class.java) + rejectCallIntent.action = BundleKeys.KEY_REJECT_INCOMING_CALL + rejectCallIntent.putExtra(BundleKeys.KEY_ACTIVE_NOTIFICATION, generatedActiveNotificationId) + val rejectCallPendingIntent = PendingIntent.getService(this@CallService, 0, rejectCallIntent, PendingIntent.FLAG_UPDATE_CURRENT) + val notificationBuilder = NotificationCompat.Builder(this@CallService, notificationChannelId) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_CALL) + .setSmallIcon(R.drawable.ic_call_black_24dp) + .setLargeIcon(largeIcon) + .setSubText(userBaseUrl) + .setShowWhen(true) + .setWhen(System.currentTimeMillis()) + .setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString())) + .setAutoCancel(true) + .setOngoing(true) + .addAction(R.drawable.ic_call_end_white_24px, resources.getString(R.string.reject_call), rejectCallPendingIntent) + //.setTimeoutAfter(45000L) + .setContentIntent(fullScreenPendingIntent) + .setFullScreenIntent(fullScreenPendingIntent, true) + .setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING) - if (vibrationEffect != null) { - notificationBuilder.setVibrate(vibrationEffect) + if (vibrationEffect != null) { + notificationBuilder.setVibrate(vibrationEffect) + } + + //checkIfCallIsActive(signatureVerification, decryptedPushMessage) + + 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) + } } - - val notification = notificationBuilder.build() - notification.flags = notification.flags or Notification.FLAG_INSISTENT - //checkIfCallIsActive(signatureVerification, decryptedPushMessage) - currentlyActiveNotificationId = timestamp - startForeground(timestamp.toInt(), notification) } else -> { - val json = Json(MagicJson.customJsonConfiguration) + if (conversation != null) { + val json = Json(MagicJson.customJsonConfiguration) - val messageData = Data.Builder() - .putString(BundleKeys.KEY_DECRYPTED_PUSH_MESSAGE, LoganSquare.serialize(decryptedPushMessage)) - .putString(BundleKeys.KEY_SIGNATURE_VERIFICATION, json.stringify(SignatureVerification.serializer(), signatureVerification)) - .build() - val pushNotificationWork = OneTimeWorkRequest.Builder(MessageNotificationWorker::class.java).setInputData(messageData).build() - WorkManager.getInstance().enqueue(pushNotificationWork) + val messageData = Data.Builder() + .putString(BundleKeys.KEY_DECRYPTED_PUSH_MESSAGE, LoganSquare.serialize(decryptedPushMessage)) + .putString(BundleKeys.KEY_SIGNATURE_VERIFICATION, json.stringify(SignatureVerification.serializer(), signatureVerification)) + .build() + val pushNotificationWork = OneTimeWorkRequest.Builder(MessageNotificationWorker::class.java).setInputData(messageData).build() + WorkManager.getInstance().enqueue(pushNotificationWork) + } } } } @@ -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 { + 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? { return null } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt b/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt index 805254558..d5b055b88 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/services/GlobalService.kt @@ -71,7 +71,7 @@ class GlobalService constructor(usersRepository: UsersRepository, object : UseCaseResponse { override suspend fun onSuccess(result: ConversationOverall) { 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) } } @@ -94,7 +94,7 @@ class GlobalService constructor(usersRepository: UsersRepository, object : UseCaseResponse { override suspend fun onSuccess(result: ConversationOverall) { 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!!) globalServiceInterface.joinedConversationForUser(it, currentConversation, GlobalServiceInterface.OperationStatus.STATUS_OK) } diff --git a/app/src/main/java/com/nextcloud/talk/newarch/utils/NextcloudRepositoryWithNoCookies.kt b/app/src/main/java/com/nextcloud/talk/newarch/utils/ComponentsWithEmptyCookieJar.kt similarity index 59% rename from app/src/main/java/com/nextcloud/talk/newarch/utils/NextcloudRepositoryWithNoCookies.kt rename to app/src/main/java/com/nextcloud/talk/newarch/utils/ComponentsWithEmptyCookieJar.kt index c5e581df8..d9b1d52e8 100644 --- a/app/src/main/java/com/nextcloud/talk/newarch/utils/NextcloudRepositoryWithNoCookies.kt +++ b/app/src/main/java/com/nextcloud/talk/newarch/utils/ComponentsWithEmptyCookieJar.kt @@ -22,6 +22,12 @@ 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.source.remote.ApiService import com.nextcloud.talk.newarch.domain.repository.online.NextcloudTalkRepository @@ -31,14 +37,35 @@ import org.koin.core.KoinComponent import retrofit2.Retrofit import java.net.CookieManager -class NextcloudRepositoryWithNoCookies( +class ComponentsWithEmptyCookieJar( private val okHttpClient: OkHttpClient, - private val retrofit: Retrofit + private val retrofit: Retrofit, + private val androidApplication: Application ) : KoinComponent { fun getRepository(): NextcloudTalkRepository { - return NextcloudTalkRepositoryImpl(retrofit.newBuilder().client( - okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()) + return NextcloudTalkRepositoryImpl(retrofit.newBuilder().client(getOkHttpClient()) .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)) + } + } + + } } \ No newline at end of file diff --git a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt index 943e59b0f..af47c21a9 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt @@ -187,8 +187,7 @@ object NotificationUtils { if (notification != null && !notification.extras.isEmpty) { if (conversationUser.id == notification.extras.getLong( - BundleKeys.KEY_INTERNAL_USER_ID - ) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID) + BundleKeys.KEY_INTERNAL_USER_ID) && notificationId == notification.extras.getLong(BundleKeys.KEY_NOTIFICATION_ID) ) { notificationManager.cancel(statusBarNotification.id) } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index 6db46eb62..b08aacd1c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -59,6 +59,7 @@ object BundleKeys { val KEY_ACCOUNT = "KEY_ACCOUNT" val KEY_FILE_ID = "KEY_FILE_ID" val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID" + val KEY_ACTIVE_NOTIFICATION = "KEY_ACTIVE_NOTIFICATION" val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID" val KEY_ENCRYPTED_SUBJECT = "KEY_ENCRYPTED_SUBJECT"