diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index b654e0190..64658f492 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -12,10 +12,12 @@ import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Typeface; import android.media.AudioAttributes; +import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.os.SystemClock; +import android.os.Vibrator; import android.preference.PreferenceManager; import android.text.SpannableString; import android.text.style.StyleSpan; @@ -43,6 +45,10 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -70,12 +76,13 @@ import eu.siacs.conversations.xmpp.jingle.Media; public class NotificationService { + private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor(); + public static final Object CATCHUP_LOCK = new Object(); private static final int LED_COLOR = 0xff00ff00; - private static final int CALL_DAT = 120; - private static final long[] CALL_PATTERN = {0, 3 * CALL_DAT, CALL_DAT, CALL_DAT, 3 * CALL_DAT, CALL_DAT, CALL_DAT}; + private static final long[] CALL_PATTERN = {0, 500, 300, 600}; private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations"; private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024; @@ -92,6 +99,10 @@ public class NotificationService { private boolean mIsInForeground; private long mLastNotification; + private static final String INCOMING_CALLS_NOTIFICATION_CHANNEL = "incoming_calls_channel"; + private Ringtone currentlyPlayingRingtone = null; + private ScheduledFuture vibrationFuture; + NotificationService(final XmppConnectionService service) { this.mXmppConnectionService = service; } @@ -129,6 +140,7 @@ public class NotificationService { } notificationManager.deleteNotificationChannel("export"); + notificationManager.deleteNotificationChannel("incoming_calls"); notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("status", c.getString(R.string.notification_group_status_information))); notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("chats", c.getString(R.string.notification_group_messages))); @@ -162,20 +174,16 @@ public class NotificationService { exportChannel.setGroup("status"); notificationManager.createNotificationChannel(exportChannel); - final NotificationChannel incomingCallsChannel = new NotificationChannel("incoming_calls", + final NotificationChannel incomingCallsChannel = new NotificationChannel(INCOMING_CALLS_NOTIFICATION_CHANNEL, c.getString(R.string.incoming_calls_channel_name), NotificationManager.IMPORTANCE_HIGH); - incomingCallsChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE), new AudioAttributes.Builder() - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) - .build()); + incomingCallsChannel.setSound(null, null); incomingCallsChannel.setShowBadge(false); incomingCallsChannel.setLightColor(LED_COLOR); incomingCallsChannel.enableLights(true); incomingCallsChannel.setGroup("calls"); incomingCallsChannel.setBypassDnd(true); - incomingCallsChannel.enableVibration(true); - incomingCallsChannel.setVibrationPattern(CALL_PATTERN); + incomingCallsChannel.enableVibration(false); notificationManager.createNotificationChannel(incomingCallsChannel); final NotificationChannel ongoingCallsChannel = new NotificationChannel("ongoing_calls", @@ -387,14 +395,32 @@ public class NotificationService { notify(DELIVERY_FAILED_NOTIFICATION_ID, summaryNotification); } - public void showIncomingCallNotification(final AbstractJingleConnection.Id id, final Set media) { + public void startRinging(final AbstractJingleConnection.Id id, final Set media) { + showIncomingCallNotification(id, media); + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); + final Resources resources = mXmppConnectionService.getResources(); + final Uri uri = Uri.parse(preferences.getString("call_ringtone", resources.getString(R.string.incoming_call_ringtone))); + this.currentlyPlayingRingtone = RingtoneManager.getRingtone(mXmppConnectionService, uri); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + this.currentlyPlayingRingtone.setLooping(true); + } + this.currentlyPlayingRingtone.play(); + this.vibrationFuture = SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate( + new VibrationRunnable(), + 0, + 3, + TimeUnit.SECONDS + ); + } + + private void showIncomingCallNotification(final AbstractJingleConnection.Id id, final Set media) { final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class); fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString()); fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString()); fullScreenIntent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); fullScreenIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "incoming_calls"); + final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, INCOMING_CALLS_NOTIFICATION_CHANNEL); if (media.contains(Media.VIDEO)) { builder.setSmallIcon(R.drawable.ic_videocam_white_24dp); builder.setContentTitle(mXmppConnectionService.getString(R.string.rtp_state_incoming_video_call)); @@ -468,9 +494,23 @@ public class NotificationService { } public void cancelIncomingCallNotification() { + stopSoundAndVibration(); cancel(INCOMING_CALL_NOTIFICATION_ID); } + public void stopSoundAndVibration() { + if (this.currentlyPlayingRingtone != null) { + if (this.currentlyPlayingRingtone.isPlaying()) { + Log.d(Config.LOGTAG, "stop playing ring tone"); + } + this.currentlyPlayingRingtone.stop(); + } + if (this.vibrationFuture != null && !this.vibrationFuture.isCancelled()) { + Log.d(Config.LOGTAG, "cancel vibration"); + this.vibrationFuture.cancel(true); + } + } + public static void cancelIncomingCallNotification(final Context context) { final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); try { @@ -636,17 +676,7 @@ public class NotificationService { } } - private void modifyIncomingCall(Builder mBuilder) { - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); - final Resources resources = mXmppConnectionService.getResources(); - final String ringtone = preferences.getString("call_ringtone", resources.getString(R.string.incoming_call_ringtone)); - mBuilder.setVibrate(CALL_PATTERN); - final Uri uri = Uri.parse(ringtone); - try { - mBuilder.setSound(fixRingtoneUri(uri)); - } catch (SecurityException e) { - Log.d(Config.LOGTAG, "unable to use custom notification sound " + uri.toString()); - } + private void modifyIncomingCall(final Builder mBuilder) { mBuilder.setPriority(NotificationCompat.PRIORITY_HIGH); setNotificationColor(mBuilder); mBuilder.setLights(LED_COLOR, 2000, 3000); @@ -1253,4 +1283,13 @@ public class NotificationService { Log.d(Config.LOGTAG, "unable to cancel notification", e); } } + + private class VibrationRunnable implements Runnable { + + @Override + public void run() { + final Vibrator vibrator = (Vibrator) mXmppConnectionService.getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(CALL_PATTERN, -1); + } + } } diff --git a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java index 27740eb44..36562b176 100644 --- a/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/RtpSessionActivity.java @@ -14,6 +14,7 @@ import android.os.PowerManager; import android.os.SystemClock; import android.util.Log; import android.util.Rational; +import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -146,6 +147,16 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe return super.onCreateOptionsMenu(menu); } + @Override + public boolean onKeyDown(final int keyCode, final KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN){ + if (xmppConnectionService != null) { + xmppConnectionService.getNotificationService().stopSoundAndVibration(); + } + } + return super.onKeyDown(keyCode, event); + } + private boolean isHelpButtonVisible() { try { return STATES_SHOWING_HELP_BUTTON.contains(requireRtpConnection().getEndUserState()); diff --git a/src/main/java/eu/siacs/conversations/utils/Compatibility.java b/src/main/java/eu/siacs/conversations/utils/Compatibility.java index 1ea0d70ea..f181853c6 100644 --- a/src/main/java/eu/siacs/conversations/utils/Compatibility.java +++ b/src/main/java/eu/siacs/conversations/utils/Compatibility.java @@ -16,6 +16,7 @@ import androidx.annotation.BoolRes; import androidx.core.content.ContextCompat; import java.util.Arrays; +import java.util.Collections; import java.util.List; import eu.siacs.conversations.Config; @@ -31,12 +32,10 @@ public class Compatibility { "led", "notification_ringtone", "notification_headsup", - "vibrate_on_notification", - "call_ringtone" + "vibrate_on_notification" ); - private static final List UNUESD_SETTINGS_PRE_TWENTYSIX = Arrays.asList( - "message_notification_settings", - "call_notification_settings" + private static final List UNUESD_SETTINGS_PRE_TWENTYSIX = Collections.singletonList( + "message_notification_settings" ); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index a5350be1a..572c9f766 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -597,7 +597,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private void startRinging() { Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing"); ringingTimeoutFuture = jingleConnectionManager.schedule(this::ringingTimeout, BUSY_TIME_OUT, TimeUnit.SECONDS); - xmppConnectionService.getNotificationService().showIncomingCallNotification(id, getMedia()); + xmppConnectionService.getNotificationService().startRinging(id, getMedia()); } private synchronized void ringingTimeout() { diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index f2b2943b1..2fab8822a 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -113,19 +113,6 @@ android:value="messages" /> - - - - - -