Bläddra i källkod

First step towards working calls on Q

Signed-off-by: Mario Danic <mario@lovelyhq.com>
Mario Danic 5 år sedan
förälder
incheckning
9e2fbc076c

+ 1 - 0
app/build.gradle

@@ -253,6 +253,7 @@ dependencies {
     })
     findbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.9.0'
     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 {

+ 1 - 1
app/gplay.gradle

@@ -19,5 +19,5 @@
  */
 
 dependencies {
-  implementation "com.google.firebase:firebase-messaging:20.0.0"
+  implementation "com.google.firebase:firebase-messaging:20.1.0"
 }

+ 0 - 73
app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.java

@@ -1,73 +0,0 @@
-/*
- * Nextcloud Talk application
- *
- * @author Mario Danic
- * Copyright (C) 2017 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.services.firebase;
-
-import android.annotation.SuppressLint;
-
-import autodagger.AutoInjector;
-import com.google.firebase.messaging.FirebaseMessagingService;
-import com.google.firebase.messaging.RemoteMessage;
-import com.nextcloud.talk.application.NextcloudTalkApplication;
-import com.nextcloud.talk.jobs.NotificationWorker;
-import com.nextcloud.talk.jobs.PushRegistrationWorker;
-import com.nextcloud.talk.utils.bundle.BundleKeys;
-
-import androidx.work.Data;
-import androidx.work.OneTimeWorkRequest;
-import androidx.work.WorkManager;
-import com.nextcloud.talk.utils.preferences.AppPreferences;
-
-import javax.inject.Inject;
-
-@AutoInjector(NextcloudTalkApplication.class)
-public class MagicFirebaseMessagingService extends FirebaseMessagingService {
-    @Inject
-    AppPreferences appPreferences;
-
-    @Override
-    public void onNewToken(String token) {
-        super.onNewToken(token);
-        NextcloudTalkApplication.Companion.getSharedApplication().getComponentApplication().inject(this);
-        appPreferences.setPushToken(token);
-        OneTimeWorkRequest pushRegistrationWork = new OneTimeWorkRequest.Builder(PushRegistrationWorker.class).build();
-        WorkManager.getInstance().enqueue(pushRegistrationWork);
-    }
-
-    @SuppressLint("LongLogTag")
-    @Override
-    public void onMessageReceived(RemoteMessage remoteMessage) {
-        if (remoteMessage == null) {
-            return;
-        }
-
-        if (remoteMessage.getData() != null) {
-            Data messageData = new Data.Builder()
-                    .putString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SUBJECT(), remoteMessage.getData().get("subject"))
-                    .putString(BundleKeys.INSTANCE.getKEY_NOTIFICATION_SIGNATURE(), remoteMessage.getData().get("signature"))
-                    .build();
-
-            OneTimeWorkRequest pushNotificationWork = new OneTimeWorkRequest.Builder(NotificationWorker.class)
-                    .setInputData(messageData)
-                    .build();
-            WorkManager.getInstance().enqueue(pushNotificationWork);
-        }
-    }
-}

+ 185 - 0
app/src/gplay/java/com/nextcloud/talk/services/firebase/MagicFirebaseMessagingService.kt

@@ -0,0 +1,185 @@
+/*
+ * 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.services.firebase
+
+import android.annotation.SuppressLint
+import android.app.PendingIntent
+import android.content.Intent
+import android.media.AudioAttributes
+import android.net.Uri
+import android.os.Bundle
+import android.text.TextUtils
+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 autodagger.AutoInjector
+import com.bluelinelabs.logansquare.LoganSquare
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import com.nextcloud.talk.R
+import com.nextcloud.talk.activities.MagicCallActivity
+import com.nextcloud.talk.application.NextcloudTalkApplication
+import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
+import com.nextcloud.talk.jobs.NotificationWorker
+import com.nextcloud.talk.jobs.PushRegistrationWorker
+import com.nextcloud.talk.models.RingtoneSettings
+import com.nextcloud.talk.models.SignatureVerification
+import com.nextcloud.talk.models.json.push.DecryptedPushMessage
+import com.nextcloud.talk.utils.NotificationUtils.NOTIFICATION_CHANNEL_CALLS_V3
+import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
+import com.nextcloud.talk.utils.NotificationUtils.cancelExistingNotificationWithId
+import com.nextcloud.talk.utils.NotificationUtils.createNotificationChannel
+import com.nextcloud.talk.utils.PushUtils
+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_USER_ENTITY
+import com.nextcloud.talk.utils.preferences.AppPreferences
+import java.io.IOException
+import java.security.InvalidKeyException
+import java.security.NoSuchAlgorithmException
+import java.security.PrivateKey
+import javax.crypto.Cipher
+import javax.crypto.NoSuchPaddingException
+import javax.inject.Inject
+
+@AutoInjector(NextcloudTalkApplication::class)
+class MagicFirebaseMessagingService : FirebaseMessagingService() {
+    @JvmField
+    @Inject
+    var appPreferences: AppPreferences? = null
+
+    private var decryptedPushMessage: DecryptedPushMessage? = null
+    private var signatureVerification: SignatureVerification? = null
+
+    override fun onNewToken(token: String) {
+        super.onNewToken(token)
+        sharedApplication!!.componentApplication.inject(this)
+        appPreferences!!.pushToken = token
+        val pushRegistrationWork = OneTimeWorkRequest.Builder(PushRegistrationWorker::class.java).build()
+        WorkManager.getInstance().enqueue(pushRegistrationWork)
+    }
+
+    @SuppressLint("LongLogTag")
+    override fun onMessageReceived(remoteMessage: RemoteMessage) {
+        sharedApplication!!.componentApplication.inject(this)
+        if (!remoteMessage.data["subject"].isNullOrEmpty() && !remoteMessage.data["signature"].isNullOrEmpty()) {
+            decryptMessage(remoteMessage.data["subject"]!!, remoteMessage.data["signature"]!!)
+        }
+    }
+
+    private fun decryptMessage(subject: String, signature: String) {
+        try {
+            val base64DecodedSubject = Base64.decode(subject, Base64.DEFAULT)
+            val base64DecodedSignature = Base64.decode(signature, Base64.DEFAULT)
+            val pushUtils = PushUtils()
+            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 {
+                        timestamp = System.currentTimeMillis()
+                        if (delete) {
+                            cancelExistingNotificationWithId(applicationContext, signatureVerification!!.userEntity, notificationId)
+                        } else if (deleteAll) {
+                            cancelAllNotificationsForAccount(applicationContext, signatureVerification!!.userEntity)
+                        } else if (type == "call") {
+                            val fullScreenIntent = Intent(applicationContext, MagicCallActivity::class.java)
+                            val bundle = Bundle()
+                            bundle.putString(BundleKeys.KEY_ROOM_ID, decryptedPushMessage!!.id)
+                            bundle.putParcelable(KEY_USER_ENTITY, signatureVerification!!.userEntity)
+                            bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, true)
+                            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 ringtonePreferencesString: String? = appPreferences!!.callRingtoneUri
+                            val soundUri = if (TextUtils.isEmpty(ringtonePreferencesString)) {
+                                Uri.parse("android.resource://" + applicationContext.packageName +
+                                        "/raw/librem_by_feandesign_call")
+                            } else {
+                                try {
+                                    val ringtoneSettings = LoganSquare.parse(ringtonePreferencesString, RingtoneSettings::class.java)
+                                    ringtoneSettings.ringtoneUri
+                                } catch (exception: IOException) {
+                                    Uri.parse("android.resource://" + applicationContext.packageName + "/raw/librem_by_feandesign_call")
+                                }
+                            }
+
+                            createNotificationChannel(applicationContext!!,
+                                    NOTIFICATION_CHANNEL_CALLS_V3, 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())
+
+                            val uri = Uri.parse(signatureVerification!!.userEntity.baseUrl)
+                            val baseUrl = uri.host
+
+                            val notificationBuilder = NotificationCompat.Builder(this@MagicFirebaseMessagingService, NOTIFICATION_CHANNEL_CALLS_V3)
+                                    .setPriority(NotificationCompat.PRIORITY_MAX)
+                                    .setCategory(NotificationCompat.CATEGORY_CALL)
+                                    .setSmallIcon(R.drawable.ic_call_black_24dp)
+                                    .setSubText(baseUrl)
+                                    .setShowWhen(true)
+                                    .setWhen(decryptedPushMessage!!.timestamp)
+                                    .setContentTitle(EmojiCompat.get().process(decryptedPushMessage!!.subject))
+                                    .setAutoCancel(true)
+                                    .setOngoing(true)
+                                    .setTimeoutAfter(45000L)
+                                    .setFullScreenIntent(fullScreenPendingIntent, true)
+                                    .setSound(soundUri)
+                            startForeground(System.currentTimeMillis().toInt(), notificationBuilder.build())
+                        } else {
+                            val messageData = Data.Builder()
+                                    .putString(BundleKeys.KEY_NOTIFICATION_SUBJECT, subject)
+                                    .putString(BundleKeys.KEY_NOTIFICATION_SIGNATURE, signature)
+                                    .build()
+                            val pushNotificationWork = OneTimeWorkRequest.Builder(NotificationWorker::class.java).setInputData(messageData).build()
+                            WorkManager.getInstance().enqueue(pushNotificationWork)
+                        }
+                    }
+                }
+            } catch (e1: NoSuchAlgorithmException) {
+                Log.d(NotificationWorker.TAG, "No proper algorithm to decrypt the message " + e1.localizedMessage)
+            } catch (e1: NoSuchPaddingException) {
+                Log.d(NotificationWorker.TAG, "No proper padding to decrypt the message " + e1.localizedMessage)
+            } catch (e1: InvalidKeyException) {
+                Log.d(NotificationWorker.TAG, "Invalid private key " + e1.localizedMessage)
+            }
+        } catch (exception: Exception) {
+            Log.d(NotificationWorker.TAG, "Something went very wrong " + exception.localizedMessage)
+        }
+
+    }
+}

+ 1 - 0
app/src/main/AndroidManifest.xml

@@ -50,6 +50,7 @@
     <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 
     <uses-permission
         android:name="android.permission.USE_CREDENTIALS"

+ 1 - 1
app/src/main/java/com/nextcloud/talk/controllers/CallNotificationController.java

@@ -256,7 +256,7 @@ public class CallNotificationController extends BaseController {
                     @Override
                     public void onNext(RoomsOverall roomsOverall) {
                         for (Conversation conversation : roomsOverall.getOcs().getData()) {
-                            if (roomId.equals(conversation.getRoomId())) {
+                            if (roomId.equals(conversation.getRoomId()) || roomId.equals(conversation.token)) {
                                 currentConversation = conversation;
                                 runAllThings();
                                 break;

+ 25 - 3
app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.java

@@ -343,21 +343,43 @@ public class NotificationWorker extends Worker {
                     groupName);*/
 
             if (decryptedPushMessage.getType().equals("chat") || decryptedPushMessage.getType().equals("room")) {
+                AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder().setContentType
+                        (AudioAttributes.CONTENT_TYPE_SONIFICATION);
+                audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT);
+
+                String ringtonePreferencesString;
+                Uri soundUri;
+
+                ringtonePreferencesString = appPreferences.getMessageRingtoneUri();
+                if (TextUtils.isEmpty(ringtonePreferencesString)) {
+                    soundUri = Uri.parse("android.resource://" + context.getPackageName() +
+                            "/raw/librem_by_feandesign_message");
+                } else {
+                    try {
+                        RingtoneSettings ringtoneSettings = LoganSquare.parse
+                                (ringtonePreferencesString, RingtoneSettings.class);
+                        soundUri = ringtoneSettings.getRingtoneUri();
+                    } catch (IOException exception) {
+                        soundUri = Uri.parse("android.resource://" + context.getPackageName() +
+                                "/raw/librem_by_feandesign_message");
+                    }
+                }
+
                 NotificationUtils.INSTANCE.createNotificationChannel(context,
                         NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3(), context.getResources()
                                 .getString(R.string.nc_notification_channel_messages), context.getResources()
                                 .getString(R.string.nc_notification_channel_messages), true,
-                        NotificationManager.IMPORTANCE_HIGH);
+                        NotificationManager.IMPORTANCE_HIGH, soundUri, audioAttributesBuilder.build());
 
                 notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_MESSAGES_V3());
             } else {
-                NotificationUtils.INSTANCE.createNotificationChannel(context,
+                /*NotificationUtils.INSTANCE.createNotificationChannel(context,
                         NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3(), context.getResources()
                                 .getString(R.string.nc_notification_channel_calls), context.getResources()
                                 .getString(R.string.nc_notification_channel_calls_description), true,
                         NotificationManager.IMPORTANCE_HIGH);
 
-                notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3());
+                notificationBuilder.setChannelId(NotificationUtils.INSTANCE.getNOTIFICATION_CHANNEL_CALLS_V3());*/
             }
 
         } else {

+ 2 - 2
app/src/main/java/com/nextcloud/talk/models/RingtoneSettings.java

@@ -34,7 +34,7 @@ import org.parceler.Parcel;
 public class RingtoneSettings {
     @JsonField(name = "ringtoneUri", typeConverter = UriTypeConverter.class)
     @Nullable
-    Uri ringtoneUri;
+   public Uri ringtoneUri;
     @JsonField(name = "ringtoneName")
-    String ringtoneName;
+    public String ringtoneName;
 }

+ 10 - 10
app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.java

@@ -31,32 +31,32 @@ import org.parceler.Parcel;
 @JsonObject
 public class DecryptedPushMessage {
     @JsonField(name = "app")
-    String app;
+    public String app;
 
     @JsonField(name = "type")
-    String type;
+    public String type;
 
     @JsonField(name = "subject")
-    String subject;
+    public String subject;
 
     @JsonField(name = "id")
-    String id;
+    public String id;
 
     @JsonField(name = "nid")
-    long notificationId;
+    public long notificationId;
 
     @JsonField(name = "delete")
-    boolean delete;
+    public boolean delete;
 
     @JsonField(name = "delete-all")
-    boolean deleteAll;
+    public boolean deleteAll;
 
     @JsonIgnore
-    NotificationUser notificationUser;
+    public NotificationUser notificationUser;
 
     @JsonIgnore
-    String text;
+    public String text;
 
     @JsonIgnore
-    long timestamp;
+    public long timestamp;
 }

+ 5 - 2
app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt

@@ -26,6 +26,8 @@ import android.app.NotificationChannel
 import android.app.NotificationChannelGroup
 import android.app.NotificationManager
 import android.content.Context
+import android.media.AudioAttributes
+import android.net.Uri
 import android.os.Build
 import android.service.notification.StatusBarNotification
 import com.nextcloud.talk.R
@@ -44,7 +46,7 @@ object NotificationUtils {
     fun createNotificationChannel(context: Context,
                                   channelId: String, channelName: String,
                                   channelDescription: String, enableLights: Boolean,
-                                  importance: Int) {
+                                  importance: Int, sound: Uri, audioAttributes: AudioAttributes) {
 
         val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
 
@@ -56,7 +58,8 @@ object NotificationUtils {
             channel.description = channelDescription
             channel.enableLights(enableLights)
             channel.lightColor = R.color.colorPrimary
-            channel.setSound(null, null)
+            channel.setSound(sound, audioAttributes)
+            channel.shouldVibrate()
 
             notificationManager.createNotificationChannel(channel)
         }