Cleaned LinphoneService a bit, moved all notifications related code to another class

This commit is contained in:
Sylvain Berfini 2018-11-20 17:10:05 +01:00
parent 886b5a36c0
commit 1e6fb279c0
22 changed files with 595 additions and 525 deletions

View file

@ -294,7 +294,7 @@
<activity android:name=".activities.LinphoneGenericActivity"/> <activity android:name=".activities.LinphoneGenericActivity"/>
<receiver <receiver
android:name=".receivers.NotificationBroadcastReceiver" android:name=".notifications.NotificationBroadcastReceiver"
android:enabled="true" android:enabled="true"
android:exported="false" /> android:exported="false" />

View file

@ -504,7 +504,7 @@ public class LinphoneManager implements CoreListener, SensorEventListener, Accou
if (call != null) { if (call != null) {
call.enableCamera(enable); call.enableCamera(enable);
if (mServiceContext.getResources().getBoolean(R.bool.enable_call_notification)) 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 (!mServiceContext.getResources().getBoolean(R.bool.disable_chat_message_notification) && !message.isOutgoing()) {
if (cr.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) { if (cr.hasCapability(ChatRoomCapabilities.OneToOne.toInt())) {
if (contact != null) { 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 { } 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 { } else {
String subject = cr.getSubject(); String subject = cr.getSubject();
if (contact != null) { 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 { } 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());
} }
} }
} }

View file

@ -23,31 +23,22 @@ import android.annotation.TargetApi;
import android.app.Activity; import android.app.Activity;
import android.app.AlarmManager; import android.app.AlarmManager;
import android.app.Application; import android.app.Application;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException; 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.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.SystemClock; import android.os.SystemClock;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.view.WindowManager; import android.view.WindowManager;
import org.linphone.activities.LinphoneActivity; import org.linphone.activities.LinphoneActivity;
import org.linphone.compatibility.Compatibility;
import org.linphone.contacts.ContactsManager; 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;
import org.linphone.core.Call.State; import org.linphone.core.Call.State;
import org.linphone.core.Core; import org.linphone.core.Core;
@ -58,14 +49,12 @@ import org.linphone.core.ProxyConfig;
import org.linphone.core.RegistrationState; import org.linphone.core.RegistrationState;
import org.linphone.mediastream.Log; import org.linphone.mediastream.Log;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.notifications.NotificationsManager;
import org.linphone.receivers.BluetoothManager; import org.linphone.receivers.BluetoothManager;
import org.linphone.receivers.KeepAliveReceiver; import org.linphone.receivers.KeepAliveReceiver;
import org.linphone.ui.LinphoneOverlay; import org.linphone.ui.LinphoneOverlay;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
/** /**
* Linphone service, reacting to Incoming calls, ...<br /> * Linphone service, reacting to Incoming calls, ...<br />
@ -81,14 +70,9 @@ public final class LinphoneService extends Service {
* setLatestEventInfo and startActivity() which needs a context. * setLatestEventInfo and startActivity() which needs a context.
*/ */
public static final String START_LINPHONE_LOGS = " ==== Phone information dump ===="; public static final String START_LINPHONE_LOGS = " ==== Phone information dump ====";
public static final int IC_LEVEL_ORANGE = 0;
private static LinphoneService instance; 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() { public static boolean isReady() {
return instance != null && instance.mTestDelayElapsed; 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; // add a timer for testing
private boolean mTestDelayElapsed = true; // no timer 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; private CoreListenerStub mListener;
public static int notifcationsPriority = (Version.sdkAboveOrEqual(Version.API16_JELLY_BEAN_41) ? Notification.PRIORITY_MIN : 0);
private WindowManager mWindowManager; private WindowManager mWindowManager;
private LinphoneOverlay mOverlay; private LinphoneOverlay mOverlay;
private Application.ActivityLifecycleCallbacks activityCallbacks; private Application.ActivityLifecycleCallbacks activityCallbacks;
private class Notified { private NotificationsManager mNotificationManager;
int notificationId;
int numberOfUnreadMessage; public NotificationsManager getNotificationManager() {
return mNotificationManager;
} }
private HashMap<String, Notified> mChatNotifMap, mCallNotifMap; private String incomingReceivedActivityName;
private int mLastNotificationId; private Class<? extends Activity> incomingReceivedActivity = LinphoneActivity.class;
public Class<? extends Activity> getIncomingReceivedActivity() {
return incomingReceivedActivity;
}
public void setCurrentlyDisplayedChatRoom(String address) { public void setCurrentlyDisplayedChatRoom(String address) {
if (address != null) { if (address != null) {
resetMessageNotifCount(address); mNotificationManager.resetMessageNotifCount(address);
}
}
private void resetMessageNotifCount(String address) {
Notified notif = mChatNotifMap.get(address);
if (notif != null) {
notif.numberOfUnreadMessage = 0;
mNM.cancel(notif.notificationId);
} }
} }
@ -169,8 +143,6 @@ public final class LinphoneService extends Service {
} }
} }
;
private InactivityChecker mLastChecker; private InactivityChecker mLastChecker;
@Override @Override
@ -275,31 +247,6 @@ public final class LinphoneService extends Service {
getApplication().registerActivityLifecycleCallbacks(activityCallbacks = new ActivityMonitor()); 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 @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId); super.onStartCommand(intent, flags, startId);
@ -309,9 +256,10 @@ public final class LinphoneService extends Service {
return START_REDELIVER_INTENT; return START_REDELIVER_INTENT;
} }
LinphoneManager.createAndStart(LinphoneService.this); LinphoneManager.createAndStart(this);
instance = this; // instance is ready once linphone manager has been created instance = this; // instance is ready once linphone manager has been created
mNotificationManager = new NotificationsManager(this);
LinphoneManager.getLc().addListener(mListener = new CoreListenerStub() { LinphoneManager.getLc().addListener(mListener = new CoreListenerStub() {
@Override @Override
public void onCallStateChanged(Core lc, Call call, Call.State state, String message) { 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)) { if (getResources().getBoolean(R.bool.enable_call_notification)) {
displayCallNotification(call); mNotificationManager.displayCallNotification(call);
} }
if (state == Call.State.IncomingReceived) { 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) { if (state == State.End && call.getCallLog().getStatus() == Call.Status.Missed) {
int missedCallCount = LinphoneManager.getLcIfManagerNotDestroyedOrNull().getMissedCallsCount(); mNotificationManager.displayMissedCallNotification(call);
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);
} }
} }
@Override @Override
public void onGlobalStateChanged(Core lc, GlobalState state, String message) { public void onGlobalStateChanged(Core lc, GlobalState state, String message) {
if (!mDisableRegistrationStatus && state == GlobalState.On && displayServiceNotification()) { //TODO global state if ON
sendNotification(IC_LEVEL_ORANGE, R.string.notification_started);
}
} }
@Override @Override
public void onRegistrationStateChanged(Core lc, ProxyConfig cfg, RegistrationState state, String smessage) { public void onRegistrationStateChanged(Core lc, ProxyConfig cfg, RegistrationState state, String smessage) {
if (!mDisableRegistrationStatus) { //TODO registration status
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);
}
}
} }
}); });
if (displayServiceNotification() || (Version.sdkAboveOrEqual(Version.API26_O_80) && intent.getBooleanExtra("ForceStartForeground", false))) { if (Version.sdkAboveOrEqual(Version.API26_O_80) && intent.getBooleanExtra("ForceStartForeground", false)) {
startForegroundCompat(NOTIF_ID, mNotif); mNotificationManager.startForeground();
} }
if (!Version.sdkAboveOrEqual(Version.API26_O_80) if (!Version.sdkAboveOrEqual(Version.API26_O_80)
@ -426,13 +339,8 @@ public final class LinphoneService extends Service {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
mLastNotificationId = 8; // To not interfere with other notifs ids
mChatNotifMap = new HashMap<>();
mCallNotifMap = new HashMap<>();
setupActivityMonitor(); 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 // Needed in order for the two next calls to succeed, libraries must have been loaded first
LinphonePreferences.instance().setContext(getBaseContext()); LinphonePreferences.instance().setContext(getBaseContext());
@ -445,27 +353,6 @@ public final class LinphoneService extends Service {
dumpDeviceInformation(); dumpDeviceInformation();
dumpInstalledLinphoneInformation(); 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(); incomingReceivedActivityName = LinphonePreferences.instance().getActivityToLaunchOnIncomingReceived();
try { try {
incomingReceivedActivity = (Class<? extends Activity>) Class.forName(incomingReceivedActivityName); incomingReceivedActivity = (Class<? extends Activity>) Class.forName(incomingReceivedActivityName);
@ -473,13 +360,6 @@ public final class LinphoneService extends Service {
Log.e(e); 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); mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
} }
@ -504,249 +384,6 @@ public final class LinphoneService extends Service {
mOverlay = null; 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<? extends Activity> 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() { private void dumpDeviceInformation() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("DEVICE=").append(Build.DEVICE).append("\n"); 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 @Override
public IBinder onBind(Intent intent) { public IBinder onBind(Intent intent) {
return null; return null;
@ -849,8 +454,7 @@ public final class LinphoneService extends Service {
LinphoneManager.destroy(); LinphoneManager.destroy();
// Make sure our notification is gone. // Make sure our notification is gone.
stopForegroundCompat(NOTIF_ID); mNotificationManager.destroy();
mNM.cancelAll();
// This will prevent the app from crashing if the service gets killed in background mode // This will prevent the app from crashing if the service gets killed in background mode
if (LinphoneActivity.isInstanciated()) { if (LinphoneActivity.isInstanciated()) {
@ -875,8 +479,8 @@ public final class LinphoneService extends Service {
protected void onIncomingReceived() { protected void onIncomingReceived() {
//wakeup linphone //wakeup linphone
startActivity(new Intent() startActivity(new Intent()
.setClass(this, incomingReceivedActivity) .setClass(this, incomingReceivedActivity)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
} }
} }

View file

@ -698,10 +698,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
changeCurrentFragment(FragmentsAvailable.CREATE_CHAT, extras); 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(); Bundle extras = new Bundle();
extras.putString("SipUri", sipUri); extras.putString("SipUri", sipUri);
extras.putString("LocalIdentity", localIdentity);
if (shareInfos != null) { if (shareInfos != null) {
if (shareInfos.getString("fileSharedUri") != null) if (shareInfos.getString("fileSharedUri") != null)
@ -1382,9 +1381,6 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); Core lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull();
if (lc != null) { if (lc != null) {
lc.addListener(mListener); lc.addListener(mListener);
if (!LinphoneService.instance().displayServiceNotification()) {
lc.refreshRegisters();
}
} }
if (isTablet()) { if (isTablet()) {
@ -1483,12 +1479,11 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
Bundle extras = intent.getExtras(); Bundle extras = intent.getExtras();
if (extras != null && extras.getBoolean("GoToChat", false)) { if (extras != null && extras.getBoolean("GoToChat", false)) {
String sipUri = extras.getString("ChatContactSipUri"); String sipUri = extras.getString("ChatContactSipUri");
String localIdentity = extras.getString("LocalIdentity");
intent.putExtra("DoNotGoToCallActivity", true); intent.putExtra("DoNotGoToCallActivity", true);
if (sipUri == null) { if (sipUri == null) {
goToChatList(); goToChatList();
} else { } else {
goToChat(sipUri, extras, localIdentity); goToChat(sipUri, extras);
} }
} else if (extras != null && extras.getBoolean("GoToHistory", false)) { } else if (extras != null && extras.getBoolean("GoToHistory", false)) {
intent.putExtra("DoNotGoToCallActivity", true); intent.putExtra("DoNotGoToCallActivity", true);
@ -1496,7 +1491,7 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
} else if (extras != null && extras.getBoolean("GoToInapp", false)) { } else if (extras != null && extras.getBoolean("GoToInapp", false)) {
intent.putExtra("DoNotGoToCallActivity", true); intent.putExtra("DoNotGoToCallActivity", true);
displayInapp(); displayInapp();
} else if (extras != null && extras.getBoolean("Notification", false)) { } else if (extras != null && extras.getBoolean("Notifiable", false)) {
if (LinphoneManager.getLc().getCallsNb() > 0) { if (LinphoneManager.getLc().getCallsNb() > 0) {
startIncallActivity(); startIncallActivity();
} }
@ -1840,9 +1835,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick
LinphonePreferences.instance().setInappPopupTime(String.valueOf(newDate)); LinphonePreferences.instance().setInappPopupTime(String.valueOf(newDate));
} }
if (isTrialAccount) { 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 { } 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));
} }
} }

View file

@ -213,7 +213,7 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
if (newState == ChatRoom.State.Created) { if (newState == ChatRoom.State.Created) {
mWaitLayout.setVisibility(View.GONE); 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) { } else if (newState == ChatRoom.State.CreationFailed) {
mWaitLayout.setVisibility(View.GONE); mWaitLayout.setVisibility(View.GONE);
LinphoneActivity.instance().displayChatRoomError(); LinphoneActivity.instance().displayChatRoomError();
@ -468,7 +468,7 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen
if (createEncryptedChatRoom && lpc != null && lpc.getConferenceFactoryUri() != null) { if (createEncryptedChatRoom && lpc != null && lpc.getConferenceFactoryUri() != null) {
mChatRoom = lc.findOneToOneChatRoom(lpc.getIdentityAddress(), ca.getAddress(), true); mChatRoom = lc.findOneToOneChatRoom(lpc.getIdentityAddress(), ca.getAddress(), true);
if (mChatRoom != null) { if (mChatRoom != null) {
LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos);
} else { } else {
mChatRoom = lc.createClientGroupChatRoom(getString(R.string.dummy_group_chat_subject), !createEncryptedChatRoom, createEncryptedChatRoom); mChatRoom = lc.createClientGroupChatRoom(getString(R.string.dummy_group_chat_subject), !createEncryptedChatRoom, createEncryptedChatRoom);
mChatRoom.addListener(mChatRoomCreationListener); mChatRoom.addListener(mChatRoomCreationListener);
@ -487,11 +487,11 @@ public class ChatCreationFragment extends Fragment implements View.OnClickListen
participants[0] = ca.getAddress(); participants[0] = ca.getAddress();
mChatRoom.addParticipants(participants); mChatRoom.addParticipants(participants);
} else { } else {
LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mChatRoom.getPeerAddress().asStringUriOnly(), mShareInfos);
} }
} else { } else {
ChatRoom chatRoom = lc.getChatRoom(ca.getAddress()); ChatRoom chatRoom = lc.getChatRoom(ca.getAddress());
LinphoneActivity.instance().goToChat(chatRoom.getPeerAddress().asStringUriOnly(), mShareInfos, chatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(chatRoom.getPeerAddress().asStringUriOnly(), mShareInfos);
} }
} }
} else { } else {

View file

@ -161,7 +161,7 @@ public class ChatListFragment extends Fragment implements ContactsUpdatedListene
mChatRoomsAdapter.toggleSelection(position); mChatRoomsAdapter.toggleSelection(position);
} else { } else {
ChatRoom room = (ChatRoom) mChatRoomsAdapter.getItem(position); 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(); ChatRoomsAdapter adapter = (ChatRoomsAdapter) mChatRoomsList.getAdapter();
if (adapter != null && adapter.getItemCount() > 0) { if (adapter != null && adapter.getItemCount() > 0) {
ChatRoom room = (ChatRoom) adapter.getItem(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 { } else {
LinphoneActivity.instance().displayEmptyFragment(); LinphoneActivity.instance().displayEmptyFragment();
} }

View file

@ -106,7 +106,7 @@ public class DevicesFragment extends Fragment {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (LinphoneActivity.instance().isTablet()) { if (LinphoneActivity.instance().isTablet()) {
LinphoneActivity.instance().goToChat(mRoomUri, null, mRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mRoomUri, null);
} else { } else {
LinphoneActivity.instance().onBackPressed(); LinphoneActivity.instance().onBackPressed();
} }

View file

@ -951,11 +951,11 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con
if (LinphoneActivity.instance().isOnBackground()) { if (LinphoneActivity.instance().isOnBackground()) {
if (!getResources().getBoolean(R.bool.disable_chat_message_notification)) { if (!getResources().getBoolean(R.bool.disable_chat_message_notification)) {
if (contact != null) { if (contact != null) {
LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), LinphoneService.instance().getNotificationManager().displayMessageNotification(from.asStringUriOnly(),
contact.getFullName(), contact.getThumbnailUri(), getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress().asString()); contact.getFullName(), contact.getThumbnailUri(), getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress());
} else { } else {
LinphoneService.instance().displayMessageNotification(from.asStringUriOnly(), LinphoneService.instance().getNotificationManager().displayMessageNotification(from.asStringUriOnly(),
from.getUsername(), null, getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress().asString()); from.getUsername(), null, getString(R.string.message_cant_be_decrypted_notif), cr.getLocalAddress());
} }
} }
} else if (LinphoneManager.getLc().limeEnabled() == LimeState.Mandatory) { } else if (LinphoneManager.getLc().limeEnabled() == LimeState.Mandatory) {

View file

@ -159,7 +159,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener {
public void onClick(View view) { public void onClick(View view) {
if (mIsAlreadyCreatedGroup) { if (mIsAlreadyCreatedGroup) {
if (LinphoneActivity.instance().isTablet()) { if (LinphoneActivity.instance().isTablet()) {
LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asStringUriOnly(), mShareInfos, mChatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asStringUriOnly(), mShareInfos);
} else { } else {
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
} }
@ -185,7 +185,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener {
public void onClick(View view) { public void onClick(View view) {
if (mChatRoom != null) { if (mChatRoom != null) {
mChatRoom.leave(); mChatRoom.leave();
LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null, mChatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null);
} else { } else {
Log.e("Can't leave, chatRoom for address " + mGroupChatRoomAddress.asString() + " is null..."); 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 // This will remove both the creation fragment and the group info fragment from the back stack
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
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) { } else if (newState == ChatRoom.State.CreationFailed) {
mWaitLayout.setVisibility(View.GONE); mWaitLayout.setVisibility(View.GONE);
LinphoneActivity.instance().displayChatRoomError(); LinphoneActivity.instance().displayChatRoomError();
@ -322,7 +322,7 @@ public class GroupInfoFragment extends Fragment implements ChatRoomListener {
toAdd.toArray(participantsToAdd); toAdd.toArray(participantsToAdd);
mChatRoom.addParticipants(participantsToAdd); mChatRoom.addParticipants(participantsToAdd);
LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null, mChatRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mGroupChatRoomAddress.asString(), null);
} }
} }
}); });

View file

@ -85,7 +85,7 @@ public class ImdnFragment extends Fragment {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
if (LinphoneActivity.instance().isTablet()) { if (LinphoneActivity.instance().isTablet()) {
LinphoneActivity.instance().goToChat(mRoomUri, null, mRoom.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(mRoomUri, null);
} else { } else {
LinphoneActivity.instance().onBackPressed(); LinphoneActivity.instance().onBackPressed();
} }

View file

@ -2,23 +2,22 @@ package org.linphone.compatibility;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.FragmentTransaction;
import android.app.Notification; import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.RemoteInput; import android.app.RemoteInput;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.view.ViewTreeObserver;
import org.linphone.R; 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_ANSWER_CALL_NOTIF_ACTION;
import static org.linphone.compatibility.Compatibility.INTENT_CALL_ID; 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_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_NOTIF_ID;
import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION;
import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY;
@ -52,23 +51,17 @@ public class ApiTwentyFourPlus {
.build(); .build();
} }
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, Bitmap contactIcon, PendingIntent intent) {
String title;
if (msgCount == 1) {
title = msgSender;
} else {
title = context.getString(R.string.unread_messages).replace("%i", String.valueOf(msgCount));
}
String replyLabel = context.getResources().getString(R.string.notification_reply_label); String replyLabel = context.getResources().getString(R.string.notification_reply_label);
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build(); RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build();
Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class); Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class);
replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); 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, 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, Notification.Action action = new Notification.Action.Builder(R.drawable.chat_send_over,
context.getString(R.string.notification_reply_label), replyPendingIntent) context.getString(R.string.notification_reply_label), replyPendingIntent)
@ -76,9 +69,13 @@ public class ApiTwentyFourPlus {
.setAllowGeneratedReplies(true) .setAllowGeneratedReplies(true)
.build(); .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) return new Notification.Builder(context)
.setContentTitle(title)
.setContentText(msg)
.setSmallIcon(R.drawable.topbar_chat_notification) .setSmallIcon(R.drawable.topbar_chat_notification)
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(intent) .setContentIntent(intent)
@ -87,10 +84,11 @@ public class ApiTwentyFourPlus {
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
.setVisibility(Notification.VISIBILITY_PRIVATE) .setVisibility(Notification.VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
.setNumber(msgCount) .setNumber(notif.getMessages().size())
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setShowWhen(true) .setShowWhen(true)
.setColor(context.getColor(R.color.notification_color_led)) .setColor(context.getColor(R.color.notification_color_led))
.setStyle(style)
.addAction(action) .addAction(action)
.build(); .build();
} }

View file

@ -11,15 +11,16 @@ import android.app.RemoteInput;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.view.ViewTreeObserver;
import org.linphone.R; import org.linphone.R;
import org.linphone.mediastream.Log; import org.linphone.notifications.Notifiable;
import org.linphone.receivers.NotificationBroadcastReceiver; 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_ANSWER_CALL_NOTIF_ACTION;
import static org.linphone.compatibility.Compatibility.INTENT_CALL_ID; 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_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_NOTIF_ID;
import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION; import static org.linphone.compatibility.Compatibility.INTENT_REPLY_NOTIF_ACTION;
import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY; import static org.linphone.compatibility.Compatibility.KEY_TEXT_REPLY;
@ -83,23 +84,17 @@ public class ApiTwentySixPlus {
notificationManager.createNotificationChannel(channel); notificationManager.createNotificationChannel(channel);
} }
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, Bitmap contactIcon, PendingIntent intent) {
String title;
if (msgCount == 1) {
title = msgSender;
} else {
title = context.getString(R.string.unread_messages).replace("%i", String.valueOf(msgCount));
}
String replyLabel = context.getResources().getString(R.string.notification_reply_label); String replyLabel = context.getResources().getString(R.string.notification_reply_label);
RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build(); RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY).setLabel(replyLabel).build();
Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class); Intent replyIntent = new Intent(context, NotificationBroadcastReceiver.class);
replyIntent.setAction(INTENT_REPLY_NOTIF_ACTION); 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, 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, Notification.Action action = new Notification.Action.Builder(R.drawable.chat_send_over,
context.getString(R.string.notification_reply_label), replyPendingIntent) context.getString(R.string.notification_reply_label), replyPendingIntent)
@ -107,9 +102,13 @@ public class ApiTwentySixPlus {
.setAllowGeneratedReplies(true) .setAllowGeneratedReplies(true)
.build(); .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)) return new Notification.Builder(context, context.getString(R.string.notification_channel_id))
.setContentTitle(title)
.setContentText(msg)
.setSmallIcon(R.drawable.topbar_chat_notification) .setSmallIcon(R.drawable.topbar_chat_notification)
.setAutoCancel(true) .setAutoCancel(true)
.setContentIntent(intent) .setContentIntent(intent)
@ -118,10 +117,11 @@ public class ApiTwentySixPlus {
.setCategory(Notification.CATEGORY_MESSAGE) .setCategory(Notification.CATEGORY_MESSAGE)
.setVisibility(Notification.VISIBILITY_PRIVATE) .setVisibility(Notification.VISIBILITY_PRIVATE)
.setPriority(Notification.PRIORITY_HIGH) .setPriority(Notification.PRIORITY_HIGH)
.setNumber(msgCount) .setNumber(notif.getMessages().size())
.setWhen(System.currentTimeMillis()) .setWhen(System.currentTimeMillis())
.setShowWhen(true) .setShowWhen(true)
.setColor(context.getColor(R.color.notification_color_led)) .setColor(context.getColor(R.color.notification_color_led))
.setStyle(style)
.addAction(action) .addAction(action)
.build(); .build();
} }

View file

@ -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. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
import android.app.AlarmManager;
import android.app.FragmentTransaction; import android.app.FragmentTransaction;
import android.app.Notification; import android.app.Notification;
import android.app.PendingIntent; import android.app.PendingIntent;
@ -26,15 +25,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Build; import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings; 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 android.widget.TextView;
import org.linphone.mediastream.Version; import org.linphone.mediastream.Version;
import org.linphone.notifications.Notifiable;
public class Compatibility { public class Compatibility {
public static final String KEY_TEXT_REPLY = "key_text_reply"; 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_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_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_ANSWER_CALL_NOTIF_ACTION = "org.linphone.ANSWER_CALL_ACTION";
public static final String INTENT_LOCAL_IDENTITY = "LOCAL_IDENTITY";
public static void createNotificationChannels(Context context) { public static void createNotificationChannels(Context context) {
if (Version.sdkAboveOrEqual(Version.API26_O_80)) { if (Version.sdkAboveOrEqual(Version.API26_O_80)) {
@ -65,13 +61,13 @@ public class Compatibility {
return ApiTwentyOnePlus.createMissedCallNotification(context, title, text, intent); 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)) { 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)) { } 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) { public static Notification createRepliedNotification(Context context, String reply) {

View file

@ -85,7 +85,7 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener
if (defaultProxyConfig != null) { if (defaultProxyConfig != null) {
ChatRoom room = lc.findOneToOneChatRoom(defaultProxyConfig.getContact(), participant, isSecured); ChatRoom room = lc.findOneToOneChatRoom(defaultProxyConfig.getContact(), participant, isSecured);
if (room != null) { if (room != null) {
LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null);
} else { } else {
if (defaultProxyConfig.getConferenceFactoryUri() != null && (isSecured || !LinphonePreferences.instance().useBasicChatRoomFor1To1())) { if (defaultProxyConfig.getConferenceFactoryUri() != null && (isSecured || !LinphonePreferences.instance().useBasicChatRoomFor1To1())) {
mWaitLayout.setVisibility(View.VISIBLE); mWaitLayout.setVisibility(View.VISIBLE);
@ -96,7 +96,7 @@ public class ContactDetailsFragment extends Fragment implements OnClickListener
mChatRoom.addParticipants(participants); mChatRoom.addParticipants(participants);
} else { } else {
room = lc.getChatRoom(participant); 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) { public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
if (newState == ChatRoom.State.Created) { if (newState == ChatRoom.State.Created) {
mWaitLayout.setVisibility(View.GONE); 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) { } else if (newState == ChatRoom.State.CreationFailed) {
mWaitLayout.setVisibility(View.GONE); mWaitLayout.setVisibility(View.GONE);
LinphoneActivity.instance().displayChatRoomError(); LinphoneActivity.instance().displayChatRoomError();

View file

@ -30,7 +30,7 @@ public class FirebaseIdService extends FirebaseInstanceIdService {
public void onTokenRefresh() { public void onTokenRefresh() {
// Get updated InstanceID token. // Get updated InstanceID token.
final String refreshedToken = FirebaseInstanceId.getInstance().getToken(); 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() { LinphoneUtils.dispatchOnUIThread(new Runnable() {
@Override @Override

View file

@ -36,10 +36,10 @@ public class FirebaseMessaging extends FirebaseMessagingService {
@Override @Override
public void onMessageReceived(RemoteMessage remoteMessage) { public void onMessageReceived(RemoteMessage remoteMessage) {
android.util.Log.i("FirebaseMessaging", "[Push Notification] Received"); android.util.Log.i("FirebaseMessaging", "[Push Notifiable] Received");
if (!LinphoneService.isReady()) { 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)); startService(new Intent(ACTION_MAIN).setClass(this, LinphoneService.class));
} else if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) { } else if (LinphoneManager.isInstanciated() && LinphoneManager.getLc().getCallsNb() == 0) {
LinphoneUtils.dispatchOnUIThread(new Runnable() { LinphoneUtils.dispatchOnUIThread(new Runnable() {

View file

@ -109,7 +109,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener {
public void onStateChanged(ChatRoom cr, ChatRoom.State newState) { public void onStateChanged(ChatRoom cr, ChatRoom.State newState) {
if (newState == ChatRoom.State.Created) { if (newState == ChatRoom.State.Created) {
mWaitLayout.setVisibility(View.GONE); 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) { } else if (newState == ChatRoom.State.CreationFailed) {
mWaitLayout.setVisibility(View.GONE); mWaitLayout.setVisibility(View.GONE);
LinphoneActivity.instance().displayChatRoomError(); LinphoneActivity.instance().displayChatRoomError();
@ -198,7 +198,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener {
Address participant = Factory.instance().createAddress(sipUri); Address participant = Factory.instance().createAddress(sipUri);
ChatRoom room = lc.findOneToOneChatRoom(lc.getDefaultProxyConfig().getContact(), participant, false); ChatRoom room = lc.findOneToOneChatRoom(lc.getDefaultProxyConfig().getContact(), participant, false);
if (room != null) { if (room != null) {
LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null, room.getLocalAddress().asString()); LinphoneActivity.instance().goToChat(room.getPeerAddress().asStringUriOnly(), null);
} else { } else {
ProxyConfig lpc = lc.getDefaultProxyConfig(); ProxyConfig lpc = lc.getDefaultProxyConfig();
if (lpc != null && lpc.getConferenceFactoryUri() != null && !LinphonePreferences.instance().useBasicChatRoomFor1To1()) { if (lpc != null && lpc.getConferenceFactoryUri() != null && !LinphonePreferences.instance().useBasicChatRoomFor1To1()) {
@ -208,7 +208,7 @@ public class HistoryDetailFragment extends Fragment implements OnClickListener {
mChatRoom.addParticipant(participant); mChatRoom.addParticipant(participant);
} else { } else {
room = lc.getChatRoom(participant); 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) { } else if (id == R.id.add_contact) {

View file

@ -1362,9 +1362,9 @@ public class SettingsFragment extends PreferencesListFragment {
boolean value = (Boolean) newValue; boolean value = (Boolean) newValue;
mPrefs.setServiceNotificationVisibility(value); mPrefs.setServiceNotificationVisibility(value);
if (value) { if (value) {
LinphoneService.instance().showServiceNotification(); LinphoneService.instance().getNotificationManager().startForeground();
} else { } else {
LinphoneService.instance().hideServiceNotification(); LinphoneService.instance().getNotificationManager().stopForeground();
} }
return true; return true;
} }

View file

@ -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<NotifiableMessage> 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<NotifiableMessage> 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;
}
}

View file

@ -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;
}
}

View file

@ -1,4 +1,4 @@
package org.linphone.receivers; package org.linphone.notifications;
/* /*
NotificationBroadcastReceiver.java NotificationBroadcastReceiver.java
@ -29,48 +29,82 @@ import android.os.Bundle;
import org.linphone.LinphoneManager; import org.linphone.LinphoneManager;
import org.linphone.LinphoneService; import org.linphone.LinphoneService;
import org.linphone.R;
import org.linphone.activities.LinphoneActivity; import org.linphone.activities.LinphoneActivity;
import org.linphone.compatibility.Compatibility; import org.linphone.compatibility.Compatibility;
import org.linphone.core.Address; import org.linphone.core.Address;
import org.linphone.core.Call; import org.linphone.core.Call;
import org.linphone.core.ChatMessage; import org.linphone.core.ChatMessage;
import org.linphone.core.ChatMessageListenerStub;
import org.linphone.core.ChatRoom; import org.linphone.core.ChatRoom;
import org.linphone.core.Core; import org.linphone.core.Core;
import org.linphone.core.ProxyConfig; import org.linphone.core.ProxyConfig;
import org.linphone.core.Reason; import org.linphone.mediastream.Log;
public class NotificationBroadcastReceiver extends BroadcastReceiver { public class NotificationBroadcastReceiver extends BroadcastReceiver {
@Override @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) { if (intent.getAction() == Compatibility.INTENT_REPLY_NOTIF_ACTION) {
String reply = getMessageText(intent).toString(); final String reply = getMessageText(intent).toString();
if (reply == null) return; if (reply == null) {
Notification replied = Compatibility.createRepliedNotification(context, reply); Log.e("Couldn't get reply text");
if (replied == null) return; onError(context, notifId);
int notifId = intent.getIntExtra(Compatibility.INTENT_NOTIF_ID, 0); return;
String remoteSipAddr = LinphoneService.instance().getSipUriForNotificationId(notifId); }
String remoteSipAddr = LinphoneService.instance().getNotificationManager().getSipUriForNotificationId(notifId);
Core core = LinphoneManager.getLc(); Core core = LinphoneManager.getLc();
if (core == null) return; if (core == null) {
ProxyConfig proxyConfig = core.getDefaultProxyConfig(); Log.e("Couldn't get Core instance");
if (proxyConfig == null) return; onError(context, notifId);
Address localAddr = proxyConfig.getIdentityAddress(); return;
}
Address remoteAddr = core.interpretUrl(remoteSipAddr); 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); 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(); room.markAsRead();
if (LinphoneActivity.isInstanciated()) { if (LinphoneActivity.isInstanciated()) {
LinphoneActivity.instance().displayMissedChats(LinphoneManager.getInstance().getUnreadMessageCount()); LinphoneActivity.instance().displayMissedChats(LinphoneManager.getInstance().getUnreadMessageCount());
} }
ChatMessage msg = room.createMessage(reply); ChatMessage msg = room.createMessage(reply);
msg.send(); msg.send();
msg.setListener(new ChatMessageListenerStub() {
LinphoneService.instance().sendNotification(replied, notifId); @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) { } 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().getNotificationManager().getSipUriForCallNotificationId(notifId);
String remoteAddr = LinphoneService.instance().getSipUriForCallNotificationId(callId);
Core core = LinphoneManager.getLc(); Core core = LinphoneManager.getLc();
if (core == null) return; 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) @TargetApi(20)
private CharSequence getMessageText(Intent intent) { private CharSequence getMessageText(Intent intent) {
Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);

View file

@ -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<String, Notifiable> 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);
}
}