From d191a93ce9561d8213674276e863e0ae10d3803d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Feb 2023 13:26:40 +0100 Subject: [PATCH 01/10] WIP. add recording available notification Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/activities/MainActivity.kt | 11 +++ .../java/com/nextcloud/talk/api/NcApi.java | 8 ++ .../talk/controllers/ChatController.kt | 79 ++++++++++++++++++ .../nextcloud/talk/jobs/NotificationWorker.kt | 82 ++++++++++++++----- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + 5 files changed, 160 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index a95a9d8e1..6a93c374a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -388,6 +388,17 @@ class MainActivity : BaseActivity(), ActionBarProvider { ) logRouterBackStack(router!!) } + } else if (intent.hasExtra(BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION)) { + logRouterBackStack(router!!) + remapChatController( + router!!, + intent.getParcelableExtra(KEY_USER_ENTITY)!!.id!!, + intent.getStringExtra(KEY_ROOM_TOKEN)!!, + intent.extras!!, + true, + true + ) + logRouterBackStack(router!!) } } diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 02c763aa8..4f96c7f6c 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -595,4 +595,12 @@ public interface NcApi { @DELETE Observable withdrawRequestAssistance(@Header("Authorization") String authorization, @Url String url); + + @POST + Observable sendCommonPostRequest(@Header("Authorization") String authorization, + @Url String url); + + @DELETE + Observable sendCommonDeleteRequest(@Header("Authorization") String authorization, + @Url String url); } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 01badf1ab..914f0b8db 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -146,6 +146,7 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.mention.Mention +import com.nextcloud.talk.models.json.notifications.Notification import com.nextcloud.talk.polls.ui.PollCreateDialogFragment import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity @@ -267,6 +268,7 @@ class ChatController(args: Bundle) : private var lookingIntoFuture = false var newMessagesCount = 0 var startCallFromNotification: Boolean? = null + private var recordingAvailableNotification: Notification? = null var startCallFromRoomSwitch: Boolean = false val roomId: String val voiceOnly: Boolean @@ -343,6 +345,10 @@ class ChatController(args: Bundle) : startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL) } + if (args.containsKey(BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION)) { + recordingAvailableNotification = args.getParcelable(BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION) + } + if (args.containsKey(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL)) { startCallFromRoomSwitch = args.getBoolean(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL) } @@ -926,9 +932,82 @@ class ChatController(args: Bundle) : getRoomInfo() } } + + if (recordingAvailableNotification != null) { + binding?.root?.context?.let { context -> + val dialogBuilder = MaterialAlertDialogBuilder(context) + dialogBuilder.setTitle(recordingAvailableNotification?.subject) + dialogBuilder.setMessage("Do you want to share the recording to the chat?") + + for (action in recordingAvailableNotification?.actions!!) { + if (!action.label.isNullOrEmpty() && !action.link.isNullOrEmpty()) { + if (action.primary) { + dialogBuilder.setPositiveButton(action.label!!) { dialog, which -> + sendRequest(action.type!!, action.link!!) + } + } else { + dialogBuilder.setNegativeButton(action.label!!) { dialog, which -> + sendRequest(action.type!!, action.link!!) + } + } + } + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) + val dialog = dialogBuilder.show() + + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE), + dialog.getButton(AlertDialog.BUTTON_NEGATIVE) + ) + } + } super.onViewBound(view) } + private fun sendRequest(type: String, link: String) { + if (type == "POST") { + ncApi.sendCommonPostRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + } + + override fun onError(e: Throwable) { + } + + override fun onComplete() { + // unused atm + } + }) + } else if (type == "DELETE") { + ncApi.sendCommonDeleteRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + } + + override fun onError(e: Throwable) { + } + + override fun onComplete() { + // unused atm + } + }) + } + Log.d(TAG, "type=" + type + " link=" + link) + } + private fun switchToRoom(token: String) { if (CallActivity.active) { Log.d(TAG, "CallActivity is running. Ignore to switch chat in ChatController...") diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 35b2d86f4..6a5a0e0b6 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -83,6 +83,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CA import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MESSAGE_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID @@ -164,9 +165,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } else if (isSpreedNotification()) { Log.d(TAG, "pushMessage.type: " + pushMessage.type) when (pushMessage.type) { - "chat" -> handleChatNotification() - "room" -> handleRoomNotification() - "call" -> handleCallNotification() + TYPE_CHAT -> handleChatNotification() + TYPE_ROOM -> handleRoomNotification() + TYPE_CALL -> handleCallNotification() + TYPE_RECORDING -> handleRecordingNotification() else -> Log.e(TAG, "unknown pushMessage.type") } } else { @@ -259,6 +261,18 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor checkIfCallIsActive(signatureVerification) } + private fun handleRecordingNotification() { + val chatWithRecordingIntent = Intent(context, MainActivity::class.java) + chatWithRecordingIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + val chatBundle = Bundle() + chatBundle.putString(KEY_ROOM_TOKEN, pushMessage.id) + chatBundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) + chatWithRecordingIntent.putExtras(chatBundle) + if (pushMessage.notificationId != Long.MIN_VALUE) { + showNotificationWithObjectData(chatWithRecordingIntent) + } + } + private fun initNcApiAndCredentials() { credentials = ApiUtils.getCredentials( signatureVerification.user!!.username, @@ -332,6 +346,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor override fun onNext(notificationOverall: NotificationOverall) { val ncNotification = notificationOverall.ocs!!.notification + if ("recording" == ncNotification?.objectType) { + val bundle = Bundle() + bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, ncNotification) + intent.putExtras(bundle) + } + if (ncNotification!!.messageRichParameters != null && ncNotification.messageRichParameters!!.size > 0 ) { @@ -396,11 +416,14 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val largeIcon: Bitmap val priority = NotificationCompat.PRIORITY_HIGH val smallIcon: Int = R.drawable.ic_logo - val category: String = if (CHAT == pushMessage.type || ROOM == pushMessage.type) { - Notification.CATEGORY_MESSAGE - } else { - Notification.CATEGORY_CALL + + var category: String = "" + when (pushMessage.type) { + TYPE_CHAT, TYPE_ROOM, TYPE_RECORDING -> category = Notification.CATEGORY_MESSAGE + TYPE_CALL -> category = Notification.CATEGORY_CALL + else -> Log.e(TAG, "unknown pushMessage.type") } + when (conversationType) { "one2one" -> { pushMessage.subject = "" @@ -410,7 +433,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! "public" -> largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_link_black_24px)?.toBitmap()!! else -> // assuming one2one - largeIcon = if (CHAT == pushMessage.type || ROOM == pushMessage.type) { + largeIcon = if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { ContextCompat.getDrawable(context!!, R.drawable.ic_comment)?.toBitmap()!! } else { ContextCompat.getDrawable(context!!, R.drawable.ic_call_black_24dp)?.toBitmap()!! @@ -459,10 +482,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationBuilder.setExtras(notificationInfoBundle) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - if (CHAT == pushMessage.type || ROOM == pushMessage.type) { - notificationBuilder.setChannelId( - NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_MESSAGES_V4.name - ) + when (pushMessage.type) { + TYPE_CHAT, TYPE_ROOM, TYPE_RECORDING -> { + notificationBuilder.setChannelId( + NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_MESSAGES_V4.name + ) + } } } else { // red color for the lights @@ -482,7 +507,18 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor // It is NOT the same as the notification ID used in communication with the server. val systemNotificationId: Int = activeStatusBarNotification?.id ?: calculateCRC32(System.currentTimeMillis().toString()).toInt() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && CHAT == pushMessage.type && + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + TYPE_CHAT == pushMessage.type && + pushMessage.notificationUser != null + ) { + prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) + addReplyAction(notificationBuilder, systemNotificationId) + addMarkAsReadAction(notificationBuilder, systemNotificationId) + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + TYPE_RECORDING == pushMessage.type && pushMessage.notificationUser != null ) { prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) @@ -515,8 +551,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setName(EmojiCompat.get().process(notificationUser.name!!)) .setBot("bot" == userType) notificationBuilder.setOnlyAlertOnce(true) - addReplyAction(notificationBuilder, systemNotificationId) - addMarkAsReadAction(notificationBuilder, systemNotificationId) if ("user" == userType || "guest" == userType) { val baseUrl = signatureVerification.user!!.baseUrl @@ -566,7 +600,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor systemNotificationId, messageId ) - val action = NotificationCompat.Action.Builder( + val markAsReadAction = NotificationCompat.Action.Builder( R.drawable.ic_eye, context!!.resources.getString(R.string.nc_mark_as_read), pendingIntent @@ -574,7 +608,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) .setShowsUserInterface(false) .build() - notificationBuilder.addAction(action) + notificationBuilder.addAction(markAsReadAction) } } @@ -585,7 +619,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setLabel(replyLabel) .build() - val replyPendingIntent = buildIntentForAction(DirectReplyReceiver::class.java, systemNotificationId, 0) + val replyPendingIntent = buildIntentForAction( + DirectReplyReceiver::class.java, + systemNotificationId, + 0 + ) val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) .setShowsUserInterface(false) @@ -641,7 +679,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor ) { val audioAttributesBuilder = AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - if (CHAT == pushMessage.type || ROOM == pushMessage.type) { + if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT) } else { audioAttributesBuilder.setUsage(AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) @@ -832,8 +870,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor companion object { val TAG = NotificationWorker::class.simpleName - private const val CHAT = "chat" - private const val ROOM = "room" + private const val TYPE_CHAT = "chat" + private const val TYPE_ROOM = "room" + private const val TYPE_CALL = "call" + private const val TYPE_RECORDING = "recording" private const val SPREED_APP = "spreed" private const val TIMER_START = 1 private const val TIMER_COUNT = 12 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 e89647e3c..6083f3001 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 @@ -82,4 +82,5 @@ object BundleKeys { const val KEY_IS_MODERATOR = "KEY_IS_MODERATOR" const val KEY_SWITCH_TO_ROOM_AND_START_CALL = "KEY_SWITCH_TO_ROOM_AND_START_CALL" const val KEY_IS_BREAKOUT_ROOM = "KEY_IS_BREAKOUT_ROOM" + const val KEY_NOTIFICATION_RECORDING_NOTIFICATION = "KEY_NOTIFICATION_RECORDING_NOTIFICATION" } From ebcab60df51929f20f413a50bb71d10ba7a05cec Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Feb 2023 16:27:05 +0100 Subject: [PATCH 02/10] WIP. Refactoring NotificationWorker Signed-off-by: Marcel Hibbe --- .../java/com/nextcloud/talk/api/NcApi.java | 4 +- .../talk/data/NotificationConfirmDialog.kt | 16 ++ .../nextcloud/talk/jobs/NotificationWorker.kt | 207 +++++++++--------- .../com/nextcloud/talk/utils/ApiUtils.java | 2 +- 4 files changed, 123 insertions(+), 106 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 4f96c7f6c..7f91c8174 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -382,8 +382,8 @@ public interface NcApi { @Url String url); @GET - Observable getNotification(@Header("Authorization") String authorization, - @Url String url); + Observable getNcNotification(@Header("Authorization") String authorization, + @Url String url); @FormUrlEncoded @POST diff --git a/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt b/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt new file mode 100644 index 000000000..2295b6b46 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt @@ -0,0 +1,16 @@ +package com.nextcloud.talk.data + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class NotificationConfirmDialog( + var title: String? = "", + var question: String? = "", + var primaryActionDescription: String? = "", + var primaryActionUrl: String? = "", + var primaryActionMethod: String? = "", + var secondaryActionDescription: String? = "", + var secondaryActionUrl: String? = "", + var secondaryActionMethod: String? = "" +) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 6a5a0e0b6..130aa0a3c 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -165,10 +165,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } else if (isSpreedNotification()) { Log.d(TAG, "pushMessage.type: " + pushMessage.type) when (pushMessage.type) { - TYPE_CHAT -> handleChatNotification() - TYPE_ROOM -> handleRoomNotification() - TYPE_CALL -> handleCallNotification() - TYPE_RECORDING -> handleRecordingNotification() + TYPE_CHAT, TYPE_ROOM, TYPE_RECORDING -> handleNonCallPushMessage() + TYPE_CALL -> handleCallPushMessage() else -> Log.e(TAG, "unknown pushMessage.type") } } else { @@ -178,25 +176,16 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor return Result.success() } - private fun handleChatNotification() { - val chatIntent = Intent(context, MainActivity::class.java) - chatIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - val chatBundle = Bundle() - chatBundle.putString(KEY_ROOM_TOKEN, pushMessage.id) - chatBundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) - chatBundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) - chatIntent.putExtras(chatBundle) + private fun handleNonCallPushMessage() { + val mainActivityIntent = createMainActivityIntent() if (pushMessage.notificationId != Long.MIN_VALUE) { - showNotificationWithObjectData(chatIntent) + getNcDataAndShowNotification(mainActivityIntent) } else { - showNotification(chatIntent) + showNotification(mainActivityIntent) } } - /** - * handle messages with type 'room', e.g. "xxx invited you to a group conversation" - */ - private fun handleRoomNotification() { + private fun createMainActivityIntent(): Intent { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK val bundle = Bundle() @@ -204,12 +193,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) intent.putExtras(bundle) - if (bundle.containsKey(KEY_ROOM_TOKEN)) { - showNotificationWithObjectData(intent) - } + return intent } - private fun handleCallNotification() { + private fun handleCallPushMessage() { val fullScreenIntent = Intent(context, CallNotificationActivity::class.java) val bundle = Bundle() bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) @@ -261,18 +248,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor checkIfCallIsActive(signatureVerification) } - private fun handleRecordingNotification() { - val chatWithRecordingIntent = Intent(context, MainActivity::class.java) - chatWithRecordingIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - val chatBundle = Bundle() - chatBundle.putString(KEY_ROOM_TOKEN, pushMessage.id) - chatBundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) - chatWithRecordingIntent.putExtras(chatBundle) - if (pushMessage.notificationId != Long.MIN_VALUE) { - showNotificationWithObjectData(chatWithRecordingIntent) - } - } - private fun initNcApiAndCredentials() { credentials = ApiUtils.getCredentials( signatureVerification.user!!.username, @@ -327,13 +302,13 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor private fun isSpreedNotification() = SPREED_APP == pushMessage.app - private fun showNotificationWithObjectData(intent: Intent) { + private fun getNcDataAndShowNotification(intent: Intent) { val user = signatureVerification.user // see https://github.com/nextcloud/notifications/blob/master/docs/ocs-endpoint-v2.md - ncApi.getNotification( + ncApi.getNcNotification( credentials, - ApiUtils.getUrlForNotificationWithId( + ApiUtils.getUrlForNcNotificationWithId( user!!.baseUrl, (pushMessage.notificationId!!).toString() ) @@ -346,59 +321,9 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor override fun onNext(notificationOverall: NotificationOverall) { val ncNotification = notificationOverall.ocs!!.notification - if ("recording" == ncNotification?.objectType) { - val bundle = Bundle() - bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, ncNotification) - intent.putExtras(bundle) - } - - if (ncNotification!!.messageRichParameters != null && - ncNotification.messageRichParameters!!.size > 0 - ) { - pushMessage.text = getParsedMessage( - ncNotification.messageRich, - ncNotification.messageRichParameters - ) - } else { - pushMessage.text = ncNotification.message - } - - val subjectRichParameters = ncNotification.subjectRichParameters - - pushMessage.timestamp = ncNotification.datetime!!.millis - - if (subjectRichParameters != null && subjectRichParameters.size > 0) { - val callHashMap = subjectRichParameters["call"] - val userHashMap = subjectRichParameters["user"] - val guestHashMap = subjectRichParameters["guest"] - if (callHashMap != null && callHashMap.size > 0 && callHashMap.containsKey("name")) { - if (subjectRichParameters.containsKey("reaction")) { - pushMessage.subject = "" - pushMessage.text = ncNotification.subject - } else if (ncNotification.objectType == "chat") { - pushMessage.subject = callHashMap["name"]!! - } else { - pushMessage.subject = ncNotification.subject!! - } - if (callHashMap.containsKey("call-type")) { - conversationType = callHashMap["call-type"] - } - } - val notificationUser = NotificationUser() - if (userHashMap != null && userHashMap.isNotEmpty()) { - notificationUser.id = userHashMap["id"] - notificationUser.type = userHashMap["type"] - notificationUser.name = userHashMap["name"] - pushMessage.notificationUser = notificationUser - } else if (guestHashMap != null && guestHashMap.isNotEmpty()) { - notificationUser.id = guestHashMap["id"] - notificationUser.type = guestHashMap["type"] - notificationUser.name = guestHashMap["name"] - pushMessage.notificationUser = notificationUser - } - } - pushMessage.objectId = ncNotification.objectId - showNotification(intent) + enrichPushMessageByNcNotificationData(ncNotification) + val newIntent = enrichIntentByNcNotificationData(intent, ncNotification) + showNotification(newIntent) } override fun onError(e: Throwable) { @@ -411,6 +336,76 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor }) } + private fun enrichIntentByNcNotificationData( + intent: Intent, + ncNotification: com.nextcloud.talk.models.json.notifications.Notification? + ): Intent { + val newIntent = Intent(intent) + + if ("recording" == ncNotification?.objectType) { + val bundle = Bundle() + bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, ncNotification) + newIntent.putExtras(bundle) + } + + return newIntent + } + + private fun enrichPushMessageByNcNotificationData( + ncNotification: com.nextcloud.talk.models.json.notifications.Notification? + ) { + if (ncNotification!!.messageRichParameters != null && + ncNotification.messageRichParameters!!.size > 0 + ) { + pushMessage.text = getParsedMessage( + ncNotification.messageRich, + ncNotification.messageRichParameters + ) + } else { + pushMessage.text = ncNotification.message + } + + val subjectRichParameters = ncNotification.subjectRichParameters + + pushMessage.timestamp = ncNotification.datetime!!.millis + + if (subjectRichParameters != null && subjectRichParameters.size > 0) { + val callHashMap = subjectRichParameters["call"] + val userHashMap = subjectRichParameters["user"] + val guestHashMap = subjectRichParameters["guest"] + if (callHashMap != null && callHashMap.size > 0 && callHashMap.containsKey("name")) { + if (subjectRichParameters.containsKey("reaction")) { + pushMessage.subject = "" + pushMessage.text = ncNotification.subject + } else if (ncNotification.objectType == "chat") { + pushMessage.subject = callHashMap["name"]!! + } else { + pushMessage.subject = ncNotification.subject!! + } + + if (callHashMap.containsKey("call-type")) { + conversationType = callHashMap["call-type"] + } + if ("recording" == ncNotification.objectType) { + conversationType = "recording" + } + } + val notificationUser = NotificationUser() + if (userHashMap != null && userHashMap.isNotEmpty()) { + notificationUser.id = userHashMap["id"] + notificationUser.type = userHashMap["type"] + notificationUser.name = userHashMap["name"] + pushMessage.notificationUser = notificationUser + } else if (guestHashMap != null && guestHashMap.isNotEmpty()) { + notificationUser.id = guestHashMap["id"] + notificationUser.type = guestHashMap["type"] + notificationUser.name = guestHashMap["name"] + pushMessage.notificationUser = notificationUser + } + } + pushMessage.objectId = ncNotification.objectId + } + @Suppress("MagicNumber") private fun showNotification(intent: Intent) { val largeIcon: Bitmap @@ -425,6 +420,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } when (conversationType) { + "recording" -> { + pushMessage.subject = "new Recording available" + largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_videocam_24)?.toBitmap()!! + } "one2one" -> { pushMessage.subject = "" largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! @@ -451,6 +450,17 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, intentFlag) val uri = Uri.parse(signatureVerification.user!!.baseUrl) val baseUrl = uri.host + + var contentTitle: CharSequence? = "" + if (!TextUtils.isEmpty(pushMessage.subject)) { + contentTitle = EmojiCompat.get().process(pushMessage.subject) + } + + var contentText: CharSequence? = "" + if (!TextUtils.isEmpty(pushMessage.text)) { + contentText = EmojiCompat.get().process(pushMessage.text!!) + } + val notificationBuilder = NotificationCompat.Builder(context!!, "1") .setLargeIcon(largeIcon) .setSmallIcon(smallIcon) @@ -461,18 +471,9 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setShowWhen(true) .setContentIntent(pendingIntent) .setAutoCancel(true) - if (!TextUtils.isEmpty(pushMessage.subject)) { - notificationBuilder.setContentTitle( - EmojiCompat.get().process(pushMessage.subject) - ) - } - if (!TextUtils.isEmpty(pushMessage.text)) { - notificationBuilder.setContentText( - EmojiCompat.get().process(pushMessage.text!!) - ) - } - - notificationBuilder.color = context!!.resources.getColor(R.color.colorPrimary) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setColor(context!!.resources.getColor(R.color.colorPrimary)) val notificationInfoBundle = Bundle() notificationInfoBundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) @@ -519,7 +520,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && TYPE_RECORDING == pushMessage.type && - pushMessage.notificationUser != null + pushMessage.notificationUser != null // null ) { prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index 6c2dd557b..e649b1a3b 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -392,7 +392,7 @@ public class ApiUtils { } // see https://github.com/nextcloud/notifications/blob/master/docs/ocs-endpoint-v2.md - public static String getUrlForNotificationWithId(String baseUrl, String notificationId) { + public static String getUrlForNcNotificationWithId(String baseUrl, String notificationId) { return baseUrl + ocsApiVersion + "/apps/notifications/api/v2/notifications/" + notificationId; } From 004f71949037923033ec4f08e6626b18f480870e Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Thu, 23 Feb 2023 18:10:36 +0100 Subject: [PATCH 03/10] Use model to pass data to dialog use util to extract common rx requests Signed-off-by: Marcel Hibbe --- .../talk/controllers/ChatController.kt | 71 ++++--------------- .../talk/data/NotificationConfirmDialog.kt | 16 ----- .../talk/data/NotificationDialogData.kt | 16 +++++ .../nextcloud/talk/jobs/NotificationWorker.kt | 41 ++++++++--- .../talk/utils/rx/SendCommonRequestUtil.kt | 60 ++++++++++++++++ 5 files changed, 120 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt create mode 100644 app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt create mode 100644 app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index 914f0b8db..e09bfbf45 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -126,6 +126,7 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.callbacks.MentionAutocompleteCallback import com.nextcloud.talk.controllers.base.BaseController import com.nextcloud.talk.controllers.util.viewBinding +import com.nextcloud.talk.data.NotificationDialogData import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ControllerChatBinding import com.nextcloud.talk.events.UserMentionClickEvent @@ -146,7 +147,6 @@ import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.conversations.RoomsOverall import com.nextcloud.talk.models.json.generic.GenericOverall import com.nextcloud.talk.models.json.mention.Mention -import com.nextcloud.talk.models.json.notifications.Notification import com.nextcloud.talk.polls.ui.PollCreateDialogFragment import com.nextcloud.talk.presenters.MentionAutocompletePresenter import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity @@ -185,6 +185,7 @@ import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.remapchat.ConductorRemapping import com.nextcloud.talk.utils.remapchat.RemapChatModel import com.nextcloud.talk.utils.rx.DisposableSet +import com.nextcloud.talk.utils.rx.SendCommonRequestUtil import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder import com.nextcloud.talk.utils.text.Spans import com.nextcloud.talk.webrtc.WebSocketConnectionHelper @@ -268,7 +269,7 @@ class ChatController(args: Bundle) : private var lookingIntoFuture = false var newMessagesCount = 0 var startCallFromNotification: Boolean? = null - private var recordingAvailableNotification: Notification? = null + private var recordingAvailableNotification: NotificationDialogData? = null var startCallFromRoomSwitch: Boolean = false val roomId: String val voiceOnly: Boolean @@ -936,20 +937,17 @@ class ChatController(args: Bundle) : if (recordingAvailableNotification != null) { binding?.root?.context?.let { context -> val dialogBuilder = MaterialAlertDialogBuilder(context) - dialogBuilder.setTitle(recordingAvailableNotification?.subject) - dialogBuilder.setMessage("Do you want to share the recording to the chat?") + recordingAvailableNotification?.let { + dialogBuilder.setTitle(it.title) + dialogBuilder.setMessage(it.text) - for (action in recordingAvailableNotification?.actions!!) { - if (!action.label.isNullOrEmpty() && !action.link.isNullOrEmpty()) { - if (action.primary) { - dialogBuilder.setPositiveButton(action.label!!) { dialog, which -> - sendRequest(action.type!!, action.link!!) - } - } else { - dialogBuilder.setNegativeButton(action.label!!) { dialog, which -> - sendRequest(action.type!!, action.link!!) - } - } + val requestUtil = SendCommonRequestUtil(ncApi, credentials!!) + + dialogBuilder.setPositiveButton(it.primaryActionDescription) { _, _ -> + requestUtil.sendRequest(it.primaryActionMethod, it.primaryActionUrl) + } + dialogBuilder.setNegativeButton(it.secondaryActionDescription) { _, _ -> + requestUtil.sendRequest(it.secondaryActionMethod, it.secondaryActionUrl) } } @@ -965,49 +963,6 @@ class ChatController(args: Bundle) : super.onViewBound(view) } - private fun sendRequest(type: String, link: String) { - if (type == "POST") { - ncApi.sendCommonPostRequest(credentials, link) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - } - - override fun onError(e: Throwable) { - } - - override fun onComplete() { - // unused atm - } - }) - } else if (type == "DELETE") { - ncApi.sendCommonDeleteRequest(credentials, link) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - } - - override fun onError(e: Throwable) { - } - - override fun onComplete() { - // unused atm - } - }) - } - Log.d(TAG, "type=" + type + " link=" + link) - } - private fun switchToRoom(token: String) { if (CallActivity.active) { Log.d(TAG, "CallActivity is running. Ignore to switch chat in ChatController...") diff --git a/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt b/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt deleted file mode 100644 index 2295b6b46..000000000 --- a/app/src/main/java/com/nextcloud/talk/data/NotificationConfirmDialog.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.nextcloud.talk.data - -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize -data class NotificationConfirmDialog( - var title: String? = "", - var question: String? = "", - var primaryActionDescription: String? = "", - var primaryActionUrl: String? = "", - var primaryActionMethod: String? = "", - var secondaryActionDescription: String? = "", - var secondaryActionUrl: String? = "", - var secondaryActionMethod: String? = "" -) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt b/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt new file mode 100644 index 000000000..3d2e8db9a --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt @@ -0,0 +1,16 @@ +package com.nextcloud.talk.data + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class NotificationDialogData( + var title: String = "", + var text: String = "", + var primaryActionDescription: String = "", + var primaryActionUrl: String = "", + var primaryActionMethod: String = "", + var secondaryActionDescription: String = "", + var secondaryActionUrl: String = "", + var secondaryActionMethod: String = "" +) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 130aa0a3c..fc5f1b77a 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -58,6 +58,7 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager +import com.nextcloud.talk.data.NotificationDialogData import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage import com.nextcloud.talk.models.json.conversations.RoomOverall @@ -320,14 +321,15 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor override fun onNext(notificationOverall: NotificationOverall) { val ncNotification = notificationOverall.ocs!!.notification - - enrichPushMessageByNcNotificationData(ncNotification) - val newIntent = enrichIntentByNcNotificationData(intent, ncNotification) - showNotification(newIntent) + if (ncNotification != null) { + enrichPushMessageByNcNotificationData(ncNotification) + val newIntent = enrichIntentByNcNotificationData(intent, ncNotification) + showNotification(newIntent) + } } override fun onError(e: Throwable) { - // unused atm + Log.e(TAG, "Failed to get notification", e) } override fun onComplete() { @@ -338,13 +340,30 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor private fun enrichIntentByNcNotificationData( intent: Intent, - ncNotification: com.nextcloud.talk.models.json.notifications.Notification? + ncNotification: com.nextcloud.talk.models.json.notifications.Notification ): Intent { val newIntent = Intent(intent) - if ("recording" == ncNotification?.objectType) { + if ("recording" == ncNotification.objectType) { + val notificationDialogData = NotificationDialogData() + + notificationDialogData.title = context?.getString(R.string.record_file_available).orEmpty() + notificationDialogData.text = ncNotification.subject.orEmpty() + + for (action in ncNotification.actions!!) { + if (action.primary) { + notificationDialogData.primaryActionDescription = action.label.orEmpty() + notificationDialogData.primaryActionMethod = action.type.orEmpty() + notificationDialogData.primaryActionUrl = action.link.orEmpty() + } else { + notificationDialogData.secondaryActionDescription = action.label.orEmpty() + notificationDialogData.secondaryActionMethod = action.type.orEmpty() + notificationDialogData.secondaryActionUrl = action.link.orEmpty() + } + } + val bundle = Bundle() - bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, ncNotification) + bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, notificationDialogData) newIntent.putExtras(bundle) } @@ -376,13 +395,16 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (callHashMap != null && callHashMap.size > 0 && callHashMap.containsKey("name")) { if (subjectRichParameters.containsKey("reaction")) { pushMessage.subject = "" - pushMessage.text = ncNotification.subject } else if (ncNotification.objectType == "chat") { pushMessage.subject = callHashMap["name"]!! } else { pushMessage.subject = ncNotification.subject!! } + if (subjectRichParameters.containsKey("reaction")) { + pushMessage.text = ncNotification.subject + } + if (callHashMap.containsKey("call-type")) { conversationType = callHashMap["call-type"] } @@ -421,7 +443,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor when (conversationType) { "recording" -> { - pushMessage.subject = "new Recording available" largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_videocam_24)?.toBitmap()!! } "one2one" -> { diff --git a/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt new file mode 100644 index 000000000..915b4a60d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt @@ -0,0 +1,60 @@ +package com.nextcloud.talk.utils.rx + +import android.util.Log +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.models.json.generic.GenericOverall +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers + +class SendCommonRequestUtil(val ncApi: NcApi, val credentials: String) { + + fun sendRequest(type: String, link: String) { + if (type == "POST") { + ncApi.sendCommonPostRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Request failed", e) + } + + override fun onComplete() { + // unused atm + } + }) + } else if (type == "DELETE") { + ncApi.sendCommonDeleteRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Request failed", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + } + + companion object { + private val TAG = SendCommonRequestUtil::class.java.simpleName + } +} From 8b1a8094643aae5898feac0dc696550cc36ddc56 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 10:05:17 +0100 Subject: [PATCH 04/10] Handle new subject for call recording notification see https://github.com/nextcloud/spreed/pull/8837 Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/jobs/NotificationWorker.kt | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index fc5f1b77a..80da76747 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -347,8 +347,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if ("recording" == ncNotification.objectType) { val notificationDialogData = NotificationDialogData() - notificationDialogData.title = context?.getString(R.string.record_file_available).orEmpty() - notificationDialogData.text = ncNotification.subject.orEmpty() + notificationDialogData.title = pushMessage.subject + notificationDialogData.text = pushMessage.text.orEmpty() for (action in ncNotification.actions!!) { if (action.primary) { @@ -371,9 +371,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } private fun enrichPushMessageByNcNotificationData( - ncNotification: com.nextcloud.talk.models.json.notifications.Notification? + ncNotification: com.nextcloud.talk.models.json.notifications.Notification ) { - if (ncNotification!!.messageRichParameters != null && + pushMessage.objectId = ncNotification.objectId + pushMessage.timestamp = ncNotification.datetime!!.millis + + if (ncNotification.messageRichParameters != null && ncNotification.messageRichParameters!!.size > 0 ) { pushMessage.text = getParsedMessage( @@ -385,9 +388,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } val subjectRichParameters = ncNotification.subjectRichParameters - - pushMessage.timestamp = ncNotification.datetime!!.millis - if (subjectRichParameters != null && subjectRichParameters.size > 0) { val callHashMap = subjectRichParameters["call"] val userHashMap = subjectRichParameters["user"] @@ -408,9 +408,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (callHashMap.containsKey("call-type")) { conversationType = callHashMap["call-type"] } - if ("recording" == ncNotification.objectType) { - conversationType = "recording" - } } val notificationUser = NotificationUser() if (userHashMap != null && userHashMap.isNotEmpty()) { @@ -424,8 +421,13 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationUser.name = guestHashMap["name"] pushMessage.notificationUser = notificationUser } + } else { + pushMessage.subject = ncNotification.subject.orEmpty() + } + + if ("recording" == ncNotification.objectType) { + conversationType = "recording" } - pushMessage.objectId = ncNotification.objectId } @Suppress("MagicNumber") @@ -487,13 +489,13 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setSmallIcon(smallIcon) .setCategory(category) .setPriority(priority) + .setContentTitle(contentTitle) + .setContentText(contentText) .setSubText(baseUrl) .setWhen(pushMessage.timestamp) .setShowWhen(true) .setContentIntent(pendingIntent) .setAutoCancel(true) - .setContentTitle(contentTitle) - .setContentText(contentText) .setColor(context!!.resources.getColor(R.color.colorPrimary)) val notificationInfoBundle = Bundle() From 328a747d79c15209f3e6eae70fd8e5fa1294d2f6 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 10:45:13 +0100 Subject: [PATCH 05/10] move createMainActivityIntent near getIntentToOpenConversation TODO: unify? Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/jobs/NotificationWorker.kt | 28 ++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 80da76747..3c595aac2 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -186,17 +186,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } } - private fun createMainActivityIntent(): Intent { - val intent = Intent(context, MainActivity::class.java) - intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - val bundle = Bundle() - bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) - bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) - bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) - intent.putExtras(bundle) - return intent - } - private fun handleCallPushMessage() { val fullScreenIntent = Intent(context, CallNotificationActivity::class.java) val bundle = Bundle() @@ -871,16 +860,23 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor }) } } - - private fun getIntentToOpenConversation(): PendingIntent? { - val bundle = Bundle() + private fun createMainActivityIntent(): Intent { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) + bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) + bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) + intent.putExtras(bundle) + return intent + } + private fun getIntentToOpenConversation(): PendingIntent? { + val intent = Intent(context, MainActivity::class.java) + intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + val bundle = Bundle() bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) bundle.putParcelable(KEY_USER_ENTITY, signatureVerification.user) bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) - intent.putExtras(bundle) val requestCode = System.currentTimeMillis().toInt() From 0f3662cd82c5b47451c9357bec92671887a5264f Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 12:32:27 +0100 Subject: [PATCH 06/10] restrict deletion of "Recording available" notification ...for preparation to replace Dialog that was openend in ChatController. It will be replaced by Notification Actions. The dialog was not opened when already being in a chat, because remapChatController only moved the currentChatController to top so to won't be initialized again. That's why the dialog didn't pop up for this case. As a solution, the actions should be available directly inside the notification. For this, it must be avoided that the "recording available" notification is closed whenever the chat is opened. Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/jobs/NotificationWorker.kt | 125 +++++++++++++----- .../nextcloud/talk/utils/NotificationUtils.kt | 4 +- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + 3 files changed, 98 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 3c595aac2..4be297f76 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -85,6 +85,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MESSAGE_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RESTRICT_DELETION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID @@ -413,44 +414,17 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } else { pushMessage.subject = ncNotification.subject.orEmpty() } - - if ("recording" == ncNotification.objectType) { - conversationType = "recording" - } } @Suppress("MagicNumber") private fun showNotification(intent: Intent) { - val largeIcon: Bitmap - val priority = NotificationCompat.PRIORITY_HIGH - val smallIcon: Int = R.drawable.ic_logo - - var category: String = "" + var category = "" when (pushMessage.type) { TYPE_CHAT, TYPE_ROOM, TYPE_RECORDING -> category = Notification.CATEGORY_MESSAGE TYPE_CALL -> category = Notification.CATEGORY_CALL else -> Log.e(TAG, "unknown pushMessage.type") } - when (conversationType) { - "recording" -> { - largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_videocam_24)?.toBitmap()!! - } - "one2one" -> { - pushMessage.subject = "" - largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! - } - "group" -> - largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! - "public" -> largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_link_black_24px)?.toBitmap()!! - else -> // assuming one2one - largeIcon = if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { - ContextCompat.getDrawable(context!!, R.drawable.ic_comment)?.toBitmap()!! - } else { - ContextCompat.getDrawable(context!!, R.drawable.ic_call_black_24dp)?.toBitmap()!! - } - } - // Use unique request code to make sure that a new PendingIntent gets created for each notification // See https://github.com/nextcloud/talk-android/issues/2111 val requestCode = System.currentTimeMillis().toInt() @@ -474,10 +448,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } val notificationBuilder = NotificationCompat.Builder(context!!, "1") - .setLargeIcon(largeIcon) - .setSmallIcon(smallIcon) + .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(category) - .setPriority(priority) + .setLargeIcon(getLargeIcon()) + .setSmallIcon(R.drawable.ic_logo) .setContentTitle(contentTitle) .setContentText(contentText) .setSubText(baseUrl) @@ -492,6 +466,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor // could be an ID or a TOKEN notificationInfoBundle.putString(KEY_ROOM_TOKEN, pushMessage.id) notificationInfoBundle.putLong(KEY_NOTIFICATION_ID, pushMessage.notificationId!!) + + if (pushMessage.type == TYPE_RECORDING) { + notificationInfoBundle.putBoolean(KEY_NOTIFICATION_RESTRICT_DELETION, true) + } + notificationBuilder.setExtras(notificationInfoBundle) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -530,6 +509,15 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor addMarkAsReadAction(notificationBuilder, systemNotificationId) } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && + TYPE_RECORDING == pushMessage.type && + pushMessage.notificationUser != null + ) { + prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) + // addDiscardRecordingAvailableAction(notificationBuilder, systemNotificationId) + // addShareRecordingToChatAction(notificationBuilder, systemNotificationId) + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && TYPE_RECORDING == pushMessage.type && pushMessage.notificationUser != null // null @@ -539,6 +527,34 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor sendNotification(systemNotificationId, notificationBuilder.build()) } + private fun getLargeIcon(): Bitmap { + val largeIcon: Bitmap + if (pushMessage.type == "recording") { + largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_videocam_24)?.toBitmap()!! + } else { + when (conversationType) { + "one2one" -> { + pushMessage.subject = "" + largeIcon = + ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! + } + "group" -> + largeIcon = + ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! + "public" -> + largeIcon = + ContextCompat.getDrawable(context!!, R.drawable.ic_link_black_24px)?.toBitmap()!! + else -> // assuming one2one + largeIcon = if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { + ContextCompat.getDrawable(context!!, R.drawable.ic_comment)?.toBitmap()!! + } else { + ContextCompat.getDrawable(context!!, R.drawable.ic_call_black_24dp)?.toBitmap()!! + } + } + } + return largeIcon + } + private fun calculateCRC32(s: String): Long { val crc32 = CRC32() crc32.update(s.toByteArray()) @@ -646,6 +662,51 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationBuilder.addAction(replyAction) } + // @RequiresApi(api = Build.VERSION_CODES.N) + // private fun addDiscardRecordingAvailableAction(notificationBuilder: NotificationCompat.Builder, + // systemNotificationId: + // Int) { + // val replyLabel = context!!.resources.getString(R.string.nc_reply) + // val remoteInput = RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) + // .setLabel(replyLabel) + // .build() + // + // val replyPendingIntent = buildIntentForAction( + // DirectReplyReceiver::class.java, + // systemNotificationId, + // 0 + // ) + // val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) + // .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) + // .setShowsUserInterface(false) + // .setAllowGeneratedReplies(true) + // .addRemoteInput(remoteInput) + // .build() + // notificationBuilder.addAction(replyAction) + // } + // + // @RequiresApi(api = Build.VERSION_CODES.N) + // private fun addShareRecordingToChatAction(notificationBuilder: NotificationCompat.Builder, systemNotificationId: + // Int) { + // val replyLabel = context!!.resources.getString(R.string.nc_reply) + // val remoteInput = RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) + // .setLabel(replyLabel) + // .build() + // + // val replyPendingIntent = buildIntentForAction( + // DirectReplyReceiver::class.java, + // systemNotificationId, + // 0 + // ) + // val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) + // .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) + // .setShowsUserInterface(false) + // .setAllowGeneratedReplies(true) + // .addRemoteInput(remoteInput) + // .build() + // notificationBuilder.addAction(replyAction) + // } + @RequiresApi(api = Build.VERSION_CODES.N) private fun getStyle(person: Person, style: NotificationCompat.MessagingStyle?): NotificationCompat.MessagingStyle { val newStyle = NotificationCompat.MessagingStyle(person) @@ -860,6 +921,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor }) } } + private fun createMainActivityIntent(): Intent { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK @@ -870,6 +932,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor intent.putExtras(bundle) return intent } + private fun getIntentToOpenConversation(): PendingIntent? { val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK 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 c2745cd51..a5682d959 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt @@ -267,7 +267,9 @@ object NotificationUtils { fun cancelExistingNotificationsForRoom(context: Context?, conversationUser: User, roomTokenOrId: String) { scanNotifications(context, conversationUser) { notificationManager, statusBarNotification, notification -> - if (roomTokenOrId == notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN)) { + if (roomTokenOrId == notification.extras.getString(BundleKeys.KEY_ROOM_TOKEN) && + !notification.extras.getBoolean(BundleKeys.KEY_NOTIFICATION_RESTRICT_DELETION) + ) { 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 6083f3001..63c740bc8 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 @@ -83,4 +83,5 @@ object BundleKeys { const val KEY_SWITCH_TO_ROOM_AND_START_CALL = "KEY_SWITCH_TO_ROOM_AND_START_CALL" const val KEY_IS_BREAKOUT_ROOM = "KEY_IS_BREAKOUT_ROOM" const val KEY_NOTIFICATION_RECORDING_NOTIFICATION = "KEY_NOTIFICATION_RECORDING_NOTIFICATION" + const val KEY_NOTIFICATION_RESTRICT_DELETION = "KEY_NOTIFICATION_RESTRICT_DELETION" } From 36de155c4455216883d98273ac84cdefcfba3def Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 15:09:17 +0100 Subject: [PATCH 07/10] Add dismiss/share actions for "recording available" notification Remove first approach with the dialog inside the ChatController. Signed-off-by: Marcel Hibbe --- app/src/main/AndroidManifest.xml | 2 + .../nextcloud/talk/activities/MainActivity.kt | 11 -- .../talk/controllers/ChatController.kt | 34 ---- .../talk/data/NotificationDialogData.kt | 16 -- .../nextcloud/talk/jobs/NotificationWorker.kt | 182 +++++++++--------- .../DismissRecordingAvailableReceiver.kt | 109 +++++++++++ .../receivers/ShareRecordingToChatReceiver.kt | 109 +++++++++++ .../nextcloud/talk/utils/bundle/BundleKeys.kt | 3 +- .../talk/utils/rx/SendCommonRequestUtil.kt | 60 ------ 9 files changed, 317 insertions(+), 209 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt create mode 100644 app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt create mode 100644 app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt delete mode 100644 app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dc3cbc5e8..279b00951 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -198,6 +198,8 @@ + + (KEY_USER_ENTITY)!!.id!!, - intent.getStringExtra(KEY_ROOM_TOKEN)!!, - intent.extras!!, - true, - true - ) - logRouterBackStack(router!!) } } diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt index e09bfbf45..01badf1ab 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt +++ b/app/src/main/java/com/nextcloud/talk/controllers/ChatController.kt @@ -126,7 +126,6 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.callbacks.MentionAutocompleteCallback import com.nextcloud.talk.controllers.base.BaseController import com.nextcloud.talk.controllers.util.viewBinding -import com.nextcloud.talk.data.NotificationDialogData import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.ControllerChatBinding import com.nextcloud.talk.events.UserMentionClickEvent @@ -185,7 +184,6 @@ import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.remapchat.ConductorRemapping import com.nextcloud.talk.utils.remapchat.RemapChatModel import com.nextcloud.talk.utils.rx.DisposableSet -import com.nextcloud.talk.utils.rx.SendCommonRequestUtil import com.nextcloud.talk.utils.singletons.ApplicationWideCurrentRoomHolder import com.nextcloud.talk.utils.text.Spans import com.nextcloud.talk.webrtc.WebSocketConnectionHelper @@ -269,7 +267,6 @@ class ChatController(args: Bundle) : private var lookingIntoFuture = false var newMessagesCount = 0 var startCallFromNotification: Boolean? = null - private var recordingAvailableNotification: NotificationDialogData? = null var startCallFromRoomSwitch: Boolean = false val roomId: String val voiceOnly: Boolean @@ -346,10 +343,6 @@ class ChatController(args: Bundle) : startCallFromNotification = args.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL) } - if (args.containsKey(BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION)) { - recordingAvailableNotification = args.getParcelable(BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION) - } - if (args.containsKey(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL)) { startCallFromRoomSwitch = args.getBoolean(BundleKeys.KEY_SWITCH_TO_ROOM_AND_START_CALL) } @@ -933,33 +926,6 @@ class ChatController(args: Bundle) : getRoomInfo() } } - - if (recordingAvailableNotification != null) { - binding?.root?.context?.let { context -> - val dialogBuilder = MaterialAlertDialogBuilder(context) - recordingAvailableNotification?.let { - dialogBuilder.setTitle(it.title) - dialogBuilder.setMessage(it.text) - - val requestUtil = SendCommonRequestUtil(ncApi, credentials!!) - - dialogBuilder.setPositiveButton(it.primaryActionDescription) { _, _ -> - requestUtil.sendRequest(it.primaryActionMethod, it.primaryActionUrl) - } - dialogBuilder.setNegativeButton(it.secondaryActionDescription) { _, _ -> - requestUtil.sendRequest(it.secondaryActionMethod, it.secondaryActionUrl) - } - } - - viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) - val dialog = dialogBuilder.show() - - viewThemeUtils.platform.colorTextButtons( - dialog.getButton(AlertDialog.BUTTON_POSITIVE), - dialog.getButton(AlertDialog.BUTTON_NEGATIVE) - ) - } - } super.onViewBound(view) } diff --git a/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt b/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt deleted file mode 100644 index 3d2e8db9a..000000000 --- a/app/src/main/java/com/nextcloud/talk/data/NotificationDialogData.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.nextcloud.talk.data - -import android.os.Parcelable -import kotlinx.parcelize.Parcelize - -@Parcelize -data class NotificationDialogData( - var title: String = "", - var text: String = "", - var primaryActionDescription: String = "", - var primaryActionUrl: String = "", - var primaryActionMethod: String = "", - var secondaryActionDescription: String = "", - var secondaryActionUrl: String = "", - var secondaryActionMethod: String = "" -) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 4be297f76..731d47be5 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -4,7 +4,7 @@ * @author Andy Scherzinger * @author Mario Danic * @author Marcel Hibbe - * Copyright (C) 2022 Marcel Hibbe + * Copyright (C) 2022-2023 Marcel Hibbe * Copyright (C) 2022 Andy Scherzinger * Copyright (C) 2017-2018 Mario Danic * @@ -58,7 +58,6 @@ import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager -import com.nextcloud.talk.data.NotificationDialogData import com.nextcloud.talk.models.SignatureVerification import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage import com.nextcloud.talk.models.json.conversations.RoomOverall @@ -68,7 +67,9 @@ import com.nextcloud.talk.models.json.participants.ParticipantsOverall import com.nextcloud.talk.models.json.push.DecryptedPushMessage import com.nextcloud.talk.models.json.push.NotificationUser import com.nextcloud.talk.receivers.DirectReplyReceiver +import com.nextcloud.talk.receivers.DismissRecordingAvailableReceiver import com.nextcloud.talk.receivers.MarkAsReadReceiver +import com.nextcloud.talk.receivers.ShareRecordingToChatReceiver import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound import com.nextcloud.talk.utils.NotificationUtils @@ -80,14 +81,15 @@ import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.loadAvatarSync import com.nextcloud.talk.utils.PushUtils import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_DISMISS_RECORDING_URL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MESSAGE_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID -import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RECORDING_NOTIFICATION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_RESTRICT_DELETION import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY import com.nextcloud.talk.utils.preferences.AppPreferences @@ -183,7 +185,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (pushMessage.notificationId != Long.MIN_VALUE) { getNcDataAndShowNotification(mainActivityIntent) } else { - showNotification(mainActivityIntent) + showNotification(mainActivityIntent, null) } } @@ -313,8 +315,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val ncNotification = notificationOverall.ocs!!.notification if (ncNotification != null) { enrichPushMessageByNcNotificationData(ncNotification) - val newIntent = enrichIntentByNcNotificationData(intent, ncNotification) - showNotification(newIntent) + // val newIntent = enrichIntentByNcNotificationData(intent, ncNotification) + showNotification(intent, ncNotification) } } @@ -334,29 +336,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor ): Intent { val newIntent = Intent(intent) - if ("recording" == ncNotification.objectType) { - val notificationDialogData = NotificationDialogData() - - notificationDialogData.title = pushMessage.subject - notificationDialogData.text = pushMessage.text.orEmpty() - - for (action in ncNotification.actions!!) { - if (action.primary) { - notificationDialogData.primaryActionDescription = action.label.orEmpty() - notificationDialogData.primaryActionMethod = action.type.orEmpty() - notificationDialogData.primaryActionUrl = action.link.orEmpty() - } else { - notificationDialogData.secondaryActionDescription = action.label.orEmpty() - notificationDialogData.secondaryActionMethod = action.type.orEmpty() - notificationDialogData.secondaryActionUrl = action.link.orEmpty() - } - } - - val bundle = Bundle() - bundle.putParcelable(KEY_NOTIFICATION_RECORDING_NOTIFICATION, notificationDialogData) - newIntent.putExtras(bundle) - } - return newIntent } @@ -417,7 +396,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } @Suppress("MagicNumber") - private fun showNotification(intent: Intent) { + private fun showNotification( + intent: Intent, + ncNotification: com.nextcloud.talk.models.json.notifications.Notification? + ) { var category = "" when (pushMessage.type) { TYPE_CHAT, TYPE_ROOM, TYPE_RECORDING -> category = Notification.CATEGORY_MESSAGE @@ -447,6 +429,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor contentText = EmojiCompat.get().process(pushMessage.text!!) } + val autoCancelOnClick = TYPE_RECORDING != pushMessage.type + val notificationBuilder = NotificationCompat.Builder(context!!, "1") .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(category) @@ -458,7 +442,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .setWhen(pushMessage.timestamp) .setShowWhen(true) .setContentIntent(pendingIntent) - .setAutoCancel(true) + .setAutoCancel(autoCancelOnClick) .setColor(context!!.resources.getColor(R.color.colorPrimary)) val notificationInfoBundle = Bundle() @@ -511,25 +495,17 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && TYPE_RECORDING == pushMessage.type && - pushMessage.notificationUser != null + ncNotification != null ) { - prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) - // addDiscardRecordingAvailableAction(notificationBuilder, systemNotificationId) - // addShareRecordingToChatAction(notificationBuilder, systemNotificationId) - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && - TYPE_RECORDING == pushMessage.type && - pushMessage.notificationUser != null // null - ) { - prepareChatNotification(notificationBuilder, activeStatusBarNotification, systemNotificationId) + addDismissRecordingAvailableAction(notificationBuilder, systemNotificationId, ncNotification) + addShareRecordingToChatAction(notificationBuilder, systemNotificationId, ncNotification) } sendNotification(systemNotificationId, notificationBuilder.build()) } private fun getLargeIcon(): Bitmap { val largeIcon: Bitmap - if (pushMessage.type == "recording") { + if (pushMessage.type == TYPE_RECORDING) { largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_baseline_videocam_24)?.toBitmap()!! } else { when (conversationType) { @@ -662,50 +638,82 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationBuilder.addAction(replyAction) } - // @RequiresApi(api = Build.VERSION_CODES.N) - // private fun addDiscardRecordingAvailableAction(notificationBuilder: NotificationCompat.Builder, - // systemNotificationId: - // Int) { - // val replyLabel = context!!.resources.getString(R.string.nc_reply) - // val remoteInput = RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) - // .setLabel(replyLabel) - // .build() - // - // val replyPendingIntent = buildIntentForAction( - // DirectReplyReceiver::class.java, - // systemNotificationId, - // 0 - // ) - // val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) - // .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) - // .setShowsUserInterface(false) - // .setAllowGeneratedReplies(true) - // .addRemoteInput(remoteInput) - // .build() - // notificationBuilder.addAction(replyAction) - // } - // - // @RequiresApi(api = Build.VERSION_CODES.N) - // private fun addShareRecordingToChatAction(notificationBuilder: NotificationCompat.Builder, systemNotificationId: - // Int) { - // val replyLabel = context!!.resources.getString(R.string.nc_reply) - // val remoteInput = RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) - // .setLabel(replyLabel) - // .build() - // - // val replyPendingIntent = buildIntentForAction( - // DirectReplyReceiver::class.java, - // systemNotificationId, - // 0 - // ) - // val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) - // .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) - // .setShowsUserInterface(false) - // .setAllowGeneratedReplies(true) - // .addRemoteInput(remoteInput) - // .build() - // notificationBuilder.addAction(replyAction) - // } + @RequiresApi(api = Build.VERSION_CODES.N) + private fun addDismissRecordingAvailableAction( + notificationBuilder: NotificationCompat.Builder, + systemNotificationId: Int, + ncNotification: com.nextcloud.talk.models.json.notifications.Notification + ) { + var dismissLabel = "" + var dismissRecordingUrl = "" + + for (action in ncNotification.actions!!) { + if (!action.primary) { + dismissLabel = action.label.orEmpty() + dismissRecordingUrl = action.link.orEmpty() + } + } + + val dismissIntent = Intent(context, DismissRecordingAvailableReceiver::class.java) + dismissIntent.putExtra(KEY_SYSTEM_NOTIFICATION_ID, systemNotificationId) + dismissIntent.putExtra(KEY_DISMISS_RECORDING_URL, dismissRecordingUrl) + + val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + val dismissPendingIntent = PendingIntent.getBroadcast(context, systemNotificationId, dismissIntent, intentFlag) + + val dismissAction = NotificationCompat.Action.Builder(R.drawable.ic_delete, dismissLabel, dismissPendingIntent) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(true) + .build() + notificationBuilder.addAction(dismissAction) + } + + @RequiresApi(api = Build.VERSION_CODES.N) + private fun addShareRecordingToChatAction( + notificationBuilder: NotificationCompat.Builder, + systemNotificationId: Int, + ncNotification: com.nextcloud.talk.models.json.notifications.Notification + ) { + var shareToChatLabel = "" + var shareToChatUrl = "" + + for (action in ncNotification.actions!!) { + if (action.primary) { + shareToChatLabel = action.label.orEmpty() + shareToChatUrl = action.link.orEmpty() + } + } + + val shareRecordingIntent = Intent(context, ShareRecordingToChatReceiver::class.java) + shareRecordingIntent.putExtra(KEY_SYSTEM_NOTIFICATION_ID, systemNotificationId) + shareRecordingIntent.putExtra(KEY_SHARE_RECORDING_TO_CHAT_URL, shareToChatUrl) + + val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + val shareRecordingPendingIntent = PendingIntent.getBroadcast( + context, + systemNotificationId, + shareRecordingIntent, + intentFlag + ) + + val shareRecordingAction = NotificationCompat.Action.Builder( + R.drawable.ic_delete, + shareToChatLabel, + shareRecordingPendingIntent + ) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(true) + .build() + notificationBuilder.addAction(shareRecordingAction) + } @RequiresApi(api = Build.VERSION_CODES.N) private fun getStyle(person: Person, style: NotificationCompat.MessagingStyle?): NotificationCompat.MessagingStyle { diff --git a/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt new file mode 100644 index 000000000..8edea6242 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt @@ -0,0 +1,109 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022-2023 Marcel Hibbe + * + * 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 . + */ + +package com.nextcloud.talk.receivers + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import autodagger.AutoInjector +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class DismissRecordingAvailableReceiver : BroadcastReceiver() { + + @Inject + lateinit var userManager: UserManager + + @Inject + lateinit var ncApi: NcApi + + lateinit var context: Context + lateinit var currentUser: User + private var systemNotificationId: Int? = null + private var link: String? = null + + init { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + } + + override fun onReceive(receiveContext: Context, intent: Intent?) { + context = receiveContext + + // NOTE - systemNotificationId is an internal ID used on the device only. + // It is NOT the same as the notification ID used in communication with the server. + systemNotificationId = intent!!.getIntExtra(KEY_SYSTEM_NOTIFICATION_ID, 0) + link = intent.getStringExtra(BundleKeys.KEY_DISMISS_RECORDING_URL) + + val id = intent.getLongExtra(KEY_INTERNAL_USER_ID, userManager.currentUser.blockingGet().id!!) + currentUser = userManager.getUserWithId(id).blockingGet() + + dismissNcRecordingAvailableNotification() + } + + private fun dismissNcRecordingAvailableNotification() { + val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) + + ncApi.sendCommonDeleteRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + cancelNotification(systemNotificationId!!) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to send dismiss for recording available", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + + private fun cancelNotification(notificationId: Int) { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(notificationId) + } + + companion object { + private val TAG = DismissRecordingAvailableReceiver::class.java.simpleName + } +} diff --git a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt new file mode 100644 index 000000000..b6b977b5d --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt @@ -0,0 +1,109 @@ +/* + * Nextcloud Talk application + * + * @author Marcel Hibbe + * Copyright (C) 2022-2023 Marcel Hibbe + * + * 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 . + */ + +package com.nextcloud.talk.receivers + +import android.app.NotificationManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import autodagger.AutoInjector +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.models.json.generic.GenericOverall +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_INTERNAL_USER_ID +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class ShareRecordingToChatReceiver : BroadcastReceiver() { + + @Inject + lateinit var userManager: UserManager + + @Inject + lateinit var ncApi: NcApi + + lateinit var context: Context + lateinit var currentUser: User + private var systemNotificationId: Int? = null + private var link: String? = null + + init { + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + } + + override fun onReceive(receiveContext: Context, intent: Intent?) { + context = receiveContext + + // NOTE - systemNotificationId is an internal ID used on the device only. + // It is NOT the same as the notification ID used in communication with the server. + systemNotificationId = intent!!.getIntExtra(KEY_SYSTEM_NOTIFICATION_ID, 0) + link = intent.getStringExtra(BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL) + + val id = intent.getLongExtra(KEY_INTERNAL_USER_ID, userManager.currentUser.blockingGet().id!!) + currentUser = userManager.getUserWithId(id).blockingGet() + + shareRecordingToChat() + } + + private fun shareRecordingToChat() { + val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) + + ncApi.sendCommonPostRequest(credentials, link) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + cancelNotification(systemNotificationId!!) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to share recording to chat request", e) + } + + override fun onComplete() { + // unused atm + } + }) + } + + private fun cancelNotification(notificationId: Int) { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(notificationId) + } + + companion object { + private val TAG = ShareRecordingToChatReceiver::class.java.simpleName + } +} 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 63c740bc8..efd7ee0df 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 @@ -82,6 +82,7 @@ object BundleKeys { const val KEY_IS_MODERATOR = "KEY_IS_MODERATOR" const val KEY_SWITCH_TO_ROOM_AND_START_CALL = "KEY_SWITCH_TO_ROOM_AND_START_CALL" const val KEY_IS_BREAKOUT_ROOM = "KEY_IS_BREAKOUT_ROOM" - const val KEY_NOTIFICATION_RECORDING_NOTIFICATION = "KEY_NOTIFICATION_RECORDING_NOTIFICATION" const val KEY_NOTIFICATION_RESTRICT_DELETION = "KEY_NOTIFICATION_RESTRICT_DELETION" + const val KEY_DISMISS_RECORDING_URL = "KEY_DISMISS_RECORDING_URL" + const val KEY_SHARE_RECORDING_TO_CHAT_URL = "KEY_SHARE_RECORDING_TO_CHAT_URL" } diff --git a/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt deleted file mode 100644 index 915b4a60d..000000000 --- a/app/src/main/java/com/nextcloud/talk/utils/rx/SendCommonRequestUtil.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.nextcloud.talk.utils.rx - -import android.util.Log -import com.nextcloud.talk.api.NcApi -import com.nextcloud.talk.models.json.generic.GenericOverall -import io.reactivex.Observer -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class SendCommonRequestUtil(val ncApi: NcApi, val credentials: String) { - - fun sendRequest(type: String, link: String) { - if (type == "POST") { - ncApi.sendCommonPostRequest(credentials, link) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Request failed", e) - } - - override fun onComplete() { - // unused atm - } - }) - } else if (type == "DELETE") { - ncApi.sendCommonDeleteRequest(credentials, link) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Request failed", e) - } - - override fun onComplete() { - // unused atm - } - }) - } - } - - companion object { - private val TAG = SendCommonRequestUtil::class.java.simpleName - } -} From ed96d53049cf192e0ee1dba618a2932fb3f531bc Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 16:11:49 +0100 Subject: [PATCH 08/10] WIP. open chat when share recording to chat (fails atm) Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/jobs/NotificationWorker.kt | 2 ++ .../receivers/ShareRecordingToChatReceiver.kt | 23 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 731d47be5..ff9b18b10 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -691,6 +691,8 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val shareRecordingIntent = Intent(context, ShareRecordingToChatReceiver::class.java) shareRecordingIntent.putExtra(KEY_SYSTEM_NOTIFICATION_ID, systemNotificationId) shareRecordingIntent.putExtra(KEY_SHARE_RECORDING_TO_CHAT_URL, shareToChatUrl) + shareRecordingIntent.putExtra(KEY_ROOM_TOKEN, pushMessage.id) + shareRecordingIntent.putExtra(KEY_USER_ENTITY, signatureVerification.user) val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT diff --git a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt index b6b977b5d..5afe26246 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt @@ -24,8 +24,10 @@ import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.os.Bundle import android.util.Log import autodagger.AutoInjector +import com.nextcloud.talk.activities.MainActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.data.user.model.User @@ -54,6 +56,8 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { lateinit var currentUser: User private var systemNotificationId: Int? = null private var link: String? = null + var roomToken: String? = null + var conversationOfShareTarget: User? = null init { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) @@ -67,6 +71,9 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { systemNotificationId = intent!!.getIntExtra(KEY_SYSTEM_NOTIFICATION_ID, 0) link = intent.getStringExtra(BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL) + roomToken = intent.getStringExtra(BundleKeys.KEY_ROOM_TOKEN) + conversationOfShareTarget = intent.getParcelableExtra(BundleKeys.KEY_USER_ENTITY) + val id = intent.getLongExtra(KEY_INTERNAL_USER_ID, userManager.currentUser.blockingGet().id!!) currentUser = userManager.getUserWithId(id).blockingGet() @@ -86,6 +93,7 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { override fun onNext(genericOverall: GenericOverall) { cancelNotification(systemNotificationId!!) + context.startActivity(createOpenChatIntent()) } override fun onError(e: Throwable) { @@ -98,6 +106,21 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { }) } + private fun createOpenChatIntent(): Intent { + val intent = Intent(context, MainActivity::class.java) + // intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + intent.addFlags( + Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP + ) + + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) + bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationOfShareTarget) + bundle.putBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false) + intent.putExtras(bundle) + return intent + } + private fun cancelNotification(notificationId: Int) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.cancel(notificationId) From dc2314f86bb518bbc1b5f4fcc2b2368a91347e1d Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 17:14:46 +0100 Subject: [PATCH 09/10] Show toast when recording was shared to chat. Signed-off-by: Marcel Hibbe --- .../receivers/ShareRecordingToChatReceiver.kt | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt index 5afe26246..1abe36622 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt @@ -24,10 +24,10 @@ import android.app.NotificationManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Bundle import android.util.Log +import android.widget.Toast import autodagger.AutoInjector -import com.nextcloud.talk.activities.MainActivity +import com.nextcloud.talk.R import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.data.user.model.User @@ -65,9 +65,6 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { override fun onReceive(receiveContext: Context, intent: Intent?) { context = receiveContext - - // NOTE - systemNotificationId is an internal ID used on the device only. - // It is NOT the same as the notification ID used in communication with the server. systemNotificationId = intent!!.getIntExtra(KEY_SYSTEM_NOTIFICATION_ID, 0) link = intent.getStringExtra(BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL) @@ -93,7 +90,19 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { override fun onNext(genericOverall: GenericOverall) { cancelNotification(systemNotificationId!!) - context.startActivity(createOpenChatIntent()) + + // Here it would make sense to open the chat where the recording was shared to (startActivity...). + // However, as we are in a broadcast receiver, this needs a TaskStackBuilder + // combined with addNextIntentWithParentStack. For further reading, see + // https://developer.android.com/develop/ui/views/notifications/navigation#DirectEntry + // As we are using the conductor framework it might be hard the combine this or to keep an overview. + // For this reason there is only a toast for now until we got rid of conductor. + + Toast.makeText( + context, + context.resources.getString(R.string.nc_all_ok_operation), + Toast.LENGTH_LONG + ).show() } override fun onError(e: Throwable) { @@ -106,21 +115,6 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { }) } - private fun createOpenChatIntent(): Intent { - val intent = Intent(context, MainActivity::class.java) - // intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - intent.addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP - ) - - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomToken) - bundle.putParcelable(BundleKeys.KEY_USER_ENTITY, conversationOfShareTarget) - bundle.putBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false) - intent.putExtras(bundle) - return intent - } - private fun cancelNotification(notificationId: Int) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.cancel(notificationId) From a339d64d37f6b7ab6638626e9c1d08a479c63bb1 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 24 Feb 2023 17:44:31 +0100 Subject: [PATCH 10/10] Increase vibration duration for short vibration 20ms was almost not recognizable for samsung phones Signed-off-by: Marcel Hibbe --- app/src/main/java/com/nextcloud/talk/utils/VibrationUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/talk/utils/VibrationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/VibrationUtils.kt index 6753c46e7..4f2c2254c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/VibrationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/VibrationUtils.kt @@ -25,7 +25,7 @@ import android.os.VibrationEffect import android.os.Vibrator object VibrationUtils { - private const val SHORT_VIBRATE: Long = 20 + private const val SHORT_VIBRATE: Long = 100 fun vibrateShort(context: Context) { val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator