Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
Mario Danic 2020-02-12 10:57:35 +01:00
parent 9e2fbc076c
commit 39c2dced64
No known key found for this signature in database
GPG Key ID: CDE0BBD2738C4CC0
7 changed files with 174 additions and 32 deletions

View File

@ -39,8 +39,8 @@ android {
targetSdkVersion 28 targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 129 versionCode 130
versionName "8.0.2" versionName "8.0.3"
flavorDimensions "default" flavorDimensions "default"
renderscriptTargetApi 19 renderscriptTargetApi 19
@ -253,7 +253,6 @@ dependencies {
}) })
findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0' findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0'
findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6' findbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.4.6'
implementation "com.google.firebase:firebase-messaging:20.1.0"
} }
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {

View File

@ -20,6 +20,7 @@
package com.nextcloud.talk.services.firebase package com.nextcloud.talk.services.firebase
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Notification
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Intent import android.content.Intent
import android.media.AudioAttributes import android.media.AudioAttributes
@ -40,14 +41,19 @@ import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage import com.google.firebase.messaging.RemoteMessage
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.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.events.CallNotificationClick
import com.nextcloud.talk.jobs.NotificationWorker import com.nextcloud.talk.jobs.NotificationWorker
import com.nextcloud.talk.jobs.PushRegistrationWorker import com.nextcloud.talk.jobs.PushRegistrationWorker
import com.nextcloud.talk.models.RingtoneSettings import com.nextcloud.talk.models.RingtoneSettings
import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.SignatureVerification
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.models.json.push.DecryptedPushMessage import com.nextcloud.talk.models.json.push.DecryptedPushMessage
import com.nextcloud.talk.utils.NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3 import com.nextcloud.talk.utils.ApiUtils
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.NotificationUtils.createNotificationChannel import com.nextcloud.talk.utils.NotificationUtils.createNotificationChannel
@ -56,7 +62,17 @@ import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_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
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.JavaNetCookieJar
import okhttp3.OkHttpClient
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import retrofit2.Retrofit
import java.io.IOException import java.io.IOException
import java.net.CookieManager
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
import java.security.PrivateKey import java.security.PrivateKey
@ -70,9 +86,42 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
@Inject @Inject
var appPreferences: AppPreferences? = null var appPreferences: AppPreferences? = null
var isServiceInForeground: Boolean = false
private var decryptedPushMessage: DecryptedPushMessage? = null private var decryptedPushMessage: DecryptedPushMessage? = null
private var signatureVerification: SignatureVerification? = null private var signatureVerification: SignatureVerification? = null
@JvmField
@Inject
var retrofit: Retrofit? = null
@JvmField
@Inject
var okHttpClient: OkHttpClient? = null
@JvmField
@Inject
var eventBus: EventBus? = null
override fun onCreate() {
super.onCreate()
sharedApplication!!.componentApplication.inject(this)
eventBus?.register(this)
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
fun onMessageEvent(event: CallNotificationClick) {
isServiceInForeground = false
stopForeground(true)
}
override fun onDestroy() {
isServiceInForeground = false
eventBus?.unregister(this)
stopForeground(true)
super.onDestroy()
}
override fun onNewToken(token: String) { override fun onNewToken(token: String) {
super.onNewToken(token) super.onNewToken(token)
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
@ -137,17 +186,22 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
} }
} }
createNotificationChannel(applicationContext!!, val notificationChannelId = NotificationUtils.getNotificationChannelId(applicationContext.resources
NOTIFICATION_CHANNEL_CALLS_V3, applicationContext.resources
.getString(R.string.nc_notification_channel_calls), applicationContext.resources .getString(R.string.nc_notification_channel_calls), applicationContext.resources
.getString(R.string.nc_notification_channel_calls_description), true, .getString(R.string.nc_notification_channel_calls_description), true,
NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, audioAttributesBuilder.build()) NotificationManagerCompat.IMPORTANCE_HIGH, soundUri!!, audioAttributesBuilder.build(), NotificationUtils.getVibrationEffectForCalls(), false)
createNotificationChannel(applicationContext!!,
notificationChannelId, 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(), NotificationUtils.getVibrationEffectForCalls(), false)
val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl) val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl)
val baseUrl = uri.host val baseUrl = uri.host
val notificationBuilder = NotificationCompat.Builder(this@MagicFirebaseMessagingService, NOTIFICATION_CHANNEL_CALLS_V3) val notification = NotificationCompat.Builder(this@MagicFirebaseMessagingService, notificationChannelId)
.setPriority(NotificationCompat.PRIORITY_MAX) .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)
.setSubText(baseUrl) .setSubText(baseUrl)
@ -156,10 +210,16 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
.setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject)) .setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject))
.setAutoCancel(true) .setAutoCancel(true)
.setOngoing(true) .setOngoing(true)
.setTimeoutAfter(45000L) //.setTimeoutAfter(45000L)
.setContentIntent(fullScreenPendingIntent)
.setFullScreenIntent(fullScreenPendingIntent, true) .setFullScreenIntent(fullScreenPendingIntent, true)
.setSound(soundUri) .setSound(soundUri)
startForeground(System.currentTimeMillis().toInt(), notificationBuilder.build()) .setVibrate(NotificationUtils.getVibrationEffectForCalls())
.build()
notification.flags = notification.flags or Notification.FLAG_INSISTENT
isServiceInForeground = true
checkIfCallIsActive(signatureVerification!!, decryptedPushMessage!!)
startForeground(decryptedPushMessage!!.timestamp.toInt(), notification)
} else { } else {
val messageData = Data.Builder() val messageData = Data.Builder()
.putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject) .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
@ -180,6 +240,45 @@ class MagicFirebaseMessagingService : FirebaseMessagingService() {
} catch (exception: Exception) { } catch (exception: Exception) {
Log.d(NotificationWorker.TAG, "Something went very wrong " + exception.localizedMessage) Log.d(NotificationWorker.TAG, "Something went very wrong " + exception.localizedMessage)
} }
}
private fun checkIfCallIsActive(signatureVerification: SignatureVerification, decryptedPushMessage: DecryptedPushMessage) {
val ncApi = retrofit!!.newBuilder().client(okHttpClient!!.newBuilder().cookieJar(JavaNetCookieJar(CookieManager())).build()).build().create(NcApi::class.java)
var hasParticipantsInCall = false
var inCallOnDifferentDevice = false
ncApi.getPeersForCall(ApiUtils.getCredentials(signatureVerification.userEntity.username, signatureVerification.userEntity.token),
ApiUtils.getUrlForParticipants(signatureVerification.userEntity.baseUrl,
decryptedPushMessage.id))
.subscribeOn(Schedulers.io())
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(participantsOverall: ParticipantsOverall) {
val participantList: List<Participant> = participantsOverall.ocs.data
for (participant in participantList) {
if (participant.participantFlags != Participant.ParticipantFlags.NOT_IN_CALL) {
hasParticipantsInCall = true
if (participant.userId == signatureVerification.userEntity.userId) {
inCallOnDifferentDevice = true
break
}
}
}
if (!hasParticipantsInCall || inCallOnDifferentDevice) {
stopForeground(true)
} else if (isServiceInForeground) {
checkIfCallIsActive(signatureVerification, decryptedPushMessage)
}
}
override fun onError(e: Throwable) {}
override fun onComplete() {
}
})
} }
} }

View File

@ -61,6 +61,7 @@ import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.controllers.base.BaseController;
import com.nextcloud.talk.events.CallNotificationClick;
import com.nextcloud.talk.events.ConfigurationChangeEvent; import com.nextcloud.talk.events.ConfigurationChangeEvent;
import com.nextcloud.talk.models.RingtoneSettings; import com.nextcloud.talk.models.RingtoneSettings;
import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.database.UserEntity;
@ -145,6 +146,7 @@ public class CallNotificationController extends BaseController {
super(args); super(args);
NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this); NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
eventBus.post(new CallNotificationClick());
this.roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), ""); this.roomId = args.getString(BundleKeys.INSTANCE.getKEY_ROOM_ID(), "");
this.currentConversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM())); this.currentConversation = Parcels.unwrap(args.getParcelable(BundleKeys.INSTANCE.getKEY_ROOM()));
this.userBeingCalled = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY()); this.userBeingCalled = args.getParcelable(BundleKeys.INSTANCE.getKEY_USER_ENTITY());

View File

@ -0,0 +1,25 @@
/*
* Nextcloud Talk application
*
* @author Mario Danic
* Copyright (C) 2017-2019 Mario Danic <mario@lovelyhq.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.nextcloud.talk.events
class CallNotificationClick {
}

View File

@ -369,7 +369,7 @@ public class NotificationWorker extends Worker {
NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3(), context.getResources() NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3(), context.getResources()
.getString(R.string.nc_notification_channel_messages), context.getResources() .getString(R.string.nc_notification_channel_messages), context.getResources()
.getString(R.string.nc_notification_channel_messages), true, .getString(R.string.nc_notification_channel_messages), true,
NotificationManager.IMPORTANCE_HIGH, soundUri, audioAttributesBuilder.build()); NotificationManager.IMPORTANCE_HIGH, soundUri, audioAttributesBuilder.build(), null, false);
notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3()); notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3());
} else { } else {

View File

@ -57,26 +57,25 @@ class PackageReplacedReceiver : BroadcastReceiver() {
val notificationManager = context.getSystemService(Context val notificationManager = context.getSystemService(Context
.NOTIFICATION_SERVICE) as NotificationManager .NOTIFICATION_SERVICE) as NotificationManager
if (notificationManager != null) { if (!appPreferences.isNotificationChannelUpgradedToV2) {
if (!appPreferences!!.isNotificationChannelUpgradedToV2) { for (notificationChannelGroup in notificationManager
for (notificationChannelGroup in notificationManager .notificationChannelGroups) {
.notificationChannelGroups) { notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
notificationManager.deleteNotificationChannelGroup(notificationChannelGroup.id)
}
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES)
appPreferences!!.setNotificationChannelIsUpgradedToV2(true)
} }
if (!appPreferences!!.isNotificationChannelUpgradedToV3 && packageInfo.versionCode > 51) { notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2) notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2)
appPreferences!!.setNotificationChannelIsUpgradedToV3(true) appPreferences.setNotificationChannelIsUpgradedToV2(true)
}
} }
if (!appPreferences.isNotificationChannelUpgradedToV3 && packageInfo.versionCode > 51) {
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_MESSAGES_V2)
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V2)
appPreferences.setNotificationChannelIsUpgradedToV3(true)
}
notificationManager.deleteNotificationChannel(NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3)
} }
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
Log.e(TAG, "Failed to fetch package info") Log.e(TAG, "Failed to fetch package info")

View File

@ -33,6 +33,7 @@ import android.service.notification.StatusBarNotification
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.models.database.UserEntity import com.nextcloud.talk.models.database.UserEntity
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import java.util.*
object NotificationUtils { object NotificationUtils {
val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS" val NOTIFICATION_CHANNEL_CALLS = "NOTIFICATION_CHANNEL_CALLS"
@ -42,15 +43,26 @@ object NotificationUtils {
val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3" val NOTIFICATION_CHANNEL_MESSAGES_V3 = "NOTIFICATION_CHANNEL_MESSAGES_V3"
val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3" val NOTIFICATION_CHANNEL_CALLS_V3 = "NOTIFICATION_CHANNEL_CALLS_V3"
fun getVibrationEffectForCalls(): LongArray? {
return longArrayOf(0L, 400L, 800L, 600L, 800L, 800L, 800L, 1000L)
}
fun getNotificationChannelId(channelName: String,
channelDescription: String, enableLights: Boolean,
importance: Int, sound: Uri, audioAttributes: AudioAttributes, vibrationPattern: LongArray?, bypassDnd: Boolean): String {
return Objects.hash(channelName, channelDescription, enableLights, importance, sound, audioAttributes, vibrationPattern, bypassDnd).toString()
}
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannel(context: Context, fun createNotificationChannel(context: Context,
channelId: String, channelName: String, channelId: String, channelName: String,
channelDescription: String, enableLights: Boolean, channelDescription: String, enableLights: Boolean,
importance: Int, sound: Uri, audioAttributes: AudioAttributes) { importance: Int, sound: Uri, audioAttributes: AudioAttributes,
vibrationPattern: LongArray?, bypassDnd: Boolean = false) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(channelId) == null) {
val channel = NotificationChannel(channelId, channelName, val channel = NotificationChannel(channelId, channelName,
importance) importance)
@ -59,7 +71,13 @@ object NotificationUtils {
channel.enableLights(enableLights) channel.enableLights(enableLights)
channel.lightColor = R.color.colorPrimary channel.lightColor = R.color.colorPrimary
channel.setSound(sound, audioAttributes) channel.setSound(sound, audioAttributes)
channel.shouldVibrate() if (vibrationPattern != null) {
channel.enableVibration(true)
channel.vibrationPattern = vibrationPattern
} else {
channel.enableVibration(false)
}
channel.setBypassDnd(bypassDnd)
notificationManager.createNotificationChannel(channel) notificationManager.createNotificationChannel(channel)
} }
@ -68,7 +86,7 @@ object NotificationUtils {
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
fun createNotificationChannelGroup(context: Context, fun createNotificationChannelGroup(context: Context,
groupId: String, groupName: CharSequence) { groupId: String, groupName: CharSequence) {
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationChannelGroup = NotificationChannelGroup(groupId, groupName) val notificationChannelGroup = NotificationChannelGroup(groupId, groupName)