remove ringtone logic (only make it depend on notification!)

this commit removes the logic to play the ringtone in CallNotificationActivity. Playing ringtone should only be controlled by the notification channel from OS!

furthermore the checks if a call is stopped or is still ongoing etc was removed from CallNotificationActivity. Instead the CallNotificationActivity now is completely dependent on the notification. If the notification is canceled, the Activity stops. If the Notification is ongoing and hangup of accept call is clicked, then the notification is canceled (including the ringtone).

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2022-11-14 14:55:49 +01:00 committed by Tim Krüger
parent 13f60d1b2d
commit 9c4b0a00c6
No known key found for this signature in database
GPG Key ID: FECE3A7222C52A4E
4 changed files with 51 additions and 173 deletions

View File

@ -22,22 +22,15 @@
package com.nextcloud.talk.activities
import android.annotation.SuppressLint
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.media.AudioAttributes
import android.media.MediaPlayer
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.SystemClock
import android.util.Log
import android.view.View
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import autodagger.AutoInjector
import com.nextcloud.talk.R
import com.nextcloud.talk.api.NcApi
@ -49,11 +42,8 @@ import com.nextcloud.talk.extensions.loadAvatar
import com.nextcloud.talk.models.json.conversations.Conversation
import com.nextcloud.talk.models.json.conversations.RoomOverall
import com.nextcloud.talk.models.json.participants.Participant
import com.nextcloud.talk.models.json.participants.ParticipantsOverall
import com.nextcloud.talk.utils.ApiUtils
import com.nextcloud.talk.utils.DoNotDisturbUtils.shouldPlaySound
import com.nextcloud.talk.utils.NotificationUtils
import com.nextcloud.talk.utils.NotificationUtils.getCallRingtoneUri
import com.nextcloud.talk.utils.ParticipantPermissions
import com.nextcloud.talk.utils.bundle.BundleKeys
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CALL_VOICE_ONLY
@ -62,7 +52,6 @@ import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew.hasSpreedFeatureCapability
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
@ -71,8 +60,8 @@ import kotlinx.android.synthetic.main.call_item.*
import okhttp3.Cache
import org.parceler.Parcels
import java.io.IOException
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import kotlin.concurrent.thread
@SuppressLint("LongLogTag")
@AutoInjector(NextcloudTalkApplication::class)
@ -88,13 +77,14 @@ class CallNotificationActivity : CallBaseActivity() {
private val disposablesList: MutableList<Disposable> = ArrayList()
private var originalBundle: Bundle? = null
private var roomToken: String? = null
private var notificationTimestamp: Int? = null
private var userBeingCalled: User? = null
private var credentials: String? = null
private var currentConversation: Conversation? = null
private var mediaPlayer: MediaPlayer? = 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)
@ -104,6 +94,7 @@ class CallNotificationActivity : CallBaseActivity() {
hideNavigationIfNoPipAvailable()
val extras = intent.extras
roomToken = extras!!.getString(KEY_ROOM_TOKEN, "")
notificationTimestamp = extras.getInt(BundleKeys.KEY_NOTIFICATION_TIMESTAMP)
currentConversation = Parcels.unwrap(extras.getParcelable(KEY_ROOM))
userBeingCalled = extras.getParcelable(KEY_USER_ENTITY)
originalBundle = extras
@ -114,9 +105,6 @@ class CallNotificationActivity : CallBaseActivity() {
} else {
setUpAfterConversationIsKnown()
}
if (shouldPlaySound()) {
playRingtoneSound()
}
initClickListeners()
}
@ -162,7 +150,6 @@ class CallNotificationActivity : CallBaseActivity() {
private fun hangup() {
leavingScreen = true
dispose()
endMediaNotifications()
finish()
}
@ -188,108 +175,6 @@ class CallNotificationActivity : CallBaseActivity() {
startActivity(intent)
}
private fun checkIfAnyParticipantsRemainInRoom() {
val apiVersion = ApiUtils.getCallApiVersion(userBeingCalled, intArrayOf(ApiUtils.APIv4, 1))
ncApi!!.getPeersForCall(
credentials,
ApiUtils.getUrlForCall(
apiVersion,
userBeingCalled!!.baseUrl,
currentConversation!!.token
)
)
.subscribeOn(Schedulers.io())
.repeatWhen { completed: Observable<Any?> ->
completed.zipWith(Observable.range(TIMER_START, TIMER_COUNT)) { _: Any?, i: Int? -> i!! }
.flatMap { Observable.timer(TIMER_DELAY, TimeUnit.SECONDS) }
.takeWhile { !leavingScreen }
}
.subscribe(object : Observer<ParticipantsOverall> {
override fun onSubscribe(d: Disposable) {
disposablesList.add(d)
}
override fun onNext(participantsOverall: ParticipantsOverall) {
val hasParticipantsInCall: Boolean
var inCallOnDifferentDevice = false
val participantList = participantsOverall.ocs!!.data
hasParticipantsInCall = participantList!!.isNotEmpty()
if (hasParticipantsInCall) {
for (participant in participantList) {
if (participant.calculatedActorType === Participant.ActorType.USERS &&
participant.calculatedActorId == userBeingCalled!!.userId
) {
inCallOnDifferentDevice = true
break
}
}
}
if (inCallOnDifferentDevice) {
runOnUiThread { hangup() }
}
if (!hasParticipantsInCall) {
showMissedCallNotification()
runOnUiThread { hangup() }
}
}
override fun onError(e: Throwable) {
Log.e(TAG, "error while getPeersForCall", e)
}
override fun onComplete() {
showMissedCallNotification()
runOnUiThread { hangup() }
}
})
}
private fun showMissedCallNotification() {
val mNotifyManager: NotificationManager?
val mBuilder: NotificationCompat.Builder?
mNotifyManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
mBuilder = NotificationCompat.Builder(
context,
NotificationUtils.NotificationChannels
.NOTIFICATION_CHANNEL_MESSAGES_V4.name
)
val notification: Notification = mBuilder
.setContentTitle(
String.format(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()
mNotifyManager.notify(notificationId, notification)
}
private fun getIntentToOpenConversation(): PendingIntent? {
val bundle = Bundle()
val intent = Intent(context, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK
bundle.putString(KEY_ROOM_TOKEN, currentConversation?.token)
bundle.putParcelable(KEY_USER_ENTITY, userBeingCalled)
bundle.putBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false)
intent.putExtras(bundle)
val requestCode = System.currentTimeMillis().toInt()
val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else {
0
}
return PendingIntent.getActivity(context, requestCode, intent, intentFlag)
}
@Suppress("MagicNumber")
private fun handleFromNotification() {
val apiVersion = ApiUtils.getConversationApiVersion(
@ -353,18 +238,26 @@ class CallNotificationActivity : CallBaseActivity() {
} else {
binding!!.avatarImageView.setImageResource(R.drawable.ic_circular_group)
}
checkIfAnyParticipantsRemainInRoom()
thread(start = true) {
var isNotificationOpen = true
while (isNotificationOpen) {
Thread.sleep(1000)
Log.d(TAG, ".")
if (!NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) {
isNotificationOpen = false
finish()
}
}
}
showAnswerControls()
}
private fun endMediaNotifications() {
if (mediaPlayer != null) {
if (mediaPlayer!!.isPlaying) {
mediaPlayer!!.stop()
}
mediaPlayer!!.release()
mediaPlayer = null
}
override fun onStop() {
val notificationManager = NotificationManagerCompat.from(context)
notificationManager.cancel(notificationTimestamp!!)
super.onStop()
}
public override fun onDestroy() {
@ -374,7 +267,6 @@ class CallNotificationActivity : CallBaseActivity() {
handler = null
}
dispose()
endMediaNotifications()
super.onDestroy()
}
@ -386,26 +278,6 @@ class CallNotificationActivity : CallBaseActivity() {
}
}
private fun playRingtoneSound() {
val ringtoneUri = getCallRingtoneUri(applicationContext, appPreferences)
if (ringtoneUri != null) {
mediaPlayer = MediaPlayer()
try {
mediaPlayer!!.setDataSource(this, ringtoneUri)
mediaPlayer!!.isLooping = true
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
.build()
mediaPlayer!!.setAudioAttributes(audioAttributes)
mediaPlayer!!.setOnPreparedListener { mediaPlayer!!.start() }
mediaPlayer!!.prepareAsync()
} catch (e: IOException) {
Log.e(TAG, "Failed to set data source")
}
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
@ -433,9 +305,6 @@ class CallNotificationActivity : CallBaseActivity() {
companion object {
const val TAG = "CallNotificationActivity"
const val TIMER_START = 1
const val TIMER_COUNT = 12
const val TIMER_DELAY: Long = 5
const val GET_ROOM_RETRY_COUNT: Long = 3
}
}

View File

@ -24,10 +24,8 @@
package com.nextcloud.talk.jobs
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Context.NOTIFICATION_SERVICE
import android.content.Intent
import android.graphics.Bitmap
import android.media.AudioAttributes
@ -85,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_TIMESTAMP
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_SYSTEM_NOTIFICATION_ID
import com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY
@ -212,6 +211,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
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.putParcelable(KEY_USER_ENTITY, signatureVerification.user)
bundle.putBoolean(KEY_FROM_NOTIFICATION_START_CALL, true)
fullScreenIntent.putExtras(bundle)
@ -231,8 +231,7 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
)
val soundUri = getCallRingtoneUri(applicationContext, appPreferences)
val notificationChannelId = NotificationUtils
.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name
val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name
val uri = Uri.parse(signatureVerification.user!!.baseUrl)
val baseUrl = uri.host
@ -245,7 +244,9 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
.setShowWhen(true)
.setWhen(pushMessage.timestamp)
.setContentTitle(EmojiCompat.get().process(pushMessage.subject))
.setAutoCancel(true)
// 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)
@ -716,7 +717,10 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
removeNotification(decryptedPushMessage.timestamp.toInt())
}
isCallNotificationVisible = isCallNotificationVisible(decryptedPushMessage)
isCallNotificationVisible = NotificationUtils.isNotificationVisible(
context,
decryptedPushMessage.timestamp.toInt()
)
}
override fun onError(e: Throwable) {
@ -817,20 +821,6 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor
return PendingIntent.getActivity(context, requestCode, intent, intentFlag)
}
private fun isCallNotificationVisible(decryptedPushMessage: DecryptedPushMessage): Boolean {
var isVisible = false
val notificationManager = context!!.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val notifications = notificationManager.activeNotifications
for (notification in notifications) {
if (notification.id == decryptedPushMessage.timestamp.toInt()) {
isVisible = true
break
}
}
return isVisible
}
companion object {
val TAG = NotificationWorker::class.simpleName
private const val CHAT = "chat"

View File

@ -274,6 +274,24 @@ object NotificationUtils {
}
}
fun isNotificationVisible(
context: Context?,
notificationId: Int
): Boolean {
var isVisible = false
val notificationManager = context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notifications = notificationManager.activeNotifications
for (notification in notifications) {
if (notification.id == notificationId) {
isVisible = true
break
}
}
return isVisible
}
private fun getRingtoneUri(
context: Context,
ringtonePreferencesString: String?,

View File

@ -66,6 +66,7 @@ object BundleKeys {
const val KEY_ACCOUNT = "KEY_ACCOUNT"
const val KEY_FILE_ID = "KEY_FILE_ID"
const val KEY_NOTIFICATION_ID = "KEY_NOTIFICATION_ID"
const val KEY_NOTIFICATION_TIMESTAMP = "KEY_NOTIFICATION_TIMESTAMP"
const val KEY_SHARED_TEXT = "KEY_SHARED_TEXT"
const val KEY_GEOCODING_QUERY = "KEY_GEOCODING_QUERY"
const val KEY_META_DATA = "KEY_META_DATA"