Merge pull request #3830 from nextcloud/bugfix/3315/fixEmptyCallNotificationScreen

Bugfix/3315/fix empty call notification screen
This commit is contained in:
Marcel Hibbe 2024-04-15 14:41:34 +02:00 committed by GitHub
commit 1d2f1bfeb7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 245 additions and 366 deletions

View File

@ -28,7 +28,6 @@ import com.nextcloud.talk.account.ServerSelectionActivity
import com.nextcloud.talk.account.WebViewLoginActivity import com.nextcloud.talk.account.WebViewLoginActivity
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.callnotification.CallNotificationActivity
import com.nextcloud.talk.chat.ChatActivity import com.nextcloud.talk.chat.ChatActivity
import com.nextcloud.talk.conversationlist.ConversationsListActivity import com.nextcloud.talk.conversationlist.ConversationsListActivity
import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.data.user.model.User
@ -245,19 +244,13 @@ class MainActivity : BaseActivity(), ActionBarProvider {
if (user != null && userManager.setUserAsActive(user).blockingGet()) { if (user != null && userManager.setUserAsActive(user).blockingGet()) {
if (intent.hasExtra(BundleKeys.KEY_REMOTE_TALK_SHARE)) { if (intent.hasExtra(BundleKeys.KEY_REMOTE_TALK_SHARE)) {
if (intent.getBooleanExtra(BundleKeys.KEY_REMOTE_TALK_SHARE, false)) { if (intent.getBooleanExtra(BundleKeys.KEY_REMOTE_TALK_SHARE, false)) {
val intent = Intent(this, InvitationsActivity::class.java) val invitationsIntent = Intent(this, InvitationsActivity::class.java)
startActivity(intent) startActivity(invitationsIntent)
}
} 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)
} }
} else {
val chatIntent = Intent(context, ChatActivity::class.java)
chatIntent.putExtras(intent.extras!!)
startActivity(chatIntent)
} }
} else { } else {
if (!appPreferences.isDbRoomMigrated) { if (!appPreferences.isDbRoomMigrated) {

View File

@ -18,34 +18,25 @@ import android.util.Log
import android.view.View import android.view.View
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.ViewModelProvider
import autodagger.AutoInjector import autodagger.AutoInjector
import com.google.android.material.snackbar.Snackbar
import com.nextcloud.talk.R import com.nextcloud.talk.R
import com.nextcloud.talk.activities.CallActivity import com.nextcloud.talk.activities.CallActivity
import com.nextcloud.talk.activities.CallBaseActivity import com.nextcloud.talk.activities.CallBaseActivity
import com.nextcloud.talk.api.NcApi import com.nextcloud.talk.api.NcApi
import com.nextcloud.talk.application.NextcloudTalkApplication import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication 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.data.user.model.User
import com.nextcloud.talk.databinding.CallNotificationActivityBinding import com.nextcloud.talk.databinding.CallNotificationActivityBinding
import com.nextcloud.talk.extensions.loadUserAvatar 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.models.json.participants.Participant
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.SpreedFeatures import com.nextcloud.talk.utils.SpreedFeatures
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.NotificationUtils 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
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY 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.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability import com.nextcloud.talk.utils.CapabilitiesUtil.hasSpreedFeatureCapability
import io.reactivex.disposables.Disposable
import okhttp3.Cache import okhttp3.Cache
import java.io.IOException import java.io.IOException
import javax.inject.Inject import javax.inject.Inject
@ -64,49 +55,110 @@ class CallNotificationActivity : CallBaseActivity() {
@Inject @Inject
lateinit var userManager: UserManager lateinit var userManager: UserManager
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory
lateinit var callNotificationViewModel: CallNotificationViewModel
private val disposablesList: MutableList<Disposable> = ArrayList()
private var originalBundle: Bundle? = null
private var roomToken: String? = null private var roomToken: String? = null
private var notificationTimestamp: Int? = 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 userBeingCalled: User? = null
private var credentials: String? = null
var currentConversation: ConversationModel? = null
private var leavingScreen = false private var leavingScreen = false
private var handler: Handler? = null private var handler: Handler? = null
private var binding: CallNotificationActivityBinding? = null private var binding: CallNotificationActivityBinding? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
Log.d(TAG, "onCreate")
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedApplication!!.componentApplication.inject(this) sharedApplication!!.componentApplication.inject(this)
binding = CallNotificationActivityBinding.inflate(layoutInflater) binding = CallNotificationActivityBinding.inflate(layoutInflater)
setContentView(binding!!.root) setContentView(binding!!.root)
hideNavigationIfNoPipAvailable() 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() userBeingCalled = userManager.getUserWithId(internalUserId).blockingGet()
originalBundle = extras setupCallTypeDescription()
credentials = ApiUtils.getCredentials(userBeingCalled!!.username, userBeingCalled!!.token) 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() private fun setupAvatar(isOneToOneCall: Boolean, conversationName: String?) {
if (isOneToOneCall) {
if (userManager.setUserAsActive(userBeingCalled!!).blockingGet()) { binding!!.avatarImageView.loadUserAvatar(
setCallDescriptionText() userBeingCalled!!,
callNotificationViewModel.getRoom(userBeingCalled!!, roomToken!!) 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() { override fun onStart() {
super.onStart() super.onStart()
if (handler == null) { if (handler == null) {
@ -122,136 +174,26 @@ class CallNotificationActivity : CallBaseActivity() {
private fun initClickListeners() { private fun initClickListeners() {
binding!!.callAnswerVoiceOnlyView.setOnClickListener { binding!!.callAnswerVoiceOnlyView.setOnClickListener {
Log.d(TAG, "accept call (voice only)") Log.d(TAG, "accept call (voice only)")
originalBundle!!.putBoolean(KEY_CALL_VOICE_ONLY, true) intent.extras!!.putBoolean(KEY_CALL_VOICE_ONLY, true)
proceedToCall() proceedToCall()
} }
binding!!.callAnswerCameraView.setOnClickListener { binding!!.callAnswerCameraView.setOnClickListener {
Log.d(TAG, "accept call (with video)") Log.d(TAG, "accept call (with video)")
originalBundle!!.putBoolean(KEY_CALL_VOICE_ONLY, false) intent.extras!!.putBoolean(KEY_CALL_VOICE_ONLY, false)
proceedToCall() proceedToCall()
} }
binding!!.hangupButton.setOnClickListener { hangup() } 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() { private fun hangup() {
leavingScreen = true leavingScreen = true
dispose()
finish() finish()
} }
private fun proceedToCall() { private fun proceedToCall() {
if (currentConversation != null) { val callIntent = Intent(this, CallActivity::class.java)
originalBundle!!.putString(KEY_ROOM_TOKEN, currentConversation!!.token) callIntent.putExtras(intent.extras!!)
originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName) startActivity(callIntent)
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.")
}
} }
private fun isInCallWithVideo(callFlag: Int): Boolean { private fun isInCallWithVideo(callFlag: Int): Boolean {
@ -270,18 +212,9 @@ class CallNotificationActivity : CallBaseActivity() {
handler!!.removeCallbacksAndMessages(null) handler!!.removeCallbacksAndMessages(null)
handler = null handler = null
} }
dispose()
super.onDestroy() super.onDestroy()
} }
private fun dispose() {
for (disposable in disposablesList) {
if (!disposable.isDisposed) {
disposable.dispose()
}
}
}
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) { override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig) super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
@ -308,7 +241,7 @@ class CallNotificationActivity : CallBaseActivity() {
} }
companion object { companion object {
const val TAG = "CallNotificationActivity" private val TAG = CallNotificationActivity::class.simpleName
const val ONE_SECOND: Long = 1000 const val ONE_SECOND: Long = 1000
} }
} }

View File

@ -1,65 +0,0 @@
/*
* Nextcloud Talk - Android Client
*
* SPDX-FileCopyrightText: 2023 Marcel Hibbe <dev@mhibbe.de>
* 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<ViewState> = MutableLiveData(GetRoomStartState)
val getRoomViewState: LiveData<ViewState>
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<ConversationModel> {
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
}
}

View File

@ -9,7 +9,6 @@ package com.nextcloud.talk.dagger.modules
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.nextcloud.talk.callnotification.viewmodel.CallNotificationViewModel
import com.nextcloud.talk.chat.viewmodels.ChatViewModel import com.nextcloud.talk.chat.viewmodels.ChatViewModel
import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel import com.nextcloud.talk.conversation.viewmodel.ConversationViewModel
import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel import com.nextcloud.talk.conversation.viewmodel.RenameConversationViewModel
@ -119,11 +118,6 @@ abstract class ViewModelModule {
@ViewModelKey(ChatViewModel::class) @ViewModelKey(ChatViewModel::class)
abstract fun chatViewModel(viewModel: ChatViewModel): ViewModel abstract fun chatViewModel(viewModel: ChatViewModel): ViewModel
@Binds
@IntoMap
@ViewModelKey(CallNotificationViewModel::class)
abstract fun callNotificationViewModel(viewModel: CallNotificationViewModel): ViewModel
@Binds @Binds
@IntoMap @IntoMap
@ViewModelKey(ConversationInfoViewModel::class) @ViewModelKey(ConversationInfoViewModel::class)

View File

@ -49,9 +49,11 @@ import com.nextcloud.talk.application.NextcloudTalkApplication
import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication import com.nextcloud.talk.application.NextcloudTalkApplication.Companion.sharedApplication
import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager import com.nextcloud.talk.arbitrarystorage.ArbitraryStorageManager
import com.nextcloud.talk.callnotification.CallNotificationActivity import com.nextcloud.talk.callnotification.CallNotificationActivity
import com.nextcloud.talk.chat.data.ChatRepository
import com.nextcloud.talk.models.SignatureVerification 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.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.notifications.NotificationOverall
import com.nextcloud.talk.models.json.participants.Participant import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.ParticipantsOverall 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.DismissRecordingAvailableReceiver
import com.nextcloud.talk.receivers.MarkAsReadReceiver import com.nextcloud.talk.receivers.MarkAsReadReceiver
import com.nextcloud.talk.receivers.ShareRecordingToChatReceiver import com.nextcloud.talk.receivers.ShareRecordingToChatReceiver
import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.ApiUtils import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.ConversationUtils
import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount import com.nextcloud.talk.utils.NotificationUtils.cancelAllNotificationsForAccount
@ -70,6 +74,7 @@ import com.nextcloud.talk.utils.NotificationUtils.findNotificationForRoom
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri import com.nextcloud.talk.utils.NotificationUtils.getMessageRingtoneUri
import com.nextcloud.talk.utils.NotificationUtils.loadAvatarSync import com.nextcloud.talk.utils.NotificationUtils.loadAvatarSync
import com.nextcloud.talk.utils.ParticipantPermissions
import com.nextcloud.talk.utils.PushUtils import com.nextcloud.talk.utils.PushUtils
import com.nextcloud.talk.utils.bundle.BundleKeys import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_DISMISS_RECORDING_URL 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_RESTRICT_DELETION
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_NOTIFICATION_TIMESTAMP 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_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_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SHARE_RECORDING_TO_CHAT_URL 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_SYSTEM_NOTIFICATION_ID
@ -119,6 +125,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
@Inject @Inject
var retrofit: Retrofit? = null var retrofit: Retrofit? = null
var chatRepository: ChatRepository? = null
@Inject set
@Inject
lateinit var userManager: UserManager
@JvmField @JvmField
@Inject @Inject
var okHttpClient: OkHttpClient? = null var okHttpClient: OkHttpClient? = null
@ -209,55 +221,107 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
} }
private fun handleCallPushMessage() { private fun handleCallPushMessage() {
val fullScreenIntent = Intent(context, CallNotificationActivity::class.java) val userBeingCalled = userManager.getUserWithId(signatureVerification.user!!.id!!).blockingGet()
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 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( val isOneToOneCall = conversation.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
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) bundle.putBoolean(KEY_ROOM_ONE_TO_ONE, isOneToOneCall) // ggf change in Activity? not necessary????
val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name bundle.putString(BundleKeys.KEY_CONVERSATION_NAME, conversation.name)
val uri = Uri.parse(signatureVerification.user!!.baseUrl!!) bundle.putString(BundleKeys.KEY_CONVERSATION_DISPLAY_NAME, conversation.displayName)
val baseUrl = uri.host bundle.putInt(BundleKeys.KEY_CALL_FLAG, conversation.callFlag)
val notification = val participantPermission = ParticipantPermissions(
NotificationCompat.Builder(applicationContext, notificationChannelId) userBeingCalled!!.capabilities!!.spreedCapability!!,
.setPriority(NotificationCompat.PRIORITY_HIGH) conversation
.setCategory(NotificationCompat.CATEGORY_CALL) )
.setSmallIcon(R.drawable.ic_call_black_24dp) bundle.putBoolean(
.setSubText(baseUrl) BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO,
.setShowWhen(true) participantPermission.canPublishAudio()
.setWhen(pushMessage.timestamp) )
.setContentTitle(EmojiCompat.get().process(pushMessage.subject)) bundle.putBoolean(
// auto cancel is set to false because notification (including sound) should continue while BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO,
// CallNotificationActivity is active participantPermission.canPublishVideo()
.setAutoCancel(false) )
.setOngoing(true) bundle.putBoolean(
.setContentIntent(fullScreenPendingIntent) BundleKeys.KEY_IS_MODERATOR,
.setFullScreenIntent(fullScreenPendingIntent, true) ConversationUtils.isParticipantOwnerOrModerator(conversation)
.setSound(soundUri) )
.build()
notification.flags = notification.flags or Notification.FLAG_INSISTENT
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<ConversationModel> {
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() { private fun initNcApiAndCredentials() {
@ -819,7 +883,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
notificationManager.cancel(notificationId) notificationManager.cancel(notificationId)
} }
private fun checkIfCallIsActive(signatureVerification: SignatureVerification) { private fun checkIfCallIsActive(signatureVerification: SignatureVerification, conversation: ConversationModel) {
Log.d(TAG, "checkIfCallIsActive") Log.d(TAG, "checkIfCallIsActive")
var hasParticipantsInCall = true var hasParticipantsInCall = true
var inCallOnDifferentDevice = false var inCallOnDifferentDevice = false
@ -867,7 +931,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
} }
if (!hasParticipantsInCall) { if (!hasParticipantsInCall) {
showMissedCallNotification() showMissedCallNotification(conversation)
Log.d(TAG, "no participants in call") Log.d(TAG, "no participants in call")
removeNotification(pushMessage.timestamp.toInt()) removeNotification(pushMessage.timestamp.toInt())
} }
@ -881,7 +945,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
Log.e(TAG, "Error in getPeersForCall", e) Log.e(TAG, "Error in getPeersForCall", e)
if (isCallNotificationVisible) { if (isCallNotificationVisible) {
showMissedCallNotification() showMissedCallNotification(conversation)
} }
removeNotification(pushMessage.timestamp.toInt()) removeNotification(pushMessage.timestamp.toInt())
} }
@ -889,7 +953,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
override fun onComplete() { override fun onComplete() {
if (isCallNotificationVisible) { if (isCallNotificationVisible) {
// this state can be reached when call timeout is reached. // this state can be reached when call timeout is reached.
showMissedCallNotification() showMissedCallNotification(conversation)
} }
removeNotification(pushMessage.timestamp.toInt()) 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( val isOngoingCallNotificationVisible = NotificationUtils.isNotificationVisible(
context, context,
pushMessage.timestamp.toInt() pushMessage.timestamp.toInt()
) )
if (isOngoingCallNotificationVisible) { if (isOngoingCallNotificationVisible) {
val apiVersion = ApiUtils.getConversationApiVersion( val notificationBuilder: NotificationCompat.Builder?
signatureVerification.user!!,
intArrayOf( notificationBuilder = NotificationCompat.Builder(
ApiUtils.API_V4, context!!,
ApiUtils.API_V3, NotificationUtils.NotificationChannels
1 .NOTIFICATION_CHANNEL_MESSAGES_V4.name
)
) )
ncApi.getRoom(
credentials, val notification: Notification = notificationBuilder
ApiUtils.getUrlForRoom( .setContentTitle(
apiVersion, String.format(
signatureVerification.user?.baseUrl!!, context!!.resources.getString(R.string.nc_missed_call),
pushMessage.id conversation.displayName
)
) )
) .setSmallIcon(R.drawable.ic_baseline_phone_missed_24)
.subscribeOn(Schedulers.io()) .setOngoing(false)
.retry(GET_ROOM_RETRY_COUNT) .setAutoCancel(true)
.observeOn(AndroidSchedulers.mainThread()) .setPriority(NotificationCompat.PRIORITY_LOW)
.subscribe(object : Observer<RoomOverall> { .setContentIntent(getIntentToOpenConversation())
override fun onSubscribe(d: Disposable) { .build()
// unused atm
}
override fun onNext(roomOverall: RoomOverall) { val notificationId: Int = SystemClock.uptimeMillis().toInt()
val currentConversation = roomOverall.ocs!!.data if (ActivityCompat.checkSelfPermission(
val notificationBuilder: NotificationCompat.Builder? applicationContext,
Manifest.permission.POST_NOTIFICATIONS
notificationBuilder = NotificationCompat.Builder( ) != PackageManager.PERMISSION_GRANTED
context!!, ) {
NotificationUtils.NotificationChannels // here to request the missing permissions, and then overriding
.NOTIFICATION_CHANNEL_MESSAGES_V4.name // public void onRequestPermissionsResult(int requestCode, String[] permissions,
) // int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
val notification: Notification = notificationBuilder // for ActivityCompat#requestPermissions for more details.
.setContentTitle( return
String.format( }
context!!.resources.getString(R.string.nc_missed_call), notificationManager.notify(notificationId, notification)
currentConversation!!.displayName Log.d(TAG, "'you missed a call' notification was created")
)
)
.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
}
})
} }
} }
@ -986,7 +1014,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
val bundle = Bundle() val bundle = Bundle()
bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) bundle.putString(KEY_ROOM_TOKEN, pushMessage.id)
bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
intent.putExtras(bundle) intent.putExtras(bundle)
return intent return intent
} }
@ -997,7 +1024,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
val bundle = Bundle() val bundle = Bundle()
bundle.putString(KEY_ROOM_TOKEN, pushMessage.id) bundle.putString(KEY_ROOM_TOKEN, pushMessage.id)
bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) bundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!)
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
intent.putExtras(bundle) intent.putExtras(bundle)
val requestCode = System.currentTimeMillis().toInt() val requestCode = System.currentTimeMillis().toInt()

View File

@ -35,13 +35,12 @@ import com.nextcloud.talk.upload.chunked.ChunkedFileUploader
import com.nextcloud.talk.upload.chunked.OnDataTransferProgressListener import com.nextcloud.talk.upload.chunked.OnDataTransferProgressListener
import com.nextcloud.talk.upload.normal.FileUploader import com.nextcloud.talk.upload.normal.FileUploader
import com.nextcloud.talk.users.UserManager import com.nextcloud.talk.users.UserManager
import com.nextcloud.talk.utils.CapabilitiesUtil
import com.nextcloud.talk.utils.FileUtils import com.nextcloud.talk.utils.FileUtils
import com.nextcloud.talk.utils.NotificationUtils import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.RemoteFileUtils 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_INTERNAL_USER_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN 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.permissions.PlatformPermissionUtil
import com.nextcloud.talk.utils.preferences.AppPreferences import com.nextcloud.talk.utils.preferences.AppPreferences
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
@ -295,7 +294,6 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa
bundle.putString(KEY_ROOM_TOKEN, roomToken) bundle.putString(KEY_ROOM_TOKEN, roomToken)
bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!) bundle.putLong(KEY_INTERNAL_USER_ID, currentUser.id!!)
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, false)
intent.putExtras(bundle) intent.putExtras(bundle)

View File

@ -39,6 +39,7 @@ object BundleKeys {
const val KEY_INVITED_GROUP = "KEY_INVITED_GROUP" const val KEY_INVITED_GROUP = "KEY_INVITED_GROUP"
const val KEY_INVITED_EMAIL = "KEY_INVITED_EMAIL" const val KEY_INVITED_EMAIL = "KEY_INVITED_EMAIL"
const val KEY_CONVERSATION_NAME = "KEY_CONVERSATION_NAME" 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_RECORDING_STATE = "KEY_RECORDING_STATE"
const val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY" const val KEY_CALL_VOICE_ONLY = "KEY_CALL_VOICE_ONLY"
const val KEY_CALL_WITHOUT_NOTIFICATION = "KEY_CALL_WITHOUT_NOTIFICATION" 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_PASSWORD = "KEY_PASSWORD"
const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE" const val KEY_REMOTE_TALK_SHARE = "KEY_REMOTE_TALK_SHARE"
const val KEY_CHAT_API_VERSION = "KEY_CHAT_API_VERSION" const val KEY_CHAT_API_VERSION = "KEY_CHAT_API_VERSION"
const val KEY_CALL_FLAG = "KEY_CALL_FLAG"
} }

View File

@ -52,8 +52,6 @@
android:background="@drawable/shape_oval" android:background="@drawable/shape_oval"
android:backgroundTint="@color/nc_darkGreen" android:backgroundTint="@color/nc_darkGreen"
android:src="@drawable/ic_videocam_white_24px" android:src="@drawable/ic_videocam_white_24px"
android:visibility="gone"
tools:visibility="visible"
android:contentDescription="@string/nc_call_button_content_description_answer_video_call" /> android:contentDescription="@string/nc_call_button_content_description_answer_video_call" />
</LinearLayout> </LinearLayout>