From 7e1ebebd5d678b7ddd2e62943ba926ca0bcf1482 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 12 Apr 2024 11:45:05 +0200 Subject: [PATCH 1/2] remove unnecessary KEY_FROM_NOTIFICATION_START_CALL handling this seems to be a relict caused by conductor back then. CallNotificationActivity is opened directly. There shouldn't have been any scenario anymore when it would be opened by MainActivity. Back then with conductor, this was done because there must have been an activity to open which then opened a controller. Signed-off-by: Marcel Hibbe --- .../nextcloud/talk/activities/MainActivity.kt | 19 ++++++------------- .../nextcloud/talk/jobs/NotificationWorker.kt | 2 -- .../talk/jobs/UploadAndShareFilesWorker.kt | 4 +--- 3 files changed, 7 insertions(+), 18 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 a25927dd4..e56f4f898 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -28,7 +28,6 @@ import com.nextcloud.talk.account.ServerSelectionActivity import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication -import com.nextcloud.talk.callnotification.CallNotificationActivity import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.data.user.model.User @@ -245,19 +244,13 @@ class MainActivity : BaseActivity(), ActionBarProvider { if (user != null && userManager.setUserAsActive(user).blockingGet()) { if (intent.hasExtra(BundleKeys.KEY_REMOTE_TALK_SHARE)) { if (intent.getBooleanExtra(BundleKeys.KEY_REMOTE_TALK_SHARE, false)) { - val intent = Intent(this, InvitationsActivity::class.java) - startActivity(intent) - } - } else if (intent.hasExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL)) { - if (intent.getBooleanExtra(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)) { - val callNotificationIntent = Intent(this, CallNotificationActivity::class.java) - intent.extras?.let { callNotificationIntent.putExtras(it) } - startActivity(callNotificationIntent) - } else { - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(intent.extras!!) - startActivity(chatIntent) + val invitationsIntent = Intent(this, InvitationsActivity::class.java) + startActivity(invitationsIntent) } + } else { + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(intent.extras!!) + startActivity(chatIntent) } } else { if (!appPreferences.isDbRoomMigrated) { 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 bc015687e..fbbbfc9fe 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -986,7 +986,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val bundle = Bundle() bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) - bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) intent.putExtras(bundle) return intent } @@ -997,7 +996,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val bundle = Bundle() bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) - bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) intent.putExtras(bundle) val requestCode = System.currentTimeMillis().toInt() diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index 20eef7217..93d0b9212 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -35,13 +35,12 @@ import com.nextcloud.talk.upload.chunked.ChunkedFileUploader import com.nextcloud.talk.upload.chunked.OnDataTransferProgressListener import com.nextcloud.talk.upload.normal.FileUploader import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.FileUtils import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.RemoteFileUtils -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_ROOM_TOKEN -import com.nextcloud.talk.utils.CapabilitiesUtil import com.nextcloud.talk.utils.permissions.PlatformPermissionUtil import com.nextcloud.talk.utils.preferences.AppPreferences import okhttp3.MediaType.Companion.toMediaTypeOrNull @@ -295,7 +294,6 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!) - bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false) intent.putExtras(bundle) From 2485b140096a747cd70bd6ebe2db8b1db77d2588 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Fri, 12 Apr 2024 14:28:17 +0200 Subject: [PATCH 2/2] get room in NotificationWorker so CallNotificationActivity doesn't have to wait for it The get-room request in CallNotificationActivity was sometimes too slow which caused the activity to be unresponsive. With this commit, the room is first loaded before the user gets a notification. For now all necessary values are passed as intent extras. In the future with offline support, there might be reasons to load it from DB in CallNotificationActivity. Signed-off-by: Marcel Hibbe --- .../CallNotificationActivity.kt | 245 ++++++---------- .../viewmodel/CallNotificationViewModel.kt | 65 ----- .../talk/dagger/modules/ViewModelModule.kt | 6 - .../nextcloud/talk/jobs/NotificationWorker.kt | 266 ++++++++++-------- .../nextcloud/talk/utils/bundle/BundleKeys.kt | 2 + .../res/layout/call_notification_activity.xml | 2 - 6 files changed, 238 insertions(+), 348 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt index 0533d9b22..01a2c84b3 100644 --- a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt @@ -18,34 +18,25 @@ import android.util.Log import android.view.View import androidx.annotation.RequiresApi import androidx.core.app.NotificationManagerCompat -import androidx.lifecycle.ViewModelProvider import autodagger.AutoInjector -import com.google.android.material.snackbar.Snackbar import com.nextcloud.talk.R import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.CallBaseActivity import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication -import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.databinding.CallNotificationActivityBinding import com.nextcloud.talk.extensions.loadUserAvatar -import com.nextcloud.talk.models.domain.ConversationModel -import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.SpreedFeatures -import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.NotificationUtils -import com.nextcloud.talk.utils.ParticipantPermissions import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY -import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability -import io.reactivex.disposables.Disposable import okhttp3.Cache import java.io.IOException import javax.inject.Inject @@ -64,49 +55,110 @@ class CallNotificationActivity : CallBaseActivity() { @Inject lateinit var userManager: UserManager - @Inject - lateinit var viewModelFactory: ViewModelProvider.Factory - - lateinit var callNotificationViewModel: CallNotificationViewModel - - private val disposablesList: MutableList = ArrayList() - private var originalBundle: Bundle? = null private var roomToken: String? = null private var notificationTimestamp: Int? = null + private var displayName: String? = null + private var callFlag: Int = 0 + private var isOneToOneCall: Boolean = true + private var conversationName: String? = null + private var internalUserId: Long = -1 + private var userBeingCalled: User? = null - private var credentials: String? = null - var currentConversation: ConversationModel? = null private var leavingScreen = false private var handler: Handler? = null private var binding: CallNotificationActivityBinding? = null override fun onCreate(savedInstanceState: Bundle?) { - Log.d(TAG, "onCreate") super.onCreate(savedInstanceState) sharedApplication!!.componentApplication.inject(this) binding = CallNotificationActivityBinding.inflate(layoutInflater) setContentView(binding!!.root) hideNavigationIfNoPipAvailable() - val extras = intent.extras - roomToken = extras!!.getString(KEY_ROOM_TOKEN, "") - notificationTimestamp = extras.getInt(BundleKeys.KEY_NOTIFICATION_TIMESTAMP) - val internalUserId = extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) + handleExtras() userBeingCalled = userManager.getUserWithId(internalUserId).blockingGet() - originalBundle = extras - credentials = ApiUtils.getCredentials(userBeingCalled!!.username, userBeingCalled!!.token) + setupCallTypeDescription() + binding!!.conversationNameTextView.text = displayName + setupAvatar(isOneToOneCall, conversationName) + initClickListeners() + setupNotificationCanceledRoutine() + } - callNotificationViewModel = ViewModelProvider(this, viewModelFactory)[CallNotificationViewModel::class.java] + private fun handleExtras() { + val extras = intent.extras!! + roomToken = extras.getString(KEY_ROOM_TOKEN, "") + notificationTimestamp = extras.getInt(BundleKeys.KEY_NOTIFICATION_TIMESTAMP) + displayName = extras.getString(BundleKeys.KEY_CONVERSATION_DISPLAY_NAME, "") + callFlag = extras.getInt(BundleKeys.KEY_CALL_FLAG) + isOneToOneCall = extras.getBoolean(BundleKeys.KEY_ROOM_ONE_TO_ONE) + conversationName = extras.getString(BundleKeys.KEY_CONVERSATION_NAME, "") + internalUserId = extras.getLong(BundleKeys.KEY_INTERNAL_USER_ID) + } - initObservers() - - if (userManager.setUserAsActive(userBeingCalled!!).blockingGet()) { - setCallDescriptionText() - callNotificationViewModel.getRoom(userBeingCalled!!, roomToken!!) + private fun setupAvatar(isOneToOneCall: Boolean, conversationName: String?) { + if (isOneToOneCall) { + binding!!.avatarImageView.loadUserAvatar( + userBeingCalled!!, + conversationName!!, + true, + false + ) + } else { + binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group) } } + private fun setupCallTypeDescription() { + val apiVersion = ApiUtils.getConversationApiVersion( + userBeingCalled!!, + intArrayOf( + ApiUtils.API_V4, + ApiUtils.API_V3, + 1 + ) + ) + + if (apiVersion >= ApiUtils.API_V3) { + val hasCallFlags = hasSpreedFeatureCapability( + userBeingCalled?.capabilities?.spreedCapability!!, + SpreedFeatures.CONVERSATION_CALL_FLAGS + ) + if (hasCallFlags) { + if (isInCallWithVideo(callFlag)) { + binding!!.incomingCallVoiceOrVideoTextView.text = String.format( + resources.getString(R.string.nc_call_video), + resources.getString(R.string.nc_app_product_name) + ) + } else { + binding!!.incomingCallVoiceOrVideoTextView.text = String.format( + resources.getString(R.string.nc_call_voice), + resources.getString(R.string.nc_app_product_name) + ) + } + } + } else { + val callDescriptionWithoutTypeInfo = String.format( + resources.getString(R.string.nc_call_unknown), + resources.getString(R.string.nc_app_product_name) + ) + binding!!.incomingCallVoiceOrVideoTextView.text = callDescriptionWithoutTypeInfo + } + } + + private fun setupNotificationCanceledRoutine() { + val notificationHandler = Handler(Looper.getMainLooper()) + notificationHandler.post(object : Runnable { + override fun run() { + if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) { + notificationHandler.postDelayed(this, ONE_SECOND) + } else { + finish() + } + } + }) + } + override fun onStart() { super.onStart() if (handler == null) { @@ -122,136 +174,26 @@ class CallNotificationActivity : CallBaseActivity() { private fun initClickListeners() { binding!!.callAnswerVoiceOnlyView.setOnClickListener { Log.d(TAG, "accept call (voice only)") - originalBundle!!.putBoolean(KEY_CALL_VOICE_ONLY, true) + intent.extras!!.putBoolean(KEY_CALL_VOICE_ONLY, true) proceedToCall() } binding!!.callAnswerCameraView.setOnClickListener { Log.d(TAG, "accept call (with video)") - originalBundle!!.putBoolean(KEY_CALL_VOICE_ONLY, false) + intent.extras!!.putBoolean(KEY_CALL_VOICE_ONLY, false) proceedToCall() } binding!!.hangupButton.setOnClickListener { hangup() } } - private fun initObservers() { - val apiVersion = ApiUtils.getConversationApiVersion( - userBeingCalled!!, - intArrayOf( - ApiUtils.API_V4, - ApiUtils.API_V3, - 1 - ) - ) - - callNotificationViewModel.getRoomViewState.observe(this) { state -> - when (state) { - is CallNotificationViewModel.GetRoomSuccessState -> { - currentConversation = state.conversationModel - - binding!!.conversationNameTextView.text = currentConversation!!.displayName - if (currentConversation!!.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { - binding!!.avatarImageView.loadUserAvatar( - userBeingCalled!!, - currentConversation!!.name!!, - true, - false - ) - } else { - binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group) - } - - val notificationHandler = Handler(Looper.getMainLooper()) - notificationHandler.post(object : Runnable { - override fun run() { - if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) { - notificationHandler.postDelayed(this, ONE_SECOND) - } else { - finish() - } - } - }) - - showAnswerControls() - - if (apiVersion >= ApiUtils.API_V3) { - val hasCallFlags = hasSpreedFeatureCapability( - userBeingCalled?.capabilities?.spreedCapability!!, - SpreedFeatures.CONVERSATION_CALL_FLAGS - ) - if (hasCallFlags) { - if (isInCallWithVideo(currentConversation!!.callFlag)) { - binding!!.incomingCallVoiceOrVideoTextView.text = String.format( - resources.getString(R.string.nc_call_video), - resources.getString(R.string.nc_app_product_name) - ) - } else { - binding!!.incomingCallVoiceOrVideoTextView.text = String.format( - resources.getString(R.string.nc_call_voice), - resources.getString(R.string.nc_app_product_name) - ) - } - } - } - - initClickListeners() - } - - is CallNotificationViewModel.GetRoomErrorState -> { - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - } - - else -> {} - } - } - } - - private fun setCallDescriptionText() { - val callDescriptionWithoutTypeInfo = String.format( - resources.getString(R.string.nc_call_unknown), - resources.getString(R.string.nc_app_product_name) - ) - binding!!.incomingCallVoiceOrVideoTextView.text = callDescriptionWithoutTypeInfo - } - - private fun showAnswerControls() { - binding!!.callAnswerCameraView.visibility = View.VISIBLE - binding!!.callAnswerVoiceOnlyView.visibility = View.VISIBLE - } - private fun hangup() { leavingScreen = true - dispose() finish() } private fun proceedToCall() { - if (currentConversation != null) { - originalBundle!!.putString(KEY_ROOM_TOKEN, currentConversation!!.token) - originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName) - - val participantPermission = ParticipantPermissions( - userBeingCalled!!.capabilities!!.spreedCapability!!, - currentConversation!! - ) - originalBundle!!.putBoolean( - BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO, - participantPermission.canPublishAudio() - ) - originalBundle!!.putBoolean( - BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO, - participantPermission.canPublishVideo() - ) - originalBundle!!.putBoolean( - BundleKeys.KEY_IS_MODERATOR, - ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!) - ) - - val intent = Intent(this, CallActivity::class.java) - intent.putExtras(originalBundle!!) - startActivity(intent) - } else { - Log.w(TAG, "conversation was still null when clicked to answer call. User has to click another time.") - } + val callIntent = Intent(this, CallActivity::class.java) + callIntent.putExtras(intent.extras!!) + startActivity(callIntent) } private fun isInCallWithVideo(callFlag: Int): Boolean { @@ -270,18 +212,9 @@ class CallNotificationActivity : CallBaseActivity() { handler!!.removeCallbacksAndMessages(null) handler = null } - dispose() super.onDestroy() } - private fun dispose() { - for (disposable in disposablesList) { - if (!disposable.isDisposed) { - disposable.dispose() - } - } - } - @RequiresApi(api = Build.VERSION_CODES.O) override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) @@ -308,7 +241,7 @@ class CallNotificationActivity : CallBaseActivity() { } companion object { - const val TAG = "CallNotificationActivity" + private val TAG = CallNotificationActivity::class.simpleName const val ONE_SECOND: Long = 1000 } } diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt b/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt deleted file mode 100644 index d446e7ec8..000000000 --- a/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Nextcloud Talk - Android Client - * - * SPDX-FileCopyrightText: 2023 Marcel Hibbe - * SPDX-License-Identifier: GPL-3.0-or-later - */ -package com.nextcloud.talk.callnotification.viewmodel - -import android.util.Log -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import com.nextcloud.talk.chat.data.ChatRepository -import com.nextcloud.talk.data.user.model.User -import com.nextcloud.talk.models.domain.ConversationModel -import io.reactivex.Observer -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import javax.inject.Inject - -class CallNotificationViewModel @Inject constructor(private val repository: ChatRepository) : - ViewModel() { - - sealed interface ViewState - - object GetRoomStartState : ViewState - object GetRoomErrorState : ViewState - open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState - - private val _getRoomViewState: MutableLiveData = MutableLiveData(GetRoomStartState) - val getRoomViewState: LiveData - get() = _getRoomViewState - - fun getRoom(user: User, token: String) { - _getRoomViewState.value = GetRoomStartState - repository.getRoom(user, token) - .subscribeOn(Schedulers.io()) - ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(GetRoomObserver()) - } - - inner class GetRoomObserver : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(conversationModel: ConversationModel) { - _getRoomViewState.value = GetRoomSuccessState(conversationModel) - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Error when fetching room") - _getRoomViewState.value = GetRoomErrorState - } - - override fun onComplete() { - // unused atm - } - } - - companion object { - private val TAG = CallNotificationViewModel::class.simpleName - } -} diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index be1bf6c33..55cf99769 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -9,7 +9,6 @@ package com.nextcloud.talk.dagger.modules import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel import com.nextcloud.talk.chat.viewmodels.ChatViewModel import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel @@ -119,11 +118,6 @@ abstract class ViewModelModule { @ViewModelKey(ChatViewModel::class) abstract fun chatViewModel(viewModel: ChatViewModel): ViewModel - @Binds - @IntoMap - @ViewModelKey(CallNotificationViewModel::class) - abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel - @Binds @IntoMap @ViewModelKey(ConversationInfoViewModel::class) 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 fbbbfc9fe..221f4adaa 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -49,9 +49,11 @@ import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.callnotification.CallNotificationActivity +import com.nextcloud.talk.chat.data.ChatRepository import com.nextcloud.talk.models.SignatureVerification +import com.nextcloud.talk.models.domain.ConversationModel +import com.nextcloud.talk.models.domain.ConversationType import com.nextcloud.talk.models.json.chat.ChatUtils.Companion.getParsedMessage -import com.nextcloud.talk.models.json.conversations.RoomOverall import com.nextcloud.talk.models.json.notifications.NotificationOverall import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.ParticipantsOverall @@ -61,7 +63,9 @@ 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.users.UserManager import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.ConversationUtils import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount @@ -70,6 +74,7 @@ import com.nextcloud.talk.utils.NotificationUtils.findNotificationForRoom import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.loadAvatarSync +import com.nextcloud.talk.utils.ParticipantPermissions import com.nextcloud.talk.utils.PushUtils import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_DISMISS_RECORDING_URL @@ -80,6 +85,7 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_ID 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_REMOTE_TALK_SHARE +import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ONE_TO_ONE 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 @@ -119,6 +125,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor @Inject var retrofit: Retrofit? = null + var chatRepository: ChatRepository? = null + @Inject set + + @Inject + lateinit var userManager: UserManager + @JvmField @Inject var okHttpClient: OkHttpClient? = null @@ -209,55 +221,107 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } private fun handleCallPushMessage() { - val fullScreenIntent = Intent(context, CallNotificationActivity::class.java) - val bundle = Bundle() - bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) - bundle.putInt(KEY_NOTIFICATION_TIMESTAMP, pushMessage.timestamp.toInt()) - bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) - bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, true) - fullScreenIntent.putExtras(bundle) - fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK + val userBeingCalled = userManager.getUserWithId(signatureVerification.user!!.id!!).blockingGet() - val requestCode = System.currentTimeMillis().toInt() + fun prepareCallNotificationScreen(conversation: ConversationModel) { + val fullScreenIntent = Intent(context, CallNotificationActivity::class.java) + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) + bundle.putInt(KEY_NOTIFICATION_TIMESTAMP, pushMessage.timestamp.toInt()) + bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) + bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, true) - val fullScreenPendingIntent = PendingIntent.getActivity( - context, - requestCode, - fullScreenIntent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } - ) + val isOneToOneCall = conversation.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL - val soundUri = getCallRingtoneUri(applicationContext, appPreferences) - val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name - val uri = Uri.parse(signatureVerification.user!!.baseUrl!!) - val baseUrl = uri.host + bundle.putBoolean(KEY_ROOM_ONE_TO_ONE, isOneToOneCall) // ggf change in Activity? not necessary???? + bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, conversation.name) + bundle.putString(BundleKeys.KEY_CONVERSATION_DISPLAY_NAME, conversation.displayName) + bundle.putInt(BundleKeys.KEY_CALL_FLAG, conversation.callFlag) - val notification = - NotificationCompat.Builder(applicationContext, notificationChannelId) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(NotificationCompat.CATEGORY_CALL) - .setSmallIcon(R.drawable.ic_call_black_24dp) - .setSubText(baseUrl) - .setShowWhen(true) - .setWhen(pushMessage.timestamp) - .setContentTitle(EmojiCompat.get().process(pushMessage.subject)) - // auto cancel is set to false because notification (including sound) should continue while - // CallNotificationActivity is active - .setAutoCancel(false) - .setOngoing(true) - .setContentIntent(fullScreenPendingIntent) - .setFullScreenIntent(fullScreenPendingIntent, true) - .setSound(soundUri) - .build() - notification.flags = notification.flags or Notification.FLAG_INSISTENT + val participantPermission = ParticipantPermissions( + userBeingCalled!!.capabilities!!.spreedCapability!!, + conversation + ) + bundle.putBoolean( + BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO, + participantPermission.canPublishAudio() + ) + bundle.putBoolean( + BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO, + participantPermission.canPublishVideo() + ) + bundle.putBoolean( + BundleKeys.KEY_IS_MODERATOR, + ConversationUtils.isParticipantOwnerOrModerator(conversation) + ) - sendNotification(pushMessage.timestamp.toInt(), notification) + fullScreenIntent.putExtras(bundle) + fullScreenIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK - checkIfCallIsActive(signatureVerification) + val requestCode = System.currentTimeMillis().toInt() + + val fullScreenPendingIntent = PendingIntent.getActivity( + context, + requestCode, + fullScreenIntent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + ) + + val soundUri = getCallRingtoneUri(applicationContext, appPreferences) + val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name + val uri = Uri.parse(signatureVerification.user!!.baseUrl!!) + val baseUrl = uri.host + + val notification = + NotificationCompat.Builder(applicationContext, notificationChannelId) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_CALL) + .setSmallIcon(R.drawable.ic_call_black_24dp) + .setSubText(baseUrl) + .setShowWhen(true) + .setWhen(pushMessage.timestamp) + .setContentTitle(EmojiCompat.get().process(pushMessage.subject)) + // auto cancel is set to false because notification (including sound) should continue while + // CallNotificationActivity is active + .setAutoCancel(false) + .setOngoing(true) + .setContentIntent(fullScreenPendingIntent) + .setFullScreenIntent(fullScreenPendingIntent, true) + .setSound(soundUri) + .build() + notification.flags = notification.flags or Notification.FLAG_INSISTENT + + sendNotification(pushMessage.timestamp.toInt(), notification) + + checkIfCallIsActive(signatureVerification, conversation) + } + + chatRepository?.getRoom(userBeingCalled, roomToken = pushMessage.id!!) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(conversation: ConversationModel) { + if (userManager.setUserAsActive(userBeingCalled!!).blockingGet()) { + prepareCallNotificationScreen(conversation) + } + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get room", e) + } + + override fun onComplete() { + // unused atm + } + }) } private fun initNcApiAndCredentials() { @@ -819,7 +883,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationManager.cancel(notificationId) } - private fun checkIfCallIsActive(signatureVerification: SignatureVerification) { + private fun checkIfCallIsActive(signatureVerification: SignatureVerification, conversation: ConversationModel) { Log.d(TAG, "checkIfCallIsActive") var hasParticipantsInCall = true var inCallOnDifferentDevice = false @@ -867,7 +931,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } if (!hasParticipantsInCall) { - showMissedCallNotification() + showMissedCallNotification(conversation) Log.d(TAG, "no participants in call") removeNotification(pushMessage.timestamp.toInt()) } @@ -881,7 +945,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor override fun onError(e: Throwable) { Log.e(TAG, "Error in getPeersForCall", e) if (isCallNotificationVisible) { - showMissedCallNotification() + showMissedCallNotification(conversation) } removeNotification(pushMessage.timestamp.toInt()) } @@ -889,7 +953,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor override fun onComplete() { if (isCallNotificationVisible) { // this state can be reached when call timeout is reached. - showMissedCallNotification() + showMissedCallNotification(conversation) } removeNotification(pushMessage.timestamp.toInt()) @@ -897,86 +961,50 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor }) } - fun showMissedCallNotification() { + fun showMissedCallNotification(conversation: ConversationModel) { val isOngoingCallNotificationVisible = NotificationUtils.isNotificationVisible( context, pushMessage.timestamp.toInt() ) if (isOngoingCallNotificationVisible) { - val apiVersion = ApiUtils.getConversationApiVersion( - signatureVerification.user!!, - intArrayOf( - ApiUtils.API_V4, - ApiUtils.API_V3, - 1 - ) + val notificationBuilder: NotificationCompat.Builder? + + notificationBuilder = NotificationCompat.Builder( + context!!, + NotificationUtils.NotificationChannels + .NOTIFICATION_CHANNEL_MESSAGES_V4.name ) - ncApi.getRoom( - credentials, - ApiUtils.getUrlForRoom( - apiVersion, - signatureVerification.user?.baseUrl!!, - pushMessage.id + + val notification: Notification = notificationBuilder + .setContentTitle( + String.format( + context!!.resources.getString(R.string.nc_missed_call), + conversation.displayName + ) ) - ) - .subscribeOn(Schedulers.io()) - .retry(GET_ROOM_RETRY_COUNT) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .setSmallIcon(R.drawable.ic_baseline_phone_missed_24) + .setOngoing(false) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentIntent(getIntentToOpenConversation()) + .build() - override fun onNext(roomOverall: RoomOverall) { - val currentConversation = roomOverall.ocs!!.data - val notificationBuilder: NotificationCompat.Builder? - - notificationBuilder = NotificationCompat.Builder( - context!!, - NotificationUtils.NotificationChannels - .NOTIFICATION_CHANNEL_MESSAGES_V4.name - ) - - val notification: Notification = notificationBuilder - .setContentTitle( - String.format( - context!!.resources.getString(R.string.nc_missed_call), - currentConversation!!.displayName - ) - ) - .setSmallIcon(R.drawable.ic_baseline_phone_missed_24) - .setOngoing(false) - .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setContentIntent(getIntentToOpenConversation()) - .build() - - val notificationId: Int = SystemClock.uptimeMillis().toInt() - if (ActivityCompat.checkSelfPermission( - applicationContext, - Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - // here to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. - return - } - notificationManager.notify(notificationId, notification) - Log.d(TAG, "'you missed a call' notification was created") - } - - override fun onError(e: Throwable) { - Log.e(TAG, "An error occurred while fetching room for the 'missed call' notification", e) - } - - override fun onComplete() { - // unused atm - } - }) + val notificationId: Int = SystemClock.uptimeMillis().toInt() + if (ActivityCompat.checkSelfPermission( + applicationContext, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return + } + notificationManager.notify(notificationId, notification) + Log.d(TAG, "'you missed a call' notification was created") } } 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 57f163db5..50e8c28fd 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 @@ -39,6 +39,7 @@ object BundleKeys { const val KEY_INVITED_GROUP = "KEY_INVITED_GROUP" const val KEY_INVITED_EMAIL = "KEY_INVITED_EMAIL" const val KEY_CONVERSATION_NAME = "KEY_CONVERSATION_NAME" + const val KEY_CONVERSATION_DISPLAY_NAME = "KEY_CONVERSATION_DISPLAY_NAME" const val KEY_RECORDING_STATE = "KEY_RECORDING_STATE" const val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY" const val KEY_CALL_WITHOUT_NOTIFICATION = "KEY_CALL_WITHOUT_NOTIFICATION" @@ -75,4 +76,5 @@ object BundleKeys { const val KEY_PASSWORD = "KEY_PASSWORD" const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE" const val KEY_CHAT_API_VERSION = "KEY_CHAT_API_VERSION" + const val KEY_CALL_FLAG = "KEY_CALL_FLAG" } diff --git a/app/src/main/res/layout/call_notification_activity.xml b/app/src/main/res/layout/call_notification_activity.xml index d5b0435ab..49d823648 100644 --- a/app/src/main/res/layout/call_notification_activity.xml +++ b/app/src/main/res/layout/call_notification_activity.xml @@ -52,8 +52,6 @@ android:background="@drawable/shape_oval" android:backgroundTint="@color/nc_darkGreen" android:src="@drawable/ic_videocam_white_24px" - android:visibility="gone" - tools:visibility="visible" android:contentDescription="@string/nc_call_button_content_description_answer_video_call" />