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"