show proper notification on incoming call

This commit is contained in:
Daniel Gultsch 2020-04-07 18:50:39 +02:00
parent 4c6ee9693a
commit ccfc55e9b6
6 changed files with 119 additions and 24 deletions

View file

@ -10,7 +10,6 @@
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
@ -33,6 +32,8 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission <uses-permission
android:name="android.permission.READ_PHONE_STATE" android:name="android.permission.READ_PHONE_STATE"

View file

@ -55,12 +55,14 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ConversationsActivity; import eu.siacs.conversations.ui.ConversationsActivity;
import eu.siacs.conversations.ui.EditAccountActivity; import eu.siacs.conversations.ui.EditAccountActivity;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.ui.TimePreference; import eu.siacs.conversations.ui.TimePreference;
import eu.siacs.conversations.utils.AccountUtils; import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility; import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
public class NotificationService { public class NotificationService {
@ -70,9 +72,10 @@ public class NotificationService {
private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations"; private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations";
private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024; private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024;
private static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER;
static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4; static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4;
private static final int NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 2;
private static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6; private static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6;
private static final int INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8;
private final XmppConnectionService mXmppConnectionService; private final XmppConnectionService mXmppConnectionService;
private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>(); private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>(); private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>();
@ -100,6 +103,14 @@ public class NotificationService {
return Pattern.compile("(?<=(^|\\s))" + Pattern.quote(nick) + "(?=\\s|$|\\p{Punct})"); return Pattern.compile("(?<=(^|\\s))" + Pattern.quote(nick) + "(?=\\s|$|\\p{Punct})");
} }
private static boolean isImageMessage(Message message) {
return message.getType() != Message.TYPE_TEXT
&& message.getTransferable() == null
&& !message.isDeleted()
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getFileParams().height > 0;
}
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
void initializeChannels() { void initializeChannels() {
final Context c = mXmppConnectionService; final Context c = mXmppConnectionService;
@ -112,6 +123,7 @@ public class NotificationService {
notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("status", c.getString(R.string.notification_group_status_information))); 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))); notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("chats", c.getString(R.string.notification_group_messages)));
notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("calls", c.getString(R.string.notification_group_calls)));
final NotificationChannel foregroundServiceChannel = new NotificationChannel("foreground", final NotificationChannel foregroundServiceChannel = new NotificationChannel("foreground",
c.getString(R.string.foreground_service_channel_name), c.getString(R.string.foreground_service_channel_name),
NotificationManager.IMPORTANCE_MIN); NotificationManager.IMPORTANCE_MIN);
@ -141,6 +153,20 @@ public class NotificationService {
exportChannel.setGroup("status"); exportChannel.setGroup("status");
notificationManager.createNotificationChannel(exportChannel); notificationManager.createNotificationChannel(exportChannel);
final NotificationChannel incomingCallsChannel = new NotificationChannel("incoming_calls",
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.setShowBadge(false);
incomingCallsChannel.setLightColor(LED_COLOR);
incomingCallsChannel.enableLights(true);
incomingCallsChannel.setGroup("calls");
notificationManager.createNotificationChannel(incomingCallsChannel);
final NotificationChannel messagesChannel = new NotificationChannel("messages", final NotificationChannel messagesChannel = new NotificationChannel("messages",
c.getString(R.string.messages_channel_name), c.getString(R.string.messages_channel_name),
NotificationManager.IMPORTANCE_HIGH); NotificationManager.IMPORTANCE_HIGH);
@ -300,6 +326,53 @@ public class NotificationService {
} }
} }
public void showIncomingCallNotification(AbstractJingleConnection.Id id) {
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 PendingIntent pendingIntent = PendingIntent.getActivity(mXmppConnectionService, 101, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
final NotificationCompat.Builder builder = new NotificationCompat.Builder(mXmppConnectionService, "incoming_calls");
builder.setSmallIcon(R.drawable.ic_call_white_24dp);
builder.setContentTitle(mXmppConnectionService.getString(R.string.rtp_state_incoming_call));
builder.setContentText(id.account.getRoster().getContact(id.with).getDisplayName());
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
builder.setPriority(NotificationCompat.PRIORITY_HIGH);
builder.setCategory(NotificationCompat.CATEGORY_CALL);
builder.setFullScreenIntent(createPendingRtpSession(id, Intent.ACTION_VIEW, 101), true);
builder.setOngoing(true);
builder.addAction(new NotificationCompat.Action.Builder(
R.drawable.ic_call_end_white_48dp,
mXmppConnectionService.getString(R.string.dismiss_call),
createDismissCall(id.sessionId, 102))
.build());
builder.addAction(new NotificationCompat.Action.Builder(
R.drawable.ic_call_white_24dp,
mXmppConnectionService.getString(R.string.answer_call),
createPendingRtpSession(id, RtpSessionActivity.ACTION_ACCEPT, 103))
.build());
final Notification notification = builder.build();
notification.flags = notification.flags | Notification.FLAG_INSISTENT;
notify(INCOMING_CALL_NOTIFICATION_ID, builder.build());
}
private PendingIntent createPendingRtpSession(final AbstractJingleConnection.Id id, final String action, final int requestCode) {
final Intent fullScreenIntent = new Intent(mXmppConnectionService, RtpSessionActivity.class);
fullScreenIntent.setAction(action);
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);
return PendingIntent.getActivity(mXmppConnectionService, requestCode, fullScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public void cancelIncomingCallNotification() {
cancel(INCOMING_CALL_NOTIFICATION_ID);
}
private void pushNow(final Message message) { private void pushNow(final Message message) {
mXmppConnectionService.updateUnreadCountBadge(); mXmppConnectionService.updateUnreadCountBadge();
if (!notify(message)) { if (!notify(message)) {
@ -726,14 +799,6 @@ public class NotificationService {
return image; return image;
} }
private static boolean isImageMessage(Message message) {
return message.getType() != Message.TYPE_TEXT
&& message.getTransferable() == null
&& !message.isDeleted()
&& message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getFileParams().height > 0;
}
private Message getFirstDownloadableMessage(final Iterable<Message> messages) { private Message getFirstDownloadableMessage(final Iterable<Message> messages) {
for (final Message message : messages) { for (final Message message : messages) {
if (message.getTransferable() != null || (message.getType() == Message.TYPE_TEXT && message.treatAsDownloadable())) { if (message.getTransferable() != null || (message.getType() == Message.TYPE_TEXT && message.treatAsDownloadable())) {
@ -834,6 +899,14 @@ public class NotificationService {
return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT); return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT);
} }
private PendingIntent createDismissCall(String sessionId, int requestCode) {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_DISMISS_CALL);
intent.setPackage(mXmppConnectionService.getPackageName());
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, sessionId);
return PendingIntent.getService(mXmppConnectionService, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent createSnoozeIntent(Conversation conversation) { private PendingIntent createSnoozeIntent(Conversation conversation) {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
intent.setAction(XmppConnectionService.ACTION_SNOOZE); intent.setAction(XmppConnectionService.ACTION_SNOOZE);

View file

@ -107,6 +107,7 @@ import eu.siacs.conversations.parser.PresenceParser;
import eu.siacs.conversations.persistance.DatabaseBackend; import eu.siacs.conversations.persistance.DatabaseBackend;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.ui.ChooseAccountForProfilePictureActivity; import eu.siacs.conversations.ui.ChooseAccountForProfilePictureActivity;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.ui.SettingsActivity; import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.UiCallback; import eu.siacs.conversations.ui.UiCallback;
import eu.siacs.conversations.ui.interfaces.OnAvatarPublication; import eu.siacs.conversations.ui.interfaces.OnAvatarPublication;
@ -165,6 +166,7 @@ public class XmppConnectionService extends Service {
public static final String ACTION_IDLE_PING = "idle_ping"; public static final String ACTION_IDLE_PING = "idle_ping";
public static final String ACTION_FCM_TOKEN_REFRESH = "fcm_token_refresh"; public static final String ACTION_FCM_TOKEN_REFRESH = "fcm_token_refresh";
public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received"; public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received";
public static final String ACTION_DISMISS_CALL = "dismiss_call";
private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE"; private static final String ACTION_POST_CONNECTIVITY_CHANGE = "eu.siacs.conversations.POST_CONNECTIVITY_CHANGE";
private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
@ -638,6 +640,10 @@ public class XmppConnectionService extends Service {
} }
}); });
break; break;
case ACTION_DISMISS_CALL:
final String sessionId = intent.getStringExtra(RtpSessionActivity.EXTRA_SESSION_ID);
Log.d(Config.LOGTAG,"received intent to dismiss call with session id "+sessionId);
break;
case ACTION_DISMISS_ERROR_NOTIFICATIONS: case ACTION_DISMISS_ERROR_NOTIFICATIONS:
dismissErrorNotifications(); dismissErrorNotifications();
break; break;

View file

@ -25,6 +25,8 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public static final String EXTRA_WITH = "with"; public static final String EXTRA_WITH = "with";
public static final String EXTRA_SESSION_ID = "session_id"; public static final String EXTRA_SESSION_ID = "session_id";
public static final String ACTION_ACCEPT = "accept";
private WeakReference<JingleRtpConnection> rtpConnectionReference; private WeakReference<JingleRtpConnection> rtpConnectionReference;
private ActivityRtpSessionBinding binding; private ActivityRtpSessionBinding binding;
@ -32,6 +34,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
;
Log.d(Config.LOGTAG, "RtpSessionActivity.onCreate()");
this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session); this.binding = DataBindingUtil.setContentView(this, R.layout.activity_rtp_session);
this.binding.rejectCall.setOnClickListener(this::rejectCall); this.binding.rejectCall.setOnClickListener(this::rejectCall);
this.binding.endCall.setOnClickListener(this::endCall); this.binding.endCall.setOnClickListener(this::endCall);
@ -78,8 +86,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.rtpConnectionReference = reference; this.rtpConnectionReference = reference;
binding.with.setText(getWith().getDisplayName()); binding.with.setText(getWith().getDisplayName());
final RtpEndUserState currentState = requireRtpConnection().getEndUserState(); final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
final String action = intent.getAction();
updateStateDisplay(currentState); updateStateDisplay(currentState);
updateButtonConfiguration(currentState); updateButtonConfiguration(currentState);
if (ACTION_ACCEPT.equals(action)) {
Log.d(Config.LOGTAG,"intent action was accept");
requireRtpConnection().acceptCall();
}
} }
} }

View file

@ -228,12 +228,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void startRinging() { private void startRinging() {
Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing"); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": received call from " + id.with + ". start ringing");
final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class); xmppConnectionService.getNotificationService().showIncomingCallNotification(id);
intent.putExtra(RtpSessionActivity.EXTRA_ACCOUNT, id.account.getJid().asBareJid().toEscapedString());
intent.putExtra(RtpSessionActivity.EXTRA_WITH, id.with.toEscapedString());
intent.putExtra(RtpSessionActivity.EXTRA_SESSION_ID, id.sessionId);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
xmppConnectionService.startActivity(intent);
} }
private void receiveProceed(final Jid from, final Element proceed) { private void receiveProceed(final Jid from, final Element proceed) {
@ -342,6 +337,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public void rejectCall() { public void rejectCall() {
Log.d(Config.LOGTAG, "todo rejecting call"); Log.d(Config.LOGTAG, "todo rejecting call");
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
} }
public void endCall() { public void endCall() {
@ -359,6 +355,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void acceptCallFromProposed() { private void acceptCallFromProposed() {
transitionOrThrow(State.PROCEED); transitionOrThrow(State.PROCEED);
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
final MessagePacket messagePacket = new MessagePacket(); final MessagePacket messagePacket = new MessagePacket();
messagePacket.setTo(id.with); messagePacket.setTo(id.with);
//Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916 //Note that Movim needs 'accept', correct is 'proceed' https://github.com/movim/movim/issues/916
@ -368,7 +365,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
} }
private void acceptCallFromSessionInitialized() { private void acceptCallFromSessionInitialized() {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
throw new IllegalStateException("accepting from this state has not been implemented yet");
} }
private synchronized boolean isInState(State... state) { private synchronized boolean isInState(State... state) {

View file

@ -748,7 +748,9 @@
<string name="error_channel_name">Connectivity Problems</string> <string name="error_channel_name">Connectivity Problems</string>
<string name="error_channel_description">This notification category is used to display a notification in case there is a problem connecting to an account.</string> <string name="error_channel_description">This notification category is used to display a notification in case there is a problem connecting to an account.</string>
<string name="notification_group_messages">Messages</string> <string name="notification_group_messages">Messages</string>
<string name="notification_group_calls">Calls</string>
<string name="messages_channel_name">Messages</string> <string name="messages_channel_name">Messages</string>
<string name="incoming_calls_channel_name">Incoming calls</string>
<string name="silent_messages_channel_name">Silent messages</string> <string name="silent_messages_channel_name">Silent messages</string>
<string name="silent_messages_channel_description">This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string> <string name="silent_messages_channel_description">This notification group is used to display notifications that should not trigger any sound. For example when being active on another device (Grace Period).</string>
<string name="pref_more_notification_settings">Notification Settings</string> <string name="pref_more_notification_settings">Notification Settings</string>
@ -890,6 +892,8 @@
<string name="rtp_state_connected">Connected</string> <string name="rtp_state_connected">Connected</string>
<string name="rtp_state_accepting_call">Accepting call</string> <string name="rtp_state_accepting_call">Accepting call</string>
<string name="rtp_state_ending_call">Ending call</string> <string name="rtp_state_ending_call">Ending call</string>
<string name="answer_call">Answer</string>
<string name="dismiss_call">Dismiss</string>
<plurals name="view_users"> <plurals name="view_users">
<item quantity="one">View %1$d Participant</item> <item quantity="one">View %1$d Participant</item>
<item quantity="other">View %1$d Participants</item> <item quantity="other">View %1$d Participants</item>