mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-20 03:05:01 +01:00
Lots of progress on new notifications system
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
11f013a762
commit
8252240b3b
@ -44,12 +44,14 @@ import com.nextcloud.talk.jobs.MessageNotificationWorker
|
|||||||
import com.nextcloud.talk.models.SignatureVerification
|
import com.nextcloud.talk.models.SignatureVerification
|
||||||
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
|
import com.nextcloud.talk.models.json.push.DecryptedPushMessage
|
||||||
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||||
|
import com.nextcloud.talk.newarch.services.CallService
|
||||||
import com.nextcloud.talk.newarch.utils.MagicJson
|
import com.nextcloud.talk.newarch.utils.MagicJson
|
||||||
import com.nextcloud.talk.utils.NotificationUtils
|
import com.nextcloud.talk.utils.NotificationUtils
|
||||||
import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
|
import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
|
||||||
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationWithId
|
import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationWithId
|
||||||
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.bundle.BundleKeys.KEY_INCOMING_PUSH_MESSSAGE
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPEN_INCOMING_CALL
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_OPEN_INCOMING_CALL
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
|
||||||
import com.nextcloud.talk.utils.preferences.AppPreferences
|
import com.nextcloud.talk.utils.preferences.AppPreferences
|
||||||
@ -68,23 +70,6 @@ import javax.crypto.NoSuchPaddingException
|
|||||||
class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent {
|
class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent {
|
||||||
val tag: String = "MagicFirebaseMessagingService"
|
val tag: String = "MagicFirebaseMessagingService"
|
||||||
val appPreferences: AppPreferences by inject()
|
val appPreferences: AppPreferences by inject()
|
||||||
val retrofit: Retrofit by inject()
|
|
||||||
val okHttpClient: OkHttpClient by inject()
|
|
||||||
val eventBus: EventBus by inject()
|
|
||||||
val usersRepository: UsersRepository by inject()
|
|
||||||
|
|
||||||
private var isServiceInForeground: Boolean = false
|
|
||||||
|
|
||||||
override fun onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
//eventBus.register(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
|
||||||
super.onDestroy()
|
|
||||||
//eventBus.unregister(this)
|
|
||||||
isServiceInForeground = false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNewToken(token: String) {
|
override fun onNewToken(token: String) {
|
||||||
super.onNewToken(token)
|
super.onNewToken(token)
|
||||||
@ -93,110 +78,14 @@ class MagicFirebaseMessagingService : FirebaseMessagingService(), KoinComponent
|
|||||||
|
|
||||||
@SuppressLint("LongLogTag")
|
@SuppressLint("LongLogTag")
|
||||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||||
remoteMessage.data.let {
|
val incomingCallIntent = Intent(applicationContext, CallService::class.java)
|
||||||
decryptMessage(it["subject"]!!, it["signature"]!!)
|
incomingCallIntent.action = KEY_INCOMING_PUSH_MESSSAGE
|
||||||
}
|
incomingCallIntent.putExtra(BundleKeys.KEY_ENCRYPTED_SUBJECT, remoteMessage.data["subject"])
|
||||||
|
incomingCallIntent.putExtra(BundleKeys.KEY_ENCRYPTED_SIGNATURE, remoteMessage.data["signature"])
|
||||||
|
applicationContext.startService(incomingCallIntent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("LongLogTag")
|
@SuppressLint("LongLogTag")
|
||||||
private fun decryptMessage(subject: String, signature: String) {
|
|
||||||
val signatureVerification: SignatureVerification
|
|
||||||
val decryptedPushMessage: DecryptedPushMessage
|
|
||||||
|
|
||||||
try {
|
|
||||||
val base64DecodedSubject = Base64.decode(subject, Base64.DEFAULT)
|
|
||||||
val base64DecodedSignature = Base64.decode(signature, Base64.DEFAULT)
|
|
||||||
val pushUtils = PushUtils(usersRepository)
|
|
||||||
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
|
|
||||||
try {
|
|
||||||
signatureVerification = pushUtils.verifySignature(base64DecodedSignature, base64DecodedSubject)
|
|
||||||
if (signatureVerification.signatureValid) {
|
|
||||||
val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
|
||||||
val decryptedSubject = cipher.doFinal(base64DecodedSubject)
|
|
||||||
decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), DecryptedPushMessage::class.java)
|
|
||||||
decryptedPushMessage.apply {
|
|
||||||
when {
|
|
||||||
delete -> {
|
|
||||||
cancelExistingNotificationWithId(applicationContext, signatureVerification.userEntity!!, notificationId!!)
|
|
||||||
}
|
|
||||||
deleteAll -> {
|
|
||||||
cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!)
|
|
||||||
}
|
|
||||||
type == "call" -> {
|
|
||||||
val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java)
|
|
||||||
fullScreenIntent.action = KEY_OPEN_INCOMING_CALL
|
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.putString(BundleKeys.KEY_ROOM_ID, decryptedPushMessage.id)
|
|
||||||
bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.userEntity)
|
|
||||||
fullScreenIntent.putExtras(bundle)
|
|
||||||
|
|
||||||
fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
|
|
||||||
val fullScreenPendingIntent = PendingIntent.getActivity(this@MagicFirebaseMessagingService, 0, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
||||||
|
|
||||||
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 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 notificationBuilder = NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
|
|
||||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
||||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
||||||
.setSmallIcon(R.drawable.ic_call_black_24dp)
|
|
||||||
.setSubText(userBaseUrl)
|
|
||||||
.setShowWhen(true)
|
|
||||||
.setWhen(System.currentTimeMillis())
|
|
||||||
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage.subject.toString()))
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setOngoing(true)
|
|
||||||
//.setTimeoutAfter(45000L)
|
|
||||||
.setContentIntent(fullScreenPendingIntent)
|
|
||||||
.setFullScreenIntent(fullScreenPendingIntent, true)
|
|
||||||
.setSound(NotificationUtils.getCallSoundUri(applicationContext, appPreferences), AudioManager.STREAM_RING)
|
|
||||||
|
|
||||||
if (vibrationEffect != null) {
|
|
||||||
notificationBuilder.setVibrate(vibrationEffect)
|
|
||||||
}
|
|
||||||
|
|
||||||
val notification = notificationBuilder.build()
|
|
||||||
notification.flags = notification.flags or Notification.FLAG_INSISTENT
|
|
||||||
isServiceInForeground = true
|
|
||||||
checkIfCallIsActive(signatureVerification, decryptedPushMessage)
|
|
||||||
startForeground(timestamp.toInt(), notification)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e1: NoSuchAlgorithmException) {
|
|
||||||
Log.d(tag, "No proper algorithm to decrypt the message " + e1.localizedMessage)
|
|
||||||
} catch (e1: NoSuchPaddingException) {
|
|
||||||
Log.d(tag, "No proper padding to decrypt the message " + e1.localizedMessage)
|
|
||||||
} catch (e1: InvalidKeyException) {
|
|
||||||
Log.d(tag, "Invalid private key " + e1.localizedMessage)
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Log.d(tag, "Something went very wrong " + exception.localizedMessage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) {
|
private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) {
|
||||||
/*val ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java)
|
/*val ncApi = retrofit.newBuilder().client(okHttpClient.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java)
|
||||||
|
@ -125,6 +125,8 @@
|
|||||||
<service
|
<service
|
||||||
android:name="com.novoda.merlin.MerlinService"
|
android:name="com.novoda.merlin.MerlinService"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
<service android:name=".newarch.services.CallService"
|
||||||
|
android:exported="false"/>
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}"
|
android:authorities="${applicationId}"
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
package com.nextcloud.talk.activities
|
package com.nextcloud.talk.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -36,6 +37,7 @@ import com.nextcloud.talk.R
|
|||||||
import com.nextcloud.talk.controllers.CallController
|
import com.nextcloud.talk.controllers.CallController
|
||||||
import com.nextcloud.talk.controllers.CallNotificationController
|
import com.nextcloud.talk.controllers.CallNotificationController
|
||||||
import com.nextcloud.talk.events.ConfigurationChangeEvent
|
import com.nextcloud.talk.events.ConfigurationChangeEvent
|
||||||
|
import com.nextcloud.talk.newarch.services.CallService
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
|
|
||||||
class MagicCallActivity : BaseActivity() {
|
class MagicCallActivity : BaseActivity() {
|
||||||
@ -64,7 +66,13 @@ class MagicCallActivity : BaseActivity() {
|
|||||||
router!!.setPopsLastView(false)
|
router!!.setPopsLastView(false)
|
||||||
|
|
||||||
if (!router!!.hasRootController()) {
|
if (!router!!.hasRootController()) {
|
||||||
if (intent.getBooleanExtra(BundleKeys.KEY_OPEN_INCOMING_CALL, false)) {
|
if (intent.action == BundleKeys.KEY_OPEN_INCOMING_CALL) {
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
router!!.setRoot(
|
router!!.setRoot(
|
||||||
RouterTransaction.with(CallNotificationController(intent.extras!!))
|
RouterTransaction.with(CallNotificationController(intent.extras!!))
|
||||||
.pushChangeHandler(HorizontalChangeHandler())
|
.pushChangeHandler(HorizontalChangeHandler())
|
||||||
|
@ -22,6 +22,7 @@ package com.nextcloud.talk.controllers
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
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
|
||||||
@ -39,6 +40,7 @@ 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
|
||||||
@ -62,6 +64,7 @@ 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.DoNotDisturbUtils
|
||||||
import com.nextcloud.talk.utils.bundle.BundleKeys
|
import com.nextcloud.talk.utils.bundle.BundleKeys
|
||||||
@ -80,7 +83,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 org.parceler.Parcels
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
class CallNotificationController(private val originalBundle: Bundle) : BaseController() {
|
class CallNotificationController(private val originalBundle: Bundle) : BaseController() {
|
||||||
@ -111,7 +113,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
|
|||||||
@JvmField
|
@JvmField
|
||||||
@BindView(R.id.incomingTextRelativeLayout)
|
@BindView(R.id.incomingTextRelativeLayout)
|
||||||
var incomingTextRelativeLayout: RelativeLayout? = null
|
var incomingTextRelativeLayout: RelativeLayout? = null
|
||||||
private val roomId: String
|
private val conversationToken: String
|
||||||
private val userBeingCalled: UserNgEntity?
|
private val userBeingCalled: UserNgEntity?
|
||||||
private val credentials: String?
|
private val credentials: String?
|
||||||
private var currentConversation: Conversation? = null
|
private var currentConversation: Conversation? = null
|
||||||
@ -121,12 +123,12 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
|
|||||||
private var handler: Handler? = null
|
private var handler: Handler? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
this.roomId = originalBundle.getString(BundleKeys.KEY_ROOM_ID, "")
|
this.conversationToken = originalBundle.getString(BundleKeys.KEY_CONVERSATION_TOKEN)!!
|
||||||
this.currentConversation = Parcels.unwrap(originalBundle.getParcelable(BundleKeys.KEY_ROOM))
|
this.userBeingCalled = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)!!
|
||||||
this.userBeingCalled = originalBundle.getParcelable(BundleKeys.KEY_USER_ENTITY)
|
credentials = userBeingCalled.getCredentials()
|
||||||
credentials = ApiUtils.getCredentials(userBeingCalled!!.username, userBeingCalled.token)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun inflateView(
|
override fun inflateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup
|
container: ViewGroup
|
||||||
@ -278,13 +280,9 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
|
|||||||
handler = Handler()
|
handler = Handler()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentConversation == null) {
|
|
||||||
handleFromNotification()
|
|
||||||
} else {
|
|
||||||
runAllThings()
|
runAllThings()
|
||||||
}
|
|
||||||
|
|
||||||
var importantConversation = false
|
/*var importantConversation = false
|
||||||
val arbitraryStorageEntity: ArbitraryStorageEntity? = arbitraryStorageUtils.getStorageSetting(
|
val arbitraryStorageEntity: ArbitraryStorageEntity? = arbitraryStorageUtils.getStorageSetting(
|
||||||
userBeingCalled!!.id!!,
|
userBeingCalled!!.id!!,
|
||||||
"important_conversation",
|
"important_conversation",
|
||||||
@ -371,7 +369,7 @@ class CallNotificationController(private val originalBundle: Bundle) : BaseContr
|
|||||||
vibrator!!.cancel()
|
vibrator!!.cancel()
|
||||||
}
|
}
|
||||||
}, 10000)
|
}, 10000)
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
@ -0,0 +1,189 @@
|
|||||||
|
package com.nextcloud.talk.newarch.services
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.app.Service
|
||||||
|
import android.content.Intent
|
||||||
|
import android.media.AudioAttributes
|
||||||
|
import android.media.AudioManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.util.Base64
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.emoji.text.EmojiCompat
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
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.push.DecryptedPushMessage
|
||||||
|
import com.nextcloud.talk.newarch.domain.repository.offline.UsersRepository
|
||||||
|
import com.nextcloud.talk.newarch.utils.MagicJson
|
||||||
|
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.serialization.json.Json
|
||||||
|
import org.koin.core.KoinComponent
|
||||||
|
import org.koin.core.inject
|
||||||
|
import java.security.InvalidKeyException
|
||||||
|
import java.security.NoSuchAlgorithmException
|
||||||
|
import java.security.PrivateKey
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import javax.crypto.NoSuchPaddingException
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
|
class CallService : Service(), KoinComponent, CoroutineScope {
|
||||||
|
val tag: String = "CallService"
|
||||||
|
private var coroutineJob: Job = Job()
|
||||||
|
override val coroutineContext: CoroutineContext
|
||||||
|
get() = Dispatchers.IO + coroutineJob
|
||||||
|
|
||||||
|
val appPreferences: AppPreferences by inject()
|
||||||
|
val usersRepository: UsersRepository by inject()
|
||||||
|
|
||||||
|
var currentlyActiveNotificationId = 0L
|
||||||
|
|
||||||
|
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) {
|
||||||
|
stopForeground(true)
|
||||||
|
} else {
|
||||||
|
// do nothing? :D
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return START_NOT_STICKY
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun decryptMessage(subject: String, signature: String) = coroutineContext.run {
|
||||||
|
launch {
|
||||||
|
val signatureVerification: SignatureVerification
|
||||||
|
val decryptedPushMessage: DecryptedPushMessage
|
||||||
|
|
||||||
|
try {
|
||||||
|
val base64DecodedSubject = Base64.decode(subject, Base64.DEFAULT)
|
||||||
|
val base64DecodedSignature = Base64.decode(signature, Base64.DEFAULT)
|
||||||
|
val pushUtils = PushUtils(usersRepository)
|
||||||
|
val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey
|
||||||
|
try {
|
||||||
|
signatureVerification = pushUtils.verifySignature(base64DecodedSignature, base64DecodedSubject)
|
||||||
|
if (signatureVerification.signatureValid) {
|
||||||
|
val cipher = Cipher.getInstance("RSA/None/PKCS1Padding")
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, privateKey)
|
||||||
|
val decryptedSubject = cipher.doFinal(base64DecodedSubject)
|
||||||
|
decryptedPushMessage = LoganSquare.parse(String(decryptedSubject), DecryptedPushMessage::class.java)
|
||||||
|
decryptedPushMessage.apply {
|
||||||
|
when {
|
||||||
|
delete -> {
|
||||||
|
NotificationUtils.cancelExistingNotificationWithId(applicationContext, signatureVerification.userEntity!!, notificationId!!)
|
||||||
|
}
|
||||||
|
deleteAll -> {
|
||||||
|
NotificationUtils.cancelAllNotificationsForAccount(applicationContext, signatureVerification.userEntity!!)
|
||||||
|
}
|
||||||
|
type == "call" -> {
|
||||||
|
val timestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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 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 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)
|
||||||
|
|
||||||
|
if (vibrationEffect != null) {
|
||||||
|
notificationBuilder.setVibrate(vibrationEffect)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// do absolutely nothing
|
||||||
|
}
|
||||||
|
} catch (e1: NoSuchAlgorithmException) {
|
||||||
|
Log.d(tag, "No proper algorithm to decrypt the message " + e1.localizedMessage)
|
||||||
|
} catch (e1: NoSuchPaddingException) {
|
||||||
|
Log.d(tag, "No proper padding to decrypt the message " + e1.localizedMessage)
|
||||||
|
} catch (e1: InvalidKeyException) {
|
||||||
|
Log.d(tag, "Invalid private key " + e1.localizedMessage)
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.d(tag, "Something went very wrong " + exception.localizedMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -61,6 +61,12 @@ object BundleKeys {
|
|||||||
val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
|
val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
|
||||||
val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID"
|
val KEY_CONVERSATION_ID = "KEY_CONVERSATION_ID"
|
||||||
|
|
||||||
|
val KEY_ENCRYPTED_SUBJECT = "KEY_ENCRYPTED_SUBJECT"
|
||||||
|
val KEY_ENCRYPTED_SIGNATURE = "KEY_ENCRYPTED_SIGNATURE"
|
||||||
|
|
||||||
|
val KEY_REJECT_INCOMING_CALL = "KEY_REJECT_INCOMING_CALL"
|
||||||
|
val KEY_SHOW_INCOMING_CALL = "KEY_SHOW_INCOMING_CALL"
|
||||||
|
val KEY_INCOMING_PUSH_MESSSAGE = "KEY_INCOMING_PUSH_MESSAGE"
|
||||||
val KEY_DECRYPTED_PUSH_MESSAGE = "KEY_DECRYPTED_PUSH_MESSAGE"
|
val KEY_DECRYPTED_PUSH_MESSAGE = "KEY_DECRYPTED_PUSH_MESSAGE"
|
||||||
val KEY_SIGNATURE_VERIFICATION = "KEY_SIGNATURE_VERIFICATION"
|
val KEY_SIGNATURE_VERIFICATION = "KEY_SIGNATURE_VERIFICATION"
|
||||||
}
|
}
|
||||||
|
@ -344,4 +344,5 @@
|
|||||||
<string name="nc_search_for_more">Search for more participants</string>
|
<string name="nc_search_for_more">Search for more participants</string>
|
||||||
<string name="nc_new_group">New group</string>
|
<string name="nc_new_group">New group</string>
|
||||||
<string name="nc_search_empty_contacts">Where did they all hide?</string>
|
<string name="nc_search_empty_contacts">Where did they all hide?</string>
|
||||||
|
<string name="reject_call">Reject </string>
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user