From 9c4b0a00c677facc75f02016e096402abf9efbe4 Mon Sep 17 00:00:00 2001 From: Marcel Hibbe Date: Mon, 14 Nov 2022 14:55:49 +0100 Subject: [PATCH] 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 --- .../activities/CallNotificationActivity.kt | 175 +++--------------- .../nextcloud/talk/jobs/NotificationWorker.kt | 30 +-- .../nextcloud/talk/utils/NotificationUtils.kt | 18 ++ .../nextcloud/talk/utils/bundle/BundleKeys.kt | 1 + 4 files changed, 51 insertions(+), 173 deletions(-) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.kt index 6a6ed114e..a6034429a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallNotificationActivity.kt @@ -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 = 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 -> - completed.zipWith(Observable.range(TIMER_START, TIMER_COUNT)) { _: Any?, i: Int? -> i!! } - .flatMap { Observable.timer(TIMER_DELAY, TimeUnit.SECONDS) } - .takeWhile { !leavingScreen } - } - .subscribe(object : Observer { - 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 } } 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 3f02ba7e9..b9df7900b 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -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" 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 7ada7b797..9e34ad058 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt @@ -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?, 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 82db195ed..78866566e 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 @@ -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"