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.ACCESS_NETWORK_STATE" />
<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.VIBRATE" />
<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.RECORD_AUDIO" />
<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
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.ui.ConversationsActivity;
import eu.siacs.conversations.ui.EditAccountActivity;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.ui.TimePreference;
import eu.siacs.conversations.utils.AccountUtils;
import eu.siacs.conversations.utils.Compatibility;
import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.XmppConnection;
import eu.siacs.conversations.xmpp.jingle.AbstractJingleConnection;
public class NotificationService {
@ -70,9 +72,10 @@ public class NotificationService {
private static final String CONVERSATIONS_GROUP = "eu.siacs.conversations";
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;
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 INCOMING_CALL_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8;
private final XmppConnectionService mXmppConnectionService;
private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>();
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})");
}
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)
void initializeChannels() {
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("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",
c.getString(R.string.foreground_service_channel_name),
NotificationManager.IMPORTANCE_MIN);
@ -141,6 +153,20 @@ public class NotificationService {
exportChannel.setGroup("status");
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",
c.getString(R.string.messages_channel_name),
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) {
mXmppConnectionService.updateUnreadCountBadge();
if (!notify(message)) {
@ -467,7 +540,7 @@ public class NotificationService {
private Builder buildMultipleConversation(final boolean notify, final boolean quietHours) {
final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, quietHours ? "quiet_hours" : (notify ? "messages" : "silent_messages"));
final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle();
style.setBigContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations,notifications.size()));
style.setBigContentTitle(mXmppConnectionService.getString(R.string.x_unread_conversations, notifications.size()));
final StringBuilder names = new StringBuilder();
Conversation conversation = null;
for (final ArrayList<Message> messages : notifications.values()) {
@ -652,7 +725,7 @@ public class NotificationService {
return builder.build();
}
private void modifyForTextOnly(final Builder builder, final ArrayList<Message> messages) {
private void modifyForTextOnly(final Builder builder, final ArrayList<Message> messages) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final Conversation conversation = (Conversation) messages.get(0).getConversation();
final Person.Builder meBuilder = new Person.Builder().setName(mXmppConnectionService.getString(R.string.me));
@ -668,7 +741,7 @@ public class NotificationService {
for (Message message : messages) {
final Person sender = message.getStatus() == Message.STATUS_RECEIVED ? getPerson(message) : null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && isImageMessage(message)) {
final Uri dataUri = FileBackend.getMediaUri(mXmppConnectionService,mXmppConnectionService.getFileBackend().getFile(message));
final Uri dataUri = FileBackend.getMediaUri(mXmppConnectionService, mXmppConnectionService.getFileBackend().getFile(message));
NotificationCompat.MessagingStyle.Message imageMessage = new NotificationCompat.MessagingStyle.Message(UIHelper.getMessagePreview(mXmppConnectionService, message).first, message.getTimeSent(), sender);
if (dataUri != null) {
imageMessage.setData(message.getMimeType(), dataUri);
@ -683,7 +756,7 @@ public class NotificationService {
} else {
if (messages.get(0).getConversation().getMode() == Conversation.MODE_SINGLE) {
builder.setStyle(new NotificationCompat.BigTextStyle().bigText(getMergedBodies(messages)));
final CharSequence preview = UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size()-1)).first;
final CharSequence preview = UIHelper.getMessagePreview(mXmppConnectionService, messages.get(messages.size() - 1)).first;
builder.setContentText(preview);
builder.setTicker(preview);
builder.setNumber(messages.size());
@ -726,14 +799,6 @@ public class NotificationService {
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) {
for (final Message message : messages) {
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);
}
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) {
final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class);
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.FileBackend;
import eu.siacs.conversations.ui.ChooseAccountForProfilePictureActivity;
import eu.siacs.conversations.ui.RtpSessionActivity;
import eu.siacs.conversations.ui.SettingsActivity;
import eu.siacs.conversations.ui.UiCallback;
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_FCM_TOKEN_REFRESH = "fcm_token_refresh";
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 SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp";
@ -638,6 +640,10 @@ public class XmppConnectionService extends Service {
}
});
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:
dismissErrorNotifications();
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_SESSION_ID = "session_id";
public static final String ACTION_ACCEPT = "accept";
private WeakReference<JingleRtpConnection> rtpConnectionReference;
private ActivityRtpSessionBinding binding;
@ -32,6 +34,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override
public void onCreate(Bundle 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.rejectCall.setOnClickListener(this::rejectCall);
this.binding.endCall.setOnClickListener(this::endCall);
@ -41,7 +49,7 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
@Override
public void onStart() {
super.onStart();
Log.d(Config.LOGTAG,"RtpSessionActivity.onStart()");
Log.d(Config.LOGTAG, "RtpSessionActivity.onStart()");
}
private void endCall(View view) {
@ -78,8 +86,13 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
this.rtpConnectionReference = reference;
binding.with.setText(getWith().getDisplayName());
final RtpEndUserState currentState = requireRtpConnection().getEndUserState();
final String action = intent.getAction();
updateStateDisplay(currentState);
updateButtonConfiguration(currentState);
if (ACTION_ACCEPT.equals(action)) {
Log.d(Config.LOGTAG,"intent action was accept");
requireRtpConnection().acceptCall();
}
}
}
@ -137,12 +150,12 @@ public class RtpSessionActivity extends XmppActivity implements XmppConnectionSe
public void onJingleRtpConnectionUpdate(Account account, Jid with, RtpEndUserState state) {
final AbstractJingleConnection.Id id = requireRtpConnection().getId();
if (account == id.account && id.with.equals(with)) {
runOnUiThread(()->{
runOnUiThread(() -> {
updateStateDisplay(state);
updateButtonConfiguration(state);
});
} else {
Log.d(Config.LOGTAG,"received update for other rtp session");
Log.d(Config.LOGTAG, "received update for other rtp session");
}
}

View File

@ -228,12 +228,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");
final Intent intent = new Intent(xmppConnectionService, RtpSessionActivity.class);
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);
xmppConnectionService.getNotificationService().showIncomingCallNotification(id);
}
private void receiveProceed(final Jid from, final Element proceed) {
@ -342,6 +337,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
public void rejectCall() {
Log.d(Config.LOGTAG, "todo rejecting call");
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
}
public void endCall() {
@ -359,6 +355,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
private void acceptCallFromProposed() {
transitionOrThrow(State.PROCEED);
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
final MessagePacket messagePacket = new MessagePacket();
messagePacket.setTo(id.with);
//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() {
xmppConnectionService.getNotificationService().cancelIncomingCallNotification();
throw new IllegalStateException("accepting from this state has not been implemented yet");
}
private synchronized boolean isInState(State... state) {

View File

@ -748,7 +748,9 @@
<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="notification_group_messages">Messages</string>
<string name="notification_group_calls">Calls</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_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>
@ -890,6 +892,8 @@
<string name="rtp_state_connected">Connected</string>
<string name="rtp_state_accepting_call">Accepting 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">
<item quantity="one">View %1$d Participant</item>
<item quantity="other">View %1$d Participants</item>