From 1e6fb279c065fd351ab6ad86b2c6486cbb14d598 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 20 Nov 2018 17:10:05 +0100 Subject: [PATCH] Cleaned LinphoneService a bit, moved all notifications related code to another class --- app/src/main/AndroidManifest.xml | 2 +- .../java/org/linphone/LinphoneManager.java | 10 +- .../java/org/linphone/LinphoneService.java | 442 +----------------- .../linphone/activities/LinphoneActivity.java | 15 +- .../linphone/chat/ChatCreationFragment.java | 8 +- .../org/linphone/chat/ChatListFragment.java | 4 +- .../org/linphone/chat/DevicesFragment.java | 2 +- .../org/linphone/chat/GroupChatFragment.java | 8 +- .../org/linphone/chat/GroupInfoFragment.java | 8 +- .../java/org/linphone/chat/ImdnFragment.java | 2 +- .../compatibility/ApiTwentyFourPlus.java | 34 +- .../compatibility/ApiTwentySixPlus.java | 32 +- .../linphone/compatibility/Compatibility.java | 16 +- .../contacts/ContactDetailsFragment.java | 6 +- .../linphone/firebase/FirebaseIdService.java | 2 +- .../linphone/firebase/FirebaseMessaging.java | 4 +- .../fragments/HistoryDetailFragment.java | 6 +- .../linphone/fragments/SettingsFragment.java | 4 +- .../linphone/notifications/Notifiable.java | 86 ++++ .../notifications/NotifiableMessage.java | 44 ++ .../NotificationBroadcastReceiver.java | 77 ++- .../notifications/NotificationsManager.java | 308 ++++++++++++ 22 files changed, 595 insertions(+), 525 deletions(-) create mode 100644 app/src/main/java/org/linphone/notifications/Notifiable.java create mode 100644 app/src/main/java/org/linphone/notifications/NotifiableMessage.java rename app/src/main/java/org/linphone/{receivers => notifications}/NotificationBroadcastReceiver.java (52%) create mode 100644 app/src/main/java/org/linphone/notifications/NotificationsManager.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e4fd0163f..2f5423881 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -294,7 +294,7 @@ diff --git a/app/src/main/java/org/linphone/LinphoneManager.java b/app/src/main/java/org/linphone/LinphoneManager.java index de18518a6..e22d66d7c 100644 --- a/app/src/main/java/org/linphone/LinphoneManager.java +++ b/app/src/main/java/org/linphone/LinphoneManager.java @@ -504,7 +504,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou if (call != null) { call.enableCamera(enable); if (mServiceContext.getResources().getBoolean(R.bool.enable_call_notification)) - LinphoneService.instance().displayCallNotification(mLc.getCurrentCall()); + LinphoneService.instance().getNotificationManager().displayCallNotification(mLc.getCurrentCall()); } } @@ -1075,16 +1075,16 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou if (!mServiceContext.getResources().getBoolean(R.bool.disable_chat_message_notification) && !message.isOutgoing()) { if (cr.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { if (contact != null) { - LinphoneService.instance().displayMessageNotification(cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayMessageNotification(cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress()); } else { - LinphoneService.instance().displayMessageNotification(cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayMessageNotification(cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress()); } } else { String subject = cr.getSubject(); if (contact != null) { - LinphoneService.instance().displayGroupChatMessageNotification(subject, cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayGroupChatMessageNotification(subject, cr.getPeerAddress().asStringUriOnly(), contact.getFullName(), contact.getThumbnailUri(), textMessage, cr.getLocalAddress()); } else { - LinphoneService.instance().displayGroupChatMessageNotification(subject, cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayGroupChatMessageNotification(subject, cr.getPeerAddress().asStringUriOnly(), from.getUsername(), null, textMessage, cr.getLocalAddress()); } } } diff --git a/app/src/main/java/org/linphone/LinphoneService.java b/app/src/main/java/org/linphone/LinphoneService.java index 5634a311d..a7f71420e 100644 --- a/app/src/main/java/org/linphone/LinphoneService.java +++ b/app/src/main/java/org/linphone/LinphoneService.java @@ -23,31 +23,22 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.AlarmManager; import android.app.Application; -import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.SystemClock; import android.provider.ContactsContract; -import android.provider.MediaStore; import android.view.WindowManager; import org.linphone.activities.LinphoneActivity; -import org.linphone.compatibility.Compatibility; import org.linphone.contacts.ContactsManager; -import org.linphone.contacts.LinphoneContact; -import org.linphone.core.Address; import org.linphone.core.Call; import org.linphone.core.Call.State; import org.linphone.core.Core; @@ -58,14 +49,12 @@ import org.linphone.core.ProxyConfig; import org.linphone.core.RegistrationState; import org.linphone.mediastream.Log; import org.linphone.mediastream.Version; +import org.linphone.notifications.NotificationsManager; import org.linphone.receivers.BluetoothManager; import org.linphone.receivers.KeepAliveReceiver; import org.linphone.ui.LinphoneOverlay; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; -import java.util.HashMap; /** * Linphone service, reacting to Incoming calls, ...
@@ -81,14 +70,9 @@ public final class LinphoneService extends Service { * setLatestEventInfo and startActivity() which needs a context. */ public static final String START_LINPHONE_LOGS = " ==== Phone information dump ===="; - public static final int IC_LEVEL_ORANGE = 0; private static LinphoneService instance; - private final static int NOTIF_ID = 1; - private final static int CUSTOM_NOTIF_ID = 4; - private final static int MISSED_NOTIF_ID = 5; - public static boolean isReady() { return instance != null && instance.mTestDelayElapsed; } @@ -106,38 +90,28 @@ public final class LinphoneService extends Service { // private boolean mTestDelayElapsed; // add a timer for testing private boolean mTestDelayElapsed = true; // no timer - private NotificationManager mNM; - private Notification mNotif; - private Notification mCustomNotif; - private PendingIntent mNotifContentIntent; - private String mNotificationTitle; - private boolean mDisableRegistrationStatus; private CoreListenerStub mListener; - public static int notifcationsPriority = (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41) ? Notification.PRIORITY_MIN : 0); private WindowManager mWindowManager; private LinphoneOverlay mOverlay; private Application.ActivityLifecycleCallbacks activityCallbacks; - private class Notified { - int notificationId; - int numberOfUnreadMessage; + private NotificationsManager mNotificationManager; + + public NotificationsManager getNotificationManager() { + return mNotificationManager; } - private HashMap mChatNotifMap, mCallNotifMap; - private int mLastNotificationId; + private String incomingReceivedActivityName; + private Class incomingReceivedActivity = LinphoneActivity.class; + + public Class getIncomingReceivedActivity() { + return incomingReceivedActivity; + } public void setCurrentlyDisplayedChatRoom(String address) { if (address != null) { - resetMessageNotifCount(address); - } - } - - private void resetMessageNotifCount(String address) { - Notified notif = mChatNotifMap.get(address); - if (notif != null) { - notif.numberOfUnreadMessage = 0; - mNM.cancel(notif.notificationId); + mNotificationManager.resetMessageNotifCount(address); } } @@ -169,8 +143,6 @@ public final class LinphoneService extends Service { } } - ; - private InactivityChecker mLastChecker; @Override @@ -275,31 +247,6 @@ public final class LinphoneService extends Service { getApplication().registerActivityLifecycleCallbacks(activityCallbacks = new ActivityMonitor()); } - public boolean displayServiceNotification() { - return LinphonePreferences.instance().getServiceNotificationVisibility(); - } - - public void showServiceNotification() { - startForegroundCompat(NOTIF_ID, mNotif); - - Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc == null) return; - ProxyConfig lpc = lc.getDefaultProxyConfig(); - if (lpc != null) { - if (lpc.getState() == RegistrationState.Ok) { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); - } else { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_register_failure); - } - } else { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); - } - } - - public void hideServiceNotification() { - stopForegroundCompat(NOTIF_ID); - } - @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); @@ -309,9 +256,10 @@ public final class LinphoneService extends Service { return START_REDELIVER_INTENT; } - LinphoneManager.createAndStart(LinphoneService.this); + LinphoneManager.createAndStart(this); instance = this; // instance is ready once linphone manager has been created + mNotificationManager = new NotificationsManager(this); LinphoneManager.getLc().addListener(mListener = new CoreListenerStub() { @Override public void onCallStateChanged(Core lc, Call call, Call.State state, String message) { @@ -321,7 +269,7 @@ public final class LinphoneService extends Service { } if (getResources().getBoolean(R.bool.enable_call_notification)) { - displayCallNotification(call); + mNotificationManager.displayCallNotification(call); } if (state == Call.State.IncomingReceived) { @@ -339,58 +287,23 @@ public final class LinphoneService extends Service { } if (state == State.End && call.getCallLog().getStatus() == Call.Status.Missed) { - int missedCallCount = LinphoneManager.getLcIfManagerNotDestroyedOrNull().getMissedCallsCount(); - String body; - if (missedCallCount > 1) { - body = getString(R.string.missed_calls_notif_body).replace("%i", String.valueOf(missedCallCount)); - } else { - Address address = call.getRemoteAddress(); - LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(address); - if (c != null) { - body = c.getFullName(); - } else { - body = address.getDisplayName(); - if (body == null) { - body = address.asStringUriOnly(); - } - } - } - - Intent missedCallNotifIntent = new Intent(LinphoneService.this, incomingReceivedActivity); - missedCallNotifIntent.putExtra("GoToHistory", true); - PendingIntent intent = PendingIntent.getActivity(LinphoneService.this, 0, missedCallNotifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - Notification notif = Compatibility.createMissedCallNotification(instance, getString(R.string.missed_calls_notif_title), body, intent); - notifyWrapper(MISSED_NOTIF_ID, notif); + mNotificationManager.displayMissedCallNotification(call); } } @Override public void onGlobalStateChanged(Core lc, GlobalState state, String message) { - if (!mDisableRegistrationStatus && state == GlobalState.On && displayServiceNotification()) { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); - } + //TODO global state if ON } @Override public void onRegistrationStateChanged(Core lc, ProxyConfig cfg, RegistrationState state, String smessage) { - if (!mDisableRegistrationStatus) { - if (displayServiceNotification() && state == RegistrationState.Ok && LinphoneManager.getLc().getDefaultProxyConfig() != null && LinphoneManager.getLc().getDefaultProxyConfig().getState() == RegistrationState.Ok) { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_registered); - } - - if (displayServiceNotification() && (state == RegistrationState.Failed || state == RegistrationState.Cleared) && (LinphoneManager.getLc().getDefaultProxyConfig() == null || !(LinphoneManager.getLc().getDefaultProxyConfig().getState() == RegistrationState.Ok))) { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_register_failure); - } - - if (displayServiceNotification() && state == RegistrationState.None) { - sendNotification(IC_LEVEL_ORANGE, R.string.notification_started); - } - } + //TODO registration status } }); - if (displayServiceNotification() || (Version.sdkAboveOrEqual(Version.API26_O_80) && intent.getBooleanExtra("ForceStartForeground", false))) { - startForegroundCompat(NOTIF_ID, mNotif); + if (Version.sdkAboveOrEqual(Version.API26_O_80) && intent.getBooleanExtra("ForceStartForeground", false)) { + mNotificationManager.startForeground(); } if (!Version.sdkAboveOrEqual(Version.API26_O_80) @@ -426,13 +339,8 @@ public final class LinphoneService extends Service { @Override public void onCreate() { super.onCreate(); - mLastNotificationId = 8; // To not interfere with other notifs ids - mChatNotifMap = new HashMap<>(); - mCallNotifMap = new HashMap<>(); setupActivityMonitor(); - // In case restart after a crash. Main in LinphoneActivity - mNotificationTitle = getString(R.string.service_name); // Needed in order for the two next calls to succeed, libraries must have been loaded first LinphonePreferences.instance().setContext(getBaseContext()); @@ -445,27 +353,6 @@ public final class LinphoneService extends Service { dumpDeviceInformation(); dumpInstalledLinphoneInformation(); - //Disable service notification for Android O - if ((Version.sdkAboveOrEqual(Version.API26_O_80))) { - LinphonePreferences.instance().setServiceNotificationVisibility(false); - mDisableRegistrationStatus = true; - } - - mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - mNM.cancelAll(); - Compatibility.createNotificationChannels(this); - - Intent notifIntent = new Intent(this, incomingReceivedActivity); - notifIntent.putExtra("Notification", true); - mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - Bitmap bm = null; - try { - bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); - } catch (Exception e) { - } - mNotif = Compatibility.createNotification(this, mNotificationTitle, "", R.drawable.linphone_notification_icon, R.mipmap.ic_launcher, bm, mNotifContentIntent, true, notifcationsPriority); - incomingReceivedActivityName = LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived(); try { incomingReceivedActivity = (Class) Class.forName(incomingReceivedActivityName); @@ -473,13 +360,6 @@ public final class LinphoneService extends Service { Log.e(e); } - try { - mStartForeground = getClass().getMethod("startForeground", mStartFgSign); - mStopForeground = getClass().getMethod("stopForeground", mStopFgSign); - } catch (NoSuchMethodException e) { - Log.e(e, "Couldn't find startForeground or stopForeground"); - } - mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); } @@ -504,249 +384,6 @@ public final class LinphoneService extends Service { mOverlay = null; } - public void displayCallNotification(Call call) { - if (call == null) return; - - Address address = call.getRemoteAddress(); - String addressAsString = address.asStringUriOnly(); - Notified notif = mCallNotifMap.get(addressAsString); - if (notif == null) { - notif = new Notified(); - notif.notificationId = mLastNotificationId; - mLastNotificationId += 1; - mCallNotifMap.put(addressAsString, notif); - } - - if (!displayServiceNotification()) { - if (call.getCore().getCallsNb() == 0) { - hideServiceNotification(); - } else { - showServiceNotification(); - } - } - - int notificationTextId; - int iconId; - switch (call.getState()) { - case Released: - case End: - mNM.cancel(notif.notificationId); - mCallNotifMap.remove(addressAsString); - return; - case Paused: - case PausedByRemote: - case Pausing: - iconId = R.drawable.topbar_call_notification; - notificationTextId = R.string.incall_notif_paused; - break; - default: - if (call.getCurrentParams().videoEnabled()) { - iconId = R.drawable.topbar_videocall_notification; - notificationTextId = R.string.incall_notif_video; - } else { - iconId = R.drawable.topbar_call_notification; - notificationTextId = R.string.incall_notif_active; - } - break; - } - - LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address); - Uri pictureUri = contact != null ? contact.getPhotoUri() : null; - Bitmap bm; - try { - bm = MediaStore.Images.Media.getBitmap(getContentResolver(), pictureUri); - } catch (Exception e) { - bm = BitmapFactory.decodeResource(getResources(), R.drawable.avatar); - } - String name = LinphoneUtils.getAddressDisplayName(address); - - boolean showAnswerAction = call.getState() == State.IncomingReceived || call.getState() == State.IncomingEarlyMedia; - Intent notifIntent = new Intent(this, incomingReceivedActivity); - notifIntent.putExtra("Notification", true); - mNotifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - Notification notification = Compatibility.createInCallNotification(getApplicationContext(), notif.notificationId, showAnswerAction, mNotificationTitle, getString(notificationTextId), iconId, bm, name, mNotifContentIntent); - - notifyWrapper(notif.notificationId, notification); - } - - public String getSipUriForCallNotificationId(int notificationId) { - for (String addr : mCallNotifMap.keySet()) { - if (mCallNotifMap.get(addr).notificationId == notificationId) { - return addr; - } - } - return null; - } - - @Deprecated - public void addNotification(Intent onClickIntent, int iconResourceID, String title, String message) { - addCustomNotification(onClickIntent, iconResourceID, title, message, true); - } - - public void addCustomNotification(Intent onClickIntent, int iconResourceID, String title, String message, boolean isOngoingEvent) { - PendingIntent notifContentIntent = PendingIntent.getActivity(this, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - Bitmap bm = null; - try { - bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); - } catch (Exception e) { - } - mCustomNotif = Compatibility.createNotification(this, title, message, iconResourceID, 0, bm, notifContentIntent, isOngoingEvent, notifcationsPriority); - - mCustomNotif.defaults |= Notification.DEFAULT_VIBRATE; - mCustomNotif.defaults |= Notification.DEFAULT_SOUND; - mCustomNotif.defaults |= Notification.DEFAULT_LIGHTS; - - notifyWrapper(CUSTOM_NOTIF_ID, mCustomNotif); - } - - public void displayGroupChatMessageNotification(String subject, String conferenceAddress, String fromName, Uri fromPictureUri, String message, String localIdentity) { - Intent notifIntent = new Intent(this, LinphoneActivity.class); - notifIntent.putExtra("GoToChat", true); - notifIntent.putExtra("ChatContactSipUri", conferenceAddress); - notifIntent.putExtra("LocalIdentity", localIdentity); - - PendingIntent notifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - Notified notif = mChatNotifMap.get(conferenceAddress); - if (notif != null) { - notif.numberOfUnreadMessage += 1; - } else { - notif = new Notified(); - notif.numberOfUnreadMessage = 1; - notif.notificationId = mLastNotificationId; - mLastNotificationId += 1; - mChatNotifMap.put(conferenceAddress, notif); - } - - Bitmap bm; - if (fromPictureUri != null) { - try { - bm = MediaStore.Images.Media.getBitmap(getContentResolver(), fromPictureUri); - } catch (Exception e) { - bm = BitmapFactory.decodeResource(getResources(), R.drawable.chat_group_avatar); - } - } else { - bm = BitmapFactory.decodeResource(getResources(), R.drawable.topbar_avatar); - } - Notification notification = Compatibility.createMessageNotification(getApplicationContext(), notif.notificationId, notif.numberOfUnreadMessage, subject, - getString(R.string.group_chat_notif).replace("%1", fromName).replace("%2", message), bm, notifContentIntent); - - notifyWrapper(notif.notificationId, notification); - } - - public void displayMessageNotification(String fromSipUri, String fromName, Uri fromPictureUri, String message, String localIdentity) { - Intent notifIntent = new Intent(this, LinphoneActivity.class); - notifIntent.putExtra("GoToChat", true); - notifIntent.putExtra("ChatContactSipUri", fromSipUri); - notifIntent.putExtra("LocalIdentity", localIdentity); - PendingIntent notifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - - if (fromName == null) { - fromName = fromSipUri; - } - - Notified notif = mChatNotifMap.get(fromSipUri); - if (notif != null) { - notif.numberOfUnreadMessage += 1; - } else { - notif = new Notified(); - notif.numberOfUnreadMessage = 1; - notif.notificationId = mLastNotificationId; - mLastNotificationId += 1; - mChatNotifMap.put(fromSipUri, notif); - } - - Bitmap bm; - if (fromPictureUri != null) { - try { - bm = MediaStore.Images.Media.getBitmap(getContentResolver(), fromPictureUri); - } catch (Exception e) { - bm = BitmapFactory.decodeResource(getResources(), R.drawable.topbar_avatar); - } - } else { - bm = BitmapFactory.decodeResource(getResources(), R.drawable.topbar_avatar); - } - Notification notification = Compatibility.createMessageNotification(getApplicationContext(), notif.notificationId, notif.numberOfUnreadMessage, fromName, message, bm, notifContentIntent); - - notifyWrapper(notif.notificationId, notification); - } - - public void sendNotification(Notification notif, int notificationId) { - notifyWrapper(notificationId, notif); - } - - public String getSipUriForNotificationId(int notificationId) { - for (String addr : mChatNotifMap.keySet()) { - if (mChatNotifMap.get(addr).notificationId == notificationId) { - return addr; - } - } - return null; - } - - public void displayInappNotification(String message) { - Intent notifIntent = new Intent(this, LinphoneActivity.class); - notifIntent.putExtra("GoToInapp", true); - - PendingIntent notifContentIntent = PendingIntent.getActivity(this, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); - mNotif = Compatibility.createSimpleNotification(getApplicationContext(), getString(R.string.inapp_notification_title), message, notifContentIntent); - - notifyWrapper(NOTIF_ID, mNotif); - } - - private static final Class[] mSetFgSign = new Class[]{boolean.class}; - private static final Class[] mStartFgSign = new Class[]{ - int.class, Notification.class}; - private static final Class[] mStopFgSign = new Class[]{boolean.class}; - - private Method mStartForeground; - private Method mStopForeground; - private Object[] mSetForegroundArgs = new Object[1]; - private Object[] mStartForegroundArgs = new Object[2]; - private Object[] mStopForegroundArgs = new Object[1]; - private String incomingReceivedActivityName; - private Class incomingReceivedActivity = LinphoneActivity.class; - - void invokeMethod(Method method, Object[] args) { - try { - method.invoke(this, args); - } catch (InvocationTargetException e) { - // Should not happen. - Log.w(e, "Unable to invoke method"); - } catch (IllegalAccessException e) { - // Should not happen. - Log.w(e, "Unable to invoke method"); - } - } - - /** - * This is a wrapper around the new startForeground method, using the older - * APIs if it is not available. - */ - void startForegroundCompat(int id, Notification notification) { - // If we have the new startForeground API, then use it. - if (mStartForeground != null) { - mStartForegroundArgs[0] = Integer.valueOf(id); - mStartForegroundArgs[1] = notification; - invokeMethod(mStartForeground, mStartForegroundArgs); - return; - } - } - - /** - * This is a wrapper around the new stopForeground method, using the older - * APIs if it is not available. - */ - void stopForegroundCompat(int id) { - // If we have the new stopForeground API, then use it. - if (mStopForeground != null) { - mStopForegroundArgs[0] = Boolean.TRUE; - invokeMethod(mStopForeground, mStopForegroundArgs); - return; - } - } - private void dumpDeviceInformation() { StringBuilder sb = new StringBuilder(); sb.append("DEVICE=").append(Build.DEVICE).append("\n"); @@ -775,38 +412,6 @@ public final class LinphoneService extends Service { } } - private synchronized void sendNotification(int level, int textId) { - String text = getString(textId); - if (text.contains("%s") && LinphoneManager.getLc() != null) { - // Test for null lc is to avoid a NPE when Android mess up badly with the String resources. - ProxyConfig lpc = LinphoneManager.getLc().getDefaultProxyConfig(); - String id = lpc != null ? lpc.getIdentityAddress().asString() : ""; - text = String.format(text, id); - } - - Bitmap bm = null; - try { - bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); - } catch (Exception e) { - } - mNotif = Compatibility.createNotification(this, mNotificationTitle, text, R.drawable.status_level, 0, bm, mNotifContentIntent, true, notifcationsPriority); - notifyWrapper(NOTIF_ID, mNotif); - } - - /** - * Wrap notifier to avoid setting the linphone icons while the service - * is stopping. When the (rare) bug is triggered, the linphone icon is - * present despite the service is not running. To trigger it one could - * stop linphone as soon as it is started. Transport configured with TLS. - */ - private synchronized void notifyWrapper(int id, Notification notification) { - if (instance != null && notification != null) { - mNM.notify(id, notification); - } else { - Log.i("Service not ready, discarding notification"); - } - } - @Override public IBinder onBind(Intent intent) { return null; @@ -849,8 +454,7 @@ public final class LinphoneService extends Service { LinphoneManager.destroy(); // Make sure our notification is gone. - stopForegroundCompat(NOTIF_ID); - mNM.cancelAll(); + mNotificationManager.destroy(); // This will prevent the app from crashing if the service gets killed in background mode if (LinphoneActivity.isInstanciated()) { @@ -875,8 +479,8 @@ public final class LinphoneService extends Service { protected void onIncomingReceived() { //wakeup linphone startActivity(new Intent() - .setClass(this, incomingReceivedActivity) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); + .setClass(this, incomingReceivedActivity) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } } diff --git a/app/src/main/java/org/linphone/activities/LinphoneActivity.java b/app/src/main/java/org/linphone/activities/LinphoneActivity.java index 2aa9da1ef..412b4ffd1 100644 --- a/app/src/main/java/org/linphone/activities/LinphoneActivity.java +++ b/app/src/main/java/org/linphone/activities/LinphoneActivity.java @@ -698,10 +698,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick changeCurrentFragment(FragmentsAvailable.CREATE_CHAT, extras); } - public void goToChat(String sipUri, Bundle shareInfos, String localIdentity) { + public void goToChat(String sipUri, Bundle shareInfos) { Bundle extras = new Bundle(); extras.putString("SipUri", sipUri); - extras.putString("LocalIdentity", localIdentity); if (shareInfos != null) { if (shareInfos.getString("fileSharedUri") != null) @@ -1382,9 +1381,6 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); if (lc != null) { lc.addListener(mListener); - if (!LinphoneService.instance().displayServiceNotification()) { - lc.refreshRegisters(); - } } if (isTablet()) { @@ -1483,12 +1479,11 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick Bundle extras = intent.getExtras(); if (extras != null && extras.getBoolean("GoToChat", false)) { String sipUri = extras.getString("ChatContactSipUri"); - String localIdentity = extras.getString("LocalIdentity"); intent.putExtra("DoNotGoToCallActivity", true); if (sipUri == null) { goToChatList(); } else { - goToChat(sipUri, extras, localIdentity); + goToChat(sipUri, extras); } } else if (extras != null && extras.getBoolean("GoToHistory", false)) { intent.putExtra("DoNotGoToCallActivity", true); @@ -1496,7 +1491,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick } else if (extras != null && extras.getBoolean("GoToInapp", false)) { intent.putExtra("DoNotGoToCallActivity", true); displayInapp(); - } else if (extras != null && extras.getBoolean("Notification", false)) { + } else if (extras != null && extras.getBoolean("Notifiable", false)) { if (LinphoneManager.getLc().getCallsNb() > 0) { startIncallActivity(); } @@ -1840,9 +1835,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick LinphonePreferences.instance().setInappPopupTime(String.valueOf(newDate)); } if (isTrialAccount) { - LinphoneService.instance().displayInappNotification(String.format(getString(R.string.inapp_notification_trial_expire), date)); + LinphoneService.instance().getNotificationManager().displayInappNotification(String.format(getString(R.string.inapp_notification_trial_expire), date)); } else { - LinphoneService.instance().displayInappNotification(String.format(getString(R.string.inapp_notification_account_expire), date)); + LinphoneService.instance().getNotificationManager().displayInappNotification(String.format(getString(R.string.inapp_notification_account_expire), date)); } } diff --git a/app/src/main/java/org/linphone/chat/ChatCreationFragment.java b/app/src/main/java/org/linphone/chat/ChatCreationFragment.java index c009c73e3..6afce5a1c 100644 --- a/app/src/main/java/org/linphone/chat/ChatCreationFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatCreationFragment.java @@ -213,7 +213,7 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { if (newState == ChatRoom.State.Created) { mWaitLayout.setVisibility(View.GONE); - LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), mShareInfos, cr.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), mShareInfos); } else if (newState == ChatRoom.State.CreationFailed) { mWaitLayout.setVisibility(View.GONE); LinphoneActivity.instance().displayChatRoomError(); @@ -468,7 +468,7 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen if (createEncryptedChatRoom && lpc != null && lpc.getConferenceFactoryUri() != null) { mChatRoom = lc.findOneToOneChatRoom(lpc.getIdentityAddress(), ca.getAddress(), true); if (mChatRoom != null) { - LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos); } else { mChatRoom = lc.createClientGroupChatRoom(getString(R.string.dummy_group_chat_subject), !createEncryptedChatRoom, createEncryptedChatRoom); mChatRoom.addListener(mChatRoomCreationListener); @@ -487,11 +487,11 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen participants[0] = ca.getAddress(); mChatRoom.addParticipants(participants); } else { - LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos); } } else { ChatRoom chatRoom = lc.getChatRoom(ca.getAddress()); - LinphoneActivity.instance().goToChat(chatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, chatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(chatRoom.getPeerAddress().asStringUriOnly(), mShareInfos); } } } else { diff --git a/app/src/main/java/org/linphone/chat/ChatListFragment.java b/app/src/main/java/org/linphone/chat/ChatListFragment.java index e727c158a..c748ba155 100644 --- a/app/src/main/java/org/linphone/chat/ChatListFragment.java +++ b/app/src/main/java/org/linphone/chat/ChatListFragment.java @@ -161,7 +161,7 @@ public class ChatListFragment extends Fragment implements ContactsUpdatedListene mChatRoomsAdapter.toggleSelection(position); } else { ChatRoom room = (ChatRoom) mChatRoomsAdapter.getItem(position); - LinphoneActivity.instance().goToChat(room.getPeerAddress().asString(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asString(), null); } } @@ -183,7 +183,7 @@ public class ChatListFragment extends Fragment implements ContactsUpdatedListene ChatRoomsAdapter adapter = (ChatRoomsAdapter) mChatRoomsList.getAdapter(); if (adapter != null && adapter.getItemCount() > 0) { ChatRoom room = (ChatRoom) adapter.getItem(0); - LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null); } else { LinphoneActivity.instance().displayEmptyFragment(); } diff --git a/app/src/main/java/org/linphone/chat/DevicesFragment.java b/app/src/main/java/org/linphone/chat/DevicesFragment.java index ebf378d60..7fb66d548 100644 --- a/app/src/main/java/org/linphone/chat/DevicesFragment.java +++ b/app/src/main/java/org/linphone/chat/DevicesFragment.java @@ -106,7 +106,7 @@ public class DevicesFragment extends Fragment { @Override public void onClick(View view) { if (LinphoneActivity.instance().isTablet()) { - LinphoneActivity.instance().goToChat(mRoomUri, null, mRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mRoomUri, null); } else { LinphoneActivity.instance().onBackPressed(); } diff --git a/app/src/main/java/org/linphone/chat/GroupChatFragment.java b/app/src/main/java/org/linphone/chat/GroupChatFragment.java index 6be74f3fe..b007f6b07 100644 --- a/app/src/main/java/org/linphone/chat/GroupChatFragment.java +++ b/app/src/main/java/org/linphone/chat/GroupChatFragment.java @@ -951,11 +951,11 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con if (LinphoneActivity.instance().isOnBackground()) { if (!getResources().getBoolean(R.bool.disable_chat_message_notification)) { if (contact != null) { - LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), - contact.getFullName(), contact.getThumbnailUri(), getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayMessageNotification(from.asStringUriOnly(), + contact.getFullName(), contact.getThumbnailUri(), getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress()); } else { - LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), - from.getUsername(), null, getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress().asString()); + LinphoneService.instance().getNotificationManager().displayMessageNotification(from.asStringUriOnly(), + from.getUsername(), null, getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress()); } } } else if (LinphoneManager.getLc().limeEnabled() == LimeState.Mandatory) { diff --git a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java index 3c677c61d..f4a2c564c 100644 --- a/app/src/main/java/org/linphone/chat/GroupInfoFragment.java +++ b/app/src/main/java/org/linphone/chat/GroupInfoFragment.java @@ -159,7 +159,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener { public void onClick(View view) { if (mIsAlreadyCreatedGroup) { if (LinphoneActivity.instance().isTablet()) { - LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asStringUriOnly(), mShareInfos); } else { getFragmentManager().popBackStack(); } @@ -185,7 +185,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener { public void onClick(View view) { if (mChatRoom != null) { mChatRoom.leave(); - LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null, mChatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null); } else { Log.e("Can't leave, chatRoom for address " + mGroupChatRoomAddress.asString() + " is null..."); } @@ -251,7 +251,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener { // This will remove both the creation fragment and the group info fragment from the back stack getFragmentManager().popBackStack(); getFragmentManager().popBackStack(); - LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), mShareInfos, cr.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), mShareInfos); } else if (newState == ChatRoom.State.CreationFailed) { mWaitLayout.setVisibility(View.GONE); LinphoneActivity.instance().displayChatRoomError(); @@ -322,7 +322,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener { toAdd.toArray(participantsToAdd); mChatRoom.addParticipants(participantsToAdd); - LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null, mChatRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null); } } }); diff --git a/app/src/main/java/org/linphone/chat/ImdnFragment.java b/app/src/main/java/org/linphone/chat/ImdnFragment.java index 2221cc487..8e0a7a984 100644 --- a/app/src/main/java/org/linphone/chat/ImdnFragment.java +++ b/app/src/main/java/org/linphone/chat/ImdnFragment.java @@ -85,7 +85,7 @@ public class ImdnFragment extends Fragment { @Override public void onClick(View view) { if (LinphoneActivity.instance().isTablet()) { - LinphoneActivity.instance().goToChat(mRoomUri, null, mRoom.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(mRoomUri, null); } else { LinphoneActivity.instance().onBackPressed(); } diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java index 2d98b393b..6215851dd 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentyFourPlus.java @@ -2,23 +2,22 @@ package org.linphone.compatibility; import android.annotation.TargetApi; -import android.app.FragmentTransaction; import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; import android.app.PendingIntent; import android.app.RemoteInput; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.view.ViewTreeObserver; import org.linphone.R; -import org.linphone.receivers.NotificationBroadcastReceiver; +import org.linphone.notifications.Notifiable; +import org.linphone.notifications.NotifiableMessage; +import org.linphone.notifications.NotificationBroadcastReceiver; import static org.linphone.compatibility.Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_CALL_ID; import static org.linphone.compatibility.Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION; +import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; @@ -52,23 +51,17 @@ public class ApiTwentyFourPlus { .build(); } - public static Notification createMessageNotification(Context context, int notificationId, int msgCount, String msgSender, String msg, Bitmap contactIcon, PendingIntent intent) { - String title; - if (msgCount == 1) { - title = msgSender; - } else { - title = context.getString(R.string.unread_messages).replace("%i", String.valueOf(msgCount)); - } - + public static Notification createMessageNotification(Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) { String replyLabel = context.getResources().getString(R.string.notification_reply_label); RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build(); Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class); replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); - replyIntent.putExtra(INTENT_NOTIF_ID, notificationId); + replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); + replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context, - notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); + notif.getNotificationId(), replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); Notification.Action action = new Notification.Action.Builder(R.drawable.chat_send_over, context.getString(R.string.notification_reply_label), replyPendingIntent) @@ -76,9 +69,13 @@ public class ApiTwentyFourPlus { .setAllowGeneratedReplies(true) .build(); + Notification.MessagingStyle style = new Notification.MessagingStyle(notif.getMyself()); + for (NotifiableMessage message : notif.getMessages()) { + style.addMessage(message.getMessage(), message.getTime(), message.getSender()); + } + style.setConversationTitle(notif.getGroupTitle()); + return new Notification.Builder(context) - .setContentTitle(title) - .setContentText(msg) .setSmallIcon(R.drawable.topbar_chat_notification) .setAutoCancel(true) .setContentIntent(intent) @@ -87,10 +84,11 @@ public class ApiTwentyFourPlus { .setCategory(Notification.CATEGORY_MESSAGE) .setVisibility(Notification.VISIBILITY_PRIVATE) .setPriority(Notification.PRIORITY_HIGH) - .setNumber(msgCount) + .setNumber(notif.getMessages().size()) .setWhen(System.currentTimeMillis()) .setShowWhen(true) .setColor(context.getColor(R.color.notification_color_led)) + .setStyle(style) .addAction(action) .build(); } diff --git a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java index 4dd09122f..47655c317 100644 --- a/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java +++ b/app/src/main/java/org/linphone/compatibility/ApiTwentySixPlus.java @@ -11,15 +11,16 @@ import android.app.RemoteInput; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.view.ViewTreeObserver; import org.linphone.R; -import org.linphone.mediastream.Log; -import org.linphone.receivers.NotificationBroadcastReceiver; +import org.linphone.notifications.Notifiable; +import org.linphone.notifications.NotifiableMessage; +import org.linphone.notifications.NotificationBroadcastReceiver; import static org.linphone.compatibility.Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_CALL_ID; import static org.linphone.compatibility.Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION; +import static org.linphone.compatibility.Compatibility.INTENT_LOCAL_IDENTITY; import static org.linphone.compatibility.Compatibility.INTENT_NOTIF_ID; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; @@ -83,23 +84,17 @@ public class ApiTwentySixPlus { notificationManager.createNotificationChannel(channel); } - public static Notification createMessageNotification(Context context, int notificationId, int msgCount, String msgSender, String msg, Bitmap contactIcon, PendingIntent intent) { - String title; - if (msgCount == 1) { - title = msgSender; - } else { - title = context.getString(R.string.unread_messages).replace("%i", String.valueOf(msgCount)); - } - + public static Notification createMessageNotification(Context context, Notifiable notif, Bitmap contactIcon, PendingIntent intent) { String replyLabel = context.getResources().getString(R.string.notification_reply_label); RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build(); Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class); replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); - replyIntent.putExtra(INTENT_NOTIF_ID, notificationId); + replyIntent.putExtra(INTENT_NOTIF_ID, notif.getNotificationId()); + replyIntent.putExtra(INTENT_LOCAL_IDENTITY, notif.getLocalIdentity()); PendingIntent replyPendingIntent = PendingIntent.getBroadcast(context, - notificationId, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); + notif.getNotificationId(), replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); Notification.Action action = new Notification.Action.Builder(R.drawable.chat_send_over, context.getString(R.string.notification_reply_label), replyPendingIntent) @@ -107,9 +102,13 @@ public class ApiTwentySixPlus { .setAllowGeneratedReplies(true) .build(); + Notification.MessagingStyle style = new Notification.MessagingStyle(notif.getMyself()); + for (NotifiableMessage message : notif.getMessages()) { + style.addMessage(message.getMessage(), message.getTime(), message.getSender()); + } + style.setConversationTitle(notif.getGroupTitle()); + return new Notification.Builder(context, context.getString(R.string.notification_channel_id)) - .setContentTitle(title) - .setContentText(msg) .setSmallIcon(R.drawable.topbar_chat_notification) .setAutoCancel(true) .setContentIntent(intent) @@ -118,10 +117,11 @@ public class ApiTwentySixPlus { .setCategory(Notification.CATEGORY_MESSAGE) .setVisibility(Notification.VISIBILITY_PRIVATE) .setPriority(Notification.PRIORITY_HIGH) - .setNumber(msgCount) + .setNumber(notif.getMessages().size()) .setWhen(System.currentTimeMillis()) .setShowWhen(true) .setColor(context.getColor(R.color.notification_color_led)) + .setStyle(style) .addAction(action) .build(); } diff --git a/app/src/main/java/org/linphone/compatibility/Compatibility.java b/app/src/main/java/org/linphone/compatibility/Compatibility.java index 7ac5c6d41..5c14ba86a 100644 --- a/app/src/main/java/org/linphone/compatibility/Compatibility.java +++ b/app/src/main/java/org/linphone/compatibility/Compatibility.java @@ -18,7 +18,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import android.app.AlarmManager; import android.app.FragmentTransaction; import android.app.Notification; import android.app.PendingIntent; @@ -26,15 +25,11 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.Build; -import android.os.PowerManager; import android.provider.Settings; -import android.text.Html; -import android.text.Spanned; -import android.view.ViewTreeObserver; -import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.widget.TextView; import org.linphone.mediastream.Version; +import org.linphone.notifications.Notifiable; public class Compatibility { public static final String KEY_TEXT_REPLY = "key_text_reply"; @@ -43,6 +38,7 @@ public class Compatibility { public static final String INTENT_REPLY_NOTIF_ACTION = "org.linphone.REPLY_ACTION"; public static final String INTENT_HANGUP_CALL_NOTIF_ACTION = "org.linphone.HANGUP_CALL_ACTION"; public static final String INTENT_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION"; + public static final String INTENT_LOCAL_IDENTITY = "LOCAL_IDENTITY"; public static void createNotificationChannels(Context context) { if (Version.sdkAboveOrEqual(Version.API26_O_80)) { @@ -65,13 +61,13 @@ public class Compatibility { return ApiTwentyOnePlus.createMissedCallNotification(context, title, text, intent); } - public static Notification createMessageNotification(Context context, int notificationId, int msgCount, String msgSender, String msg, Bitmap contactIcon, PendingIntent intent) { + public static Notification createMessageNotification(Context context, Notifiable notif, String msgSender, String msg, Bitmap contactIcon, PendingIntent intent) { if (Version.sdkAboveOrEqual(Version.API26_O_80)) { - return ApiTwentySixPlus.createMessageNotification(context, notificationId, msgCount, msgSender, msg, contactIcon, intent); + return ApiTwentySixPlus.createMessageNotification(context, notif, contactIcon, intent); } else if (Version.sdkAboveOrEqual(Version.API24_NOUGAT_70)) { - return ApiTwentyFourPlus.createMessageNotification(context, notificationId, msgCount, msgSender, msg, contactIcon, intent); + return ApiTwentyFourPlus.createMessageNotification(context, notif, contactIcon, intent); } - return ApiTwentyOnePlus.createMessageNotification(context, msgCount, msgSender, msg, contactIcon, intent); + return ApiTwentyOnePlus.createMessageNotification(context, notif.getMessages().size(), msgSender, msg, contactIcon, intent); } public static Notification createRepliedNotification(Context context, String reply) { diff --git a/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java b/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java index 54bbbc1bd..732532aa0 100644 --- a/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java +++ b/app/src/main/java/org/linphone/contacts/ContactDetailsFragment.java @@ -85,7 +85,7 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener if (defaultProxyConfig != null) { ChatRoom room = lc.findOneToOneChatRoom(defaultProxyConfig.getContact(), participant, isSecured); if (room != null) { - LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null); } else { if (defaultProxyConfig.getConferenceFactoryUri() != null && (isSecured || !LinphonePreferences.instance().useBasicChatRoomFor1To1())) { mWaitLayout.setVisibility(View.VISIBLE); @@ -96,7 +96,7 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener mChatRoom.addParticipants(participants); } else { room = lc.getChatRoom(participant); - LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null); } } } @@ -144,7 +144,7 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { if (newState == ChatRoom.State.Created) { mWaitLayout.setVisibility(View.GONE); - LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), null, cr.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), null); } else if (newState == ChatRoom.State.CreationFailed) { mWaitLayout.setVisibility(View.GONE); LinphoneActivity.instance().displayChatRoomError(); diff --git a/app/src/main/java/org/linphone/firebase/FirebaseIdService.java b/app/src/main/java/org/linphone/firebase/FirebaseIdService.java index 9841e9dd4..e4087faff 100644 --- a/app/src/main/java/org/linphone/firebase/FirebaseIdService.java +++ b/app/src/main/java/org/linphone/firebase/FirebaseIdService.java @@ -30,7 +30,7 @@ public class FirebaseIdService extends FirebaseInstanceIdService { public void onTokenRefresh() { // Get updated InstanceID token. final String refreshedToken = FirebaseInstanceId.getInstance().getToken(); - android.util.Log.i("FirebaseIdService", "[Push Notification] Refreshed token: " + refreshedToken); + android.util.Log.i("FirebaseIdService", "[Push Notifiable] Refreshed token: " + refreshedToken); LinphoneUtils.dispatchOnUIThread(new Runnable() { @Override diff --git a/app/src/main/java/org/linphone/firebase/FirebaseMessaging.java b/app/src/main/java/org/linphone/firebase/FirebaseMessaging.java index 16b4d6225..31041c2fe 100644 --- a/app/src/main/java/org/linphone/firebase/FirebaseMessaging.java +++ b/app/src/main/java/org/linphone/firebase/FirebaseMessaging.java @@ -36,10 +36,10 @@ public class FirebaseMessaging extends FirebaseMessagingService { @Override public void onMessageReceived(RemoteMessage remoteMessage) { - android.util.Log.i("FirebaseMessaging", "[Push Notification] Received"); + android.util.Log.i("FirebaseMessaging", "[Push Notifiable] Received"); if (!LinphoneService.isReady()) { - android.util.Log.i("FirebaseMessaging", "[Push Notification] Starting Service"); + android.util.Log.i("FirebaseMessaging", "[Push Notifiable] Starting Service"); startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class)); } else if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) { LinphoneUtils.dispatchOnUIThread(new Runnable() { diff --git a/app/src/main/java/org/linphone/fragments/HistoryDetailFragment.java b/app/src/main/java/org/linphone/fragments/HistoryDetailFragment.java index e20e9becf..0ca1406ff 100644 --- a/app/src/main/java/org/linphone/fragments/HistoryDetailFragment.java +++ b/app/src/main/java/org/linphone/fragments/HistoryDetailFragment.java @@ -109,7 +109,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener { public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { if (newState == ChatRoom.State.Created) { mWaitLayout.setVisibility(View.GONE); - LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), null, cr.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(cr.getPeerAddress().asStringUriOnly(), null); } else if (newState == ChatRoom.State.CreationFailed) { mWaitLayout.setVisibility(View.GONE); LinphoneActivity.instance().displayChatRoomError(); @@ -198,7 +198,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener { Address participant = Factory.instance().createAddress(sipUri); ChatRoom room = lc.findOneToOneChatRoom(lc.getDefaultProxyConfig().getContact(), participant, false); if (room != null) { - LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null); } else { ProxyConfig lpc = lc.getDefaultProxyConfig(); if (lpc != null && lpc.getConferenceFactoryUri() != null && !LinphonePreferences.instance().useBasicChatRoomFor1To1()) { @@ -208,7 +208,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener { mChatRoom.addParticipant(participant); } else { room = lc.getChatRoom(participant); - LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); + LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null); } } } else if (id == R.id.add_contact) { diff --git a/app/src/main/java/org/linphone/fragments/SettingsFragment.java b/app/src/main/java/org/linphone/fragments/SettingsFragment.java index 26c2919b8..34577b5ec 100644 --- a/app/src/main/java/org/linphone/fragments/SettingsFragment.java +++ b/app/src/main/java/org/linphone/fragments/SettingsFragment.java @@ -1362,9 +1362,9 @@ public class SettingsFragment extends PreferencesListFragment { boolean value = (Boolean) newValue; mPrefs.setServiceNotificationVisibility(value); if (value) { - LinphoneService.instance().showServiceNotification(); + LinphoneService.instance().getNotificationManager().startForeground(); } else { - LinphoneService.instance().hideServiceNotification(); + LinphoneService.instance().getNotificationManager().stopForeground(); } return true; } diff --git a/app/src/main/java/org/linphone/notifications/Notifiable.java b/app/src/main/java/org/linphone/notifications/Notifiable.java new file mode 100644 index 000000000..b0d6f675c --- /dev/null +++ b/app/src/main/java/org/linphone/notifications/Notifiable.java @@ -0,0 +1,86 @@ +package org.linphone.notifications; + +/* +Notifiable.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import java.util.ArrayList; +import java.util.List; + +public class Notifiable { + int mNotificationId; + List mMessages; + boolean mIsGroup; + String mGroupTitle; + String mLocalIdentity; + String mMyself; + + public Notifiable(int id) { + mNotificationId = id; + mMessages = new ArrayList<>(); + mIsGroup = false; + } + + public int getNotificationId() { + return mNotificationId; + } + + public void resetMessages() { + mMessages = new ArrayList<>(); + } + + public void addMessage(NotifiableMessage notifMessage) { + mMessages.add(notifMessage); + } + + public List getMessages() { + return mMessages; + } + + public boolean isGroup() { + return mIsGroup; + } + + public void setIsGroup(boolean isGroup) { + mIsGroup = isGroup; + } + + public String getGroupTitle() { + return mGroupTitle; + } + + public void setGroupTitle(String title) { + mGroupTitle = title; + } + + public String getMyself() { + return mMyself; + } + + public void setMyself(String myself) { + mMyself = myself; + } + + public String getLocalIdentity() { + return mLocalIdentity; + } + + public void setLocalIdentity(String localIdentity) { + mLocalIdentity = localIdentity; + } +} diff --git a/app/src/main/java/org/linphone/notifications/NotifiableMessage.java b/app/src/main/java/org/linphone/notifications/NotifiableMessage.java new file mode 100644 index 000000000..20639379e --- /dev/null +++ b/app/src/main/java/org/linphone/notifications/NotifiableMessage.java @@ -0,0 +1,44 @@ +package org.linphone.notifications; + +/* +NotifiableMessage.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +public class NotifiableMessage { + String mMessage; + String mSender; + long mTime; + + public NotifiableMessage(String message, String sender, long time) { + mMessage = message; + mSender = sender; + mTime = time; + } + + public String getMessage() { + return mMessage; + } + + public String getSender() { + return mSender; + } + + public long getTime() { + return mTime; + } +} diff --git a/app/src/main/java/org/linphone/receivers/NotificationBroadcastReceiver.java b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java similarity index 52% rename from app/src/main/java/org/linphone/receivers/NotificationBroadcastReceiver.java rename to app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java index 0bdb3f336..31e560f6d 100644 --- a/app/src/main/java/org/linphone/receivers/NotificationBroadcastReceiver.java +++ b/app/src/main/java/org/linphone/notifications/NotificationBroadcastReceiver.java @@ -1,4 +1,4 @@ -package org.linphone.receivers; +package org.linphone.notifications; /* NotificationBroadcastReceiver.java @@ -29,48 +29,82 @@ import android.os.Bundle; import org.linphone.LinphoneManager; import org.linphone.LinphoneService; +import org.linphone.R; import org.linphone.activities.LinphoneActivity; import org.linphone.compatibility.Compatibility; import org.linphone.core.Address; import org.linphone.core.Call; import org.linphone.core.ChatMessage; +import org.linphone.core.ChatMessageListenerStub; import org.linphone.core.ChatRoom; import org.linphone.core.Core; import org.linphone.core.ProxyConfig; -import org.linphone.core.Reason; +import org.linphone.mediastream.Log; public class NotificationBroadcastReceiver extends BroadcastReceiver { @Override - public void onReceive(Context context, Intent intent) { + public void onReceive(final Context context, Intent intent) { + final int notifId = intent.getIntExtra(Compatibility.INTENT_NOTIF_ID, 0); + final String localyIdentity = intent.getStringExtra(Compatibility.INTENT_LOCAL_IDENTITY); + if (intent.getAction() == Compatibility.INTENT_REPLY_NOTIF_ACTION) { - String reply = getMessageText(intent).toString(); - if (reply == null) return; - Notification replied = Compatibility.createRepliedNotification(context, reply); - if (replied == null) return; - int notifId = intent.getIntExtra(Compatibility.INTENT_NOTIF_ID, 0); - String remoteSipAddr = LinphoneService.instance().getSipUriForNotificationId(notifId); + final String reply = getMessageText(intent).toString(); + if (reply == null) { + Log.e("Couldn't get reply text"); + onError(context, notifId); + return; + } + String remoteSipAddr = LinphoneService.instance().getNotificationManager().getSipUriForNotificationId(notifId); Core core = LinphoneManager.getLc(); - if (core == null) return; - ProxyConfig proxyConfig = core.getDefaultProxyConfig(); - if (proxyConfig == null) return; - Address localAddr = proxyConfig.getIdentityAddress(); + if (core == null) { + Log.e("Couldn't get Core instance"); + onError(context, notifId); + return; + } + Address remoteAddr = core.interpretUrl(remoteSipAddr); - if (localAddr == null || remoteAddr == null) return; + if (remoteAddr == null) { + Log.e("Couldn't interpret remote address " + remoteSipAddr); + onError(context, notifId); + return; + } + + Address localAddr = core.interpretUrl(localyIdentity); + if (localAddr == null) { + Log.e("Couldn't interpret local address " + localyIdentity); + onError(context, notifId); + return; + } + ChatRoom room = core.findChatRoom(remoteAddr, localAddr); - if (room == null) return; + if (room == null) { + Log.e("Couldn't find chat room for remote address " + remoteSipAddr + " and local address " + localyIdentity); + onError(context, notifId); + return; + } room.markAsRead(); if (LinphoneActivity.isInstanciated()) { LinphoneActivity.instance().displayMissedChats(LinphoneManager.getInstance().getUnreadMessageCount()); } + ChatMessage msg = room.createMessage(reply); msg.send(); - - LinphoneService.instance().sendNotification(replied, notifId); + msg.setListener(new ChatMessageListenerStub() { + @Override + public void onMsgStateChanged(ChatMessage msg, ChatMessage.State state) { + if (state == ChatMessage.State.Delivered) { + Notification replied = Compatibility.createRepliedNotification(context, reply); + LinphoneService.instance().getNotificationManager().sendNotification(notifId, replied); + } else if (state == ChatMessage.State.NotDelivered) { + Log.e("Couldn't send reply, message is not delivered"); + onError(context, notifId); + } + } + }); } else if (intent.getAction() == Compatibility.INTENT_ANSWER_CALL_NOTIF_ACTION || intent.getAction() == Compatibility.INTENT_HANGUP_CALL_NOTIF_ACTION) { - int callId = intent.getIntExtra(Compatibility.INTENT_CALL_ID, 0); - String remoteAddr = LinphoneService.instance().getSipUriForCallNotificationId(callId); + String remoteAddr = LinphoneService.instance().getNotificationManager().getSipUriForCallNotificationId(notifId); Core core = LinphoneManager.getLc(); if (core == null) return; @@ -85,6 +119,11 @@ public class NotificationBroadcastReceiver extends BroadcastReceiver { } } + private void onError(Context context, int notifId) { + Notification replyError = Compatibility.createRepliedNotification(context, context.getString(R.string.error)); + LinphoneService.instance().getNotificationManager().sendNotification(notifId, replyError); + } + @TargetApi(20) private CharSequence getMessageText(Intent intent) { Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); diff --git a/app/src/main/java/org/linphone/notifications/NotificationsManager.java b/app/src/main/java/org/linphone/notifications/NotificationsManager.java new file mode 100644 index 000000000..34dfa6256 --- /dev/null +++ b/app/src/main/java/org/linphone/notifications/NotificationsManager.java @@ -0,0 +1,308 @@ +package org.linphone.notifications; + +/* +NotificationsManager.java +Copyright (C) 2018 Belledonne Communications, Grenoble, France + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.provider.MediaStore; + +import org.linphone.LinphoneManager; +import org.linphone.LinphonePreferences; +import org.linphone.LinphoneService; +import org.linphone.LinphoneUtils; +import org.linphone.R; +import org.linphone.activities.LinphoneActivity; +import org.linphone.compatibility.Compatibility; +import org.linphone.contacts.ContactsManager; +import org.linphone.contacts.LinphoneContact; +import org.linphone.core.Address; +import org.linphone.core.Call; +import org.linphone.mediastream.Version; + +import java.util.HashMap; + +import static android.content.Context.NOTIFICATION_SERVICE; + +public class NotificationsManager { + private static final int SERVICE_NOTIF_ID = 1; + private static final int MISSED_CALLS_NOTIF_ID = 2; + private static final int IN_APP_NOTIF_ID = 3; + + private Context mContext; + private NotificationManager mNM; + private HashMap mChatNotifMap, mCallNotifMap; + private int mLastNotificationId; + private PendingIntent mPendingIntent; + private Notification mServiceNotification; + + public NotificationsManager(Context context) { + mContext = context; + mChatNotifMap = new HashMap<>(); + mCallNotifMap = new HashMap<>(); + + mNM = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE); + mNM.cancelAll(); + + mLastNotificationId = 5; // Do not conflict with hardcoded notifications ids ! + + Compatibility.createNotificationChannels(mContext); + + Intent notifIntent = new Intent(mContext, LinphoneService.instance().getIncomingReceivedActivity()); + notifIntent.putExtra("Notifiable", true); + mPendingIntent = PendingIntent.getActivity(mContext, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + //Disable service notification for Android O + if (Version.sdkAboveOrEqual(Version.API26_O_80)) { + LinphonePreferences.instance().setServiceNotificationVisibility(false); + } + + Bitmap bm = null; + try { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.ic_launcher); + } catch (Exception e) { + } + mServiceNotification = Compatibility.createNotification(mContext, mContext.getString(R.string.service_name), "", + R.drawable.linphone_notification_icon, R.mipmap.ic_launcher, bm, mPendingIntent, true, + Notification.PRIORITY_MIN); + + if (isServiceNotificationDisplayed()) { + startForeground(); + } + } + + public void destroy() { + mNM.cancelAll(); + } + + public void startForeground() { + LinphoneService.instance().startForeground(SERVICE_NOTIF_ID, mServiceNotification); + } + + public void stopForeground() { + LinphoneService.instance().stopForeground(true); + } + + public void sendNotification(int id, Notification notif) { + mNM.notify(id, notif); + } + + public void resetMessageNotifCount(String address) { + Notifiable notif = mChatNotifMap.get(address); + if (notif != null) { + notif.resetMessages(); + mNM.cancel(notif.getNotificationId()); + } + } + + public boolean isServiceNotificationDisplayed() { + return LinphonePreferences.instance().getServiceNotificationVisibility(); + } + + public String getSipUriForNotificationId(int notificationId) { + for (String addr : mChatNotifMap.keySet()) { + if (mChatNotifMap.get(addr).getNotificationId() == notificationId) { + return addr; + } + } + return null; + } + + public void displayGroupChatMessageNotification(String subject, String conferenceAddress, String fromName, Uri fromPictureUri, String message, Address localIdentity) { + Intent notifIntent = new Intent(mContext, LinphoneActivity.class); + notifIntent.putExtra("GoToChat", true); + notifIntent.putExtra("ChatContactSipUri", conferenceAddress); + PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + Notifiable notif = mChatNotifMap.get(conferenceAddress); + NotifiableMessage notifMessage = new NotifiableMessage(message, fromName, 0); + if (notif == null) { + notif = new Notifiable(mLastNotificationId); + mLastNotificationId += 1; + mChatNotifMap.put(conferenceAddress, notif); + } + notif.addMessage(notifMessage); + notif.setIsGroup(true); + notif.setGroupTitle(subject); + notif.setMyself(LinphoneUtils.getAddressDisplayName(localIdentity)); + notif.setLocalIdentity(localIdentity.asString()); + + Bitmap bm; + if (fromPictureUri != null) { + try { + bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), fromPictureUri); + } catch (Exception e) { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_group_avatar); + } + } else { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.topbar_avatar); + } + Notification notification = Compatibility.createMessageNotification(mContext, notif, subject, + mContext.getString(R.string.group_chat_notif).replace("%1", fromName).replace("%2", message), bm, pendingIntent); + sendNotification(notif.getNotificationId(), notification); + } + + public void displayMessageNotification(String fromSipUri, String fromName, Uri fromPictureUri, String message, Address localIdentity) { + Intent notifIntent = new Intent(mContext, LinphoneActivity.class); + notifIntent.putExtra("GoToChat", true); + notifIntent.putExtra("ChatContactSipUri", fromSipUri); + PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + if (fromName == null) { + fromName = fromSipUri; + } + + Notifiable notif = mChatNotifMap.get(fromSipUri); + NotifiableMessage notifMessage = new NotifiableMessage(message, fromName, 0); + if (notif == null) { + notif = new Notifiable(mLastNotificationId); + mLastNotificationId += 1; + mChatNotifMap.put(fromSipUri, notif); + } + notif.addMessage(notifMessage); + notif.setIsGroup(false); + notif.setMyself(LinphoneUtils.getAddressDisplayName(localIdentity)); + notif.setLocalIdentity(localIdentity.asString()); + + Bitmap bm; + if (fromPictureUri != null) { + try { + bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), fromPictureUri); + } catch (Exception e) { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.topbar_avatar); + } + } else { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.topbar_avatar); + } + Notification notification = Compatibility.createMessageNotification(mContext, notif, fromName, message, bm, pendingIntent); + sendNotification(notif.getNotificationId(), notification); + } + + public void displayMissedCallNotification(Call call) { + Intent missedCallNotifIntent = new Intent(mContext, LinphoneService.instance().getIncomingReceivedActivity()); + missedCallNotifIntent.putExtra("GoToHistory", true); + PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, missedCallNotifIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + int missedCallCount = LinphoneManager.getLcIfManagerNotDestroyedOrNull().getMissedCallsCount(); + String body; + if (missedCallCount > 1) { + body = mContext.getString(R.string.missed_calls_notif_body).replace("%i", String.valueOf(missedCallCount)); + } else { + Address address = call.getRemoteAddress(); + LinphoneContact c = ContactsManager.getInstance().findContactFromAddress(address); + if (c != null) { + body = c.getFullName(); + } else { + body = address.getDisplayName(); + if (body == null) { + body = address.asStringUriOnly(); + } + } + } + Notification notif = Compatibility.createMissedCallNotification(mContext, mContext.getString(R.string.missed_calls_notif_title), body, pendingIntent); + sendNotification(MISSED_CALLS_NOTIF_ID, notif); + } + + public void displayCallNotification(Call call) { + if (call == null) return; + + Address address = call.getRemoteAddress(); + String addressAsString = address.asStringUriOnly(); + Notifiable notif = mCallNotifMap.get(addressAsString); + if (notif == null) { + notif = new Notifiable(mLastNotificationId); + mLastNotificationId += 1; + mCallNotifMap.put(addressAsString, notif); + } + + if (!isServiceNotificationDisplayed()) { + if (call.getCore().getCallsNb() == 0) { + stopForeground(); + } else { + startForeground(); + } + } + + int notificationTextId; + int iconId; + switch (call.getState()) { + case Released: + case End: + mNM.cancel(notif.getNotificationId()); + mCallNotifMap.remove(addressAsString); + return; + case Paused: + case PausedByRemote: + case Pausing: + iconId = R.drawable.topbar_call_notification; + notificationTextId = R.string.incall_notif_paused; + break; + default: + if (call.getCurrentParams().videoEnabled()) { + iconId = R.drawable.topbar_videocall_notification; + notificationTextId = R.string.incall_notif_video; + } else { + iconId = R.drawable.topbar_call_notification; + notificationTextId = R.string.incall_notif_active; + } + break; + } + + LinphoneContact contact = ContactsManager.getInstance().findContactFromAddress(address); + Uri pictureUri = contact != null ? contact.getPhotoUri() : null; + Bitmap bm; + try { + bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), pictureUri); + } catch (Exception e) { + bm = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.avatar); + } + String name = LinphoneUtils.getAddressDisplayName(address); + + boolean showAnswerAction = call.getState() == Call.State.IncomingReceived || call.getState() == Call.State.IncomingEarlyMedia; + Notification notification = Compatibility.createInCallNotification(mContext, notif.getNotificationId(), + showAnswerAction, mContext.getString(R.string.service_name), + mContext.getString(notificationTextId), iconId, bm, name, mPendingIntent); + + sendNotification(notif.getNotificationId(), notification); + } + + public String getSipUriForCallNotificationId(int notificationId) { + for (String addr : mCallNotifMap.keySet()) { + if (mCallNotifMap.get(addr).getNotificationId() == notificationId) { + return addr; + } + } + return null; + } + + public void displayInappNotification(String message) { + Intent notifIntent = new Intent(mContext, LinphoneActivity.class); + notifIntent.putExtra("GoToInapp", true); + PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notifIntent, PendingIntent.FLAG_UPDATE_CURRENT); + Notification notif = Compatibility.createSimpleNotification(mContext, mContext.getString(R.string.inapp_notification_title), message, pendingIntent); + + sendNotification(IN_APP_NOTIF_ID, notif); + } +}