package com.moez.QKSMS.transaction; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.PowerManager; import android.preference.PreferenceManager; import android.provider.Telephony; import android.support.v4.app.NotificationCompat; import android.text.Html; import android.util.Log; import com.android.mms.transaction.TransactionService; import com.android.mms.transaction.TransactionState; import com.google.android.mms.pdu_alt.PduHeaders; import com.moez.QKSMS.R; import com.moez.QKSMS.common.ConversationPrefsHelper; import com.moez.QKSMS.common.utils.ImageUtils; import com.moez.QKSMS.data.Contact; import com.moez.QKSMS.data.ContactHelper; import com.moez.QKSMS.data.Message; import com.moez.QKSMS.model.ImageModel; import com.moez.QKSMS.model.SlideshowModel; import com.moez.QKSMS.receiver.RemoteMessagingReceiver; import com.moez.QKSMS.ui.MainActivity; import com.moez.QKSMS.ui.ThemeManager; import com.moez.QKSMS.ui.dialog.DefaultSmsHelper; import com.moez.QKSMS.ui.messagelist.MessageItem; import com.moez.QKSMS.ui.messagelist.MessageListActivity; import com.moez.QKSMS.ui.popup.QKComposeActivity; import com.moez.QKSMS.ui.popup.QKReplyActivity; import com.moez.QKSMS.ui.settings.SettingsFragment; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Set; public class NotificationManager { private static final String TAG = "NotificationManager"; private static final int NOTIFICATION_ID_QUICKCOMPOSE = 4516; private static final int NOTIFICATION_ID_FAILED = 4295; public static final String ACTION_MARK_READ = "com.moez.QKSMS.MARK_READ"; public static final String ACTION_MARK_SEEN = "com.moez.QKSMS.MARK_SEEN"; private static final String DEFAULT_RINGTONE = "content://settings/system/notification_sound"; private static final String PREV_NOTIFICATIONS = "key_prev_notifications"; private static final long[] VIBRATION = {0, 200, 200, 200}; private static final long[] VIBRATION_SILENT = {0, 0}; private static HandlerThread sThread; private static Looper sLooper; private static Handler sHandler; private static SharedPreferences sPrefs; private static Resources sRes; static { // Start a new thread for showing notifications on with minimum priority sThread = new HandlerThread("NotificationManager"); sThread.start(); sThread.setPriority(HandlerThread.MIN_PRIORITY); sLooper = sThread.getLooper(); sHandler = new Handler(sLooper); } public static void init(final Context context) { // Initialize the static shared prefs and resources. sPrefs = PreferenceManager.getDefaultSharedPreferences(context); sRes = context.getResources(); // Listen for MMS events. IntentFilter filter = new IntentFilter(TransactionService.TRANSACTION_COMPLETED_ACTION); context.registerReceiver(sBroadcastReceiver, filter); } private static BroadcastReceiver sBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO this should happen on a background thread because it requires a DB query int result = intent.getIntExtra(TransactionService.STATE, TransactionState.FAILED); Uri uri = intent.getParcelableExtra(TransactionService.STATE_URI); if (uri != null) { // Get the message type int msgType = -1; Cursor c = null; try { c = SqliteWrapper.query(context, context.getContentResolver(), uri, new String[]{Telephony.Mms.MESSAGE_TYPE}, null, null, null ); if (c.moveToFirst()) { msgType = c.getInt(c.getColumnIndex(Telephony.Mms.MESSAGE_TYPE)); } } finally { if (c != null) c.close(); } // For successful retrieve messages, show a notification! if (result == TransactionState.SUCCESS && msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF) { create(context); } else { update(context); } } } }; /** * Creates a new notification, called when a new message is received. This notification will have sound and * vibration */ public static void create(final Context context) { if (sPrefs.getBoolean(SettingsFragment.NOTIFICATIONS, true)) { sHandler.post(new Runnable() { @Override public void run() { HashMap<Long, ArrayList<MessageItem>> conversations = SmsHelper.getUnreadUnseenConversations(context); // Let's find the list of current notifications. If we're showing multiple notifications, now we know // which ones don't need to be touched Set<Long> oldThreads = new HashSet<>(); for (String s : sPrefs.getStringSet(PREV_NOTIFICATIONS, new HashSet<String>())) { long l = Long.parseLong(s); if (!oldThreads.contains(l)) { oldThreads.add(l); } } dismissOld(context, conversations); // If there are no messages, don't try to create a notification if (conversations.size() == 0) { return; } ArrayList<MessageItem> lastConversation = conversations.get(conversations.keySet().toArray()[0]); MessageItem lastMessage = lastConversation.get(0); // If this message is in the foreground, mark it as read Message message = new Message(context, lastMessage.mMsgId); if (MessageListActivity.isInForeground && message.getThreadId() == MessageListActivity.getThreadId()) { message.markRead(); return; } long threadId = (long) conversations.keySet().toArray()[0]; ConversationPrefsHelper conversationPrefs = new ConversationPrefsHelper(context, threadId); if (!conversationPrefs.getNotificationsEnabled()) { return; } // Otherwise, reset the state and show the notification. NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) .setPriority(getNotificationPriority(context)) .setSound(conversationPrefs.getNotificationSoundUri()) .setVibrate(VIBRATION_SILENT) .setAutoCancel(true); if (conversationPrefs.getVibrateEnabled()) { builder.setVibrate(VIBRATION); } if (conversationPrefs.getNotificationLedEnabled()) { builder.setLights(getLedColor(conversationPrefs), 1000, 1000); } Integer privateNotifications = conversationPrefs.getPrivateNotificationsSetting(); if (conversationPrefs.getTickerEnabled()) { switch (privateNotifications) { case 0: builder.setTicker(String.format("%s: %s", lastMessage.mContact, lastMessage.mBody)); break; case 1: builder.setTicker(String.format("%s: %s", lastMessage.mContact, sRes.getString(R.string.new_message))); break; case 2: builder.setTicker(String.format("%s: %s", "QKSMS", sRes.getString(R.string.new_message))); break; } } if (conversationPrefs.getWakePhoneEnabled()) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "FlashActivity"); wl.acquire(); wl.release(); } if (conversations.size() == 1 && lastConversation.size() == 1) { singleMessage(context, lastConversation, threadId, builder, conversationPrefs, privateNotifications); } else if (conversations.size() == 1) { singleSender(context, lastConversation, threadId, builder, conversationPrefs, privateNotifications); } else { multipleSenders(context, conversations, oldThreads, builder); } } }); } } /** * Updates the notifications silently. This is called when a conversation is marked read or something like that, * where we need to update the notifications without alerting the user */ public static void update(final Context context) { sHandler.post(new Runnable() { @Override public void run() { HashMap<Long, ArrayList<MessageItem>> conversations = SmsHelper.getUnreadUnseenConversations(context); // Let's find the list of current notifications. If we're showing multiple notifications, now we know // which ones don't need to be touched Set<Long> oldThreads = new HashSet<>(); for (String s : sPrefs.getStringSet(PREV_NOTIFICATIONS, new HashSet<String>())) { long l = Long.parseLong(s); if (!oldThreads.contains(l)) { oldThreads.add(l); } } dismissOld(context, conversations); // If there are no messages, don't try to create a notification // If this app is not default message app, don't try to create a notification either if (conversations.size() == 0 || !new DefaultSmsHelper(context, 0).isDefault()) { return; } ArrayList<MessageItem> lastConversation = conversations.get(conversations.keySet().toArray()[0]); MessageItem lastMessage = lastConversation.get(0); // If the message is visible (i.e. it is currently showing in the Main Activity), // don't show a notification; just mark it as read and return. Message message = new Message(context, lastMessage.mMsgId); if (MessageListActivity.isInForeground && message.getThreadId() == MessageListActivity.getThreadId()) { message.markRead(); return; } long threadId = (long) conversations.keySet().toArray()[0]; ConversationPrefsHelper conversationPrefs = new ConversationPrefsHelper(context, threadId); NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification) // SMS messages are high priority .setPriority(getNotificationPriority(context)) // Silent here because this is just an update, not a new // notification .setSound(null) .setVibrate(VIBRATION_SILENT) .setAutoCancel(true); if (conversationPrefs.getNotificationLedEnabled()) { builder.setLights(getLedColor(conversationPrefs), 1000, 1000); } Integer privateNotifications = conversationPrefs.getPrivateNotificationsSetting(); if (conversations.size() == 1 && lastConversation.size() == 1) { singleMessage(context, lastConversation, threadId, builder, conversationPrefs, privateNotifications); } else if (conversations.size() == 1) { singleSender(context, lastConversation, threadId, builder, conversationPrefs, privateNotifications); } else { multipleSenders(context, conversations, oldThreads, builder); } } }); } /** * Creates a notification to tell the user about failed messages. This is currently pretty shitty and needs to be * improved, by adding functionality such as the ability to delete all of the failed messages */ public static void notifyFailed(final Context context) { sHandler.post(new Runnable() { public void run() { Cursor failedCursor = context.getContentResolver().query( SmsHelper.SMS_CONTENT_PROVIDER, new String[]{SmsHelper.COLUMN_THREAD_ID}, SmsHelper.FAILED_SELECTION, null, null ); // Dismiss the notification if the failed cursor doesn't have any items in it. if (failedCursor == null || !failedCursor.moveToFirst() || failedCursor.getCount() <= 0) { dismiss(context, NOTIFICATION_ID_FAILED); return; } String title; PendingIntent PI; if (failedCursor.getCount() == 1) { title = sRes.getString(R.string.failed_message); Intent intent = new Intent(context, MainActivity.class); intent.putExtra(MessageListActivity.ARG_THREAD_ID, failedCursor.getLong(0)); PI = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } else { title = failedCursor.getCount() + " " + sRes.getString(R.string.failed_messages); Intent intent = new Intent(context, MainActivity.class); PI = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); for (Message message : SmsHelper.getFailedMessages(context)) { switch (Integer.parseInt(sPrefs.getString(SettingsFragment.PRIVATE_NOTIFICATION, "0"))) { case 0: inboxStyle.addLine(Html.fromHtml("<strong>" + message.getName() + "</strong> " + message.getBody())); break; case 1: inboxStyle.addLine(Html.fromHtml("<strong>" + message.getName() + "</strong> " + sRes.getString(R.string.new_message))); break; case 2: inboxStyle.addLine(Html.fromHtml("<strong>" + "QKSMS" + "</strong> " + sRes.getString(R.string.new_message))); break; } } NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setSmallIcon(R.drawable.ic_notification_failed) .setPriority(NotificationCompat.PRIORITY_HIGH) .setSound(Uri.parse(sPrefs.getString(SettingsFragment.NOTIFICATION_TONE, DEFAULT_RINGTONE))) .setVibrate(VIBRATION_SILENT) .setAutoCancel(true) .setContentTitle(title) .setStyle(inboxStyle) .setContentText(sRes.getString(R.string.failed_messages_summary)) .setContentIntent(PI) .setNumber(failedCursor.getCount()) .setColor(ThemeManager.getThemeColor()); if (sPrefs.getBoolean(SettingsFragment.NOTIFICATION_VIBRATE, false)) { builder.setVibrate(VIBRATION); } if (sPrefs.getBoolean(SettingsFragment.NOTIFICATION_LED, true)) { builder.setLights(getLedColor(new ConversationPrefsHelper(context, 0)), 1000, 1000); } if (sPrefs.getBoolean(SettingsFragment.NOTIFICATION_TICKER, false)) { builder.setTicker(title); } NotificationManager.notify(context, NOTIFICATION_ID_FAILED, builder.build()); } }); } /** * Notifies the user of the given notification. */ public static void notify(Context context, int id, android.app.Notification notification) { ((android.app.NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) .notify(id, notification); } /** * Cancels the notification for the given ID. */ public static void dismiss(Context context, int id) { // Cancel the notification for this ID. ((android.app.NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE)) .cancel(id); } /** * Dismisses all old notifications. The purpose of this is to clear notifications that don't need to show up, * without making the remaining ones dissapear and pop up again like how NotificationMangager.cancelAll and then * rebuilding them would do * <p> * This should stay private, because it assumes that the preferences have already been initialized */ private static void dismissOld(Context context, HashMap<Long, ArrayList<MessageItem>> newMessages) { // Let's find the list of current notifications Set<Long> oldThreads = new HashSet<>(); for (String s : sPrefs.getStringSet(PREV_NOTIFICATIONS, new HashSet<String>())) { long l = Long.parseLong(s); if (!oldThreads.contains(l)) { oldThreads.add(l); } } // Now we need a comparable list of thread ids for the new messages Set<Long> newThreads = newMessages.keySet(); Log.d(TAG, "Old threads: " + Arrays.toString(oldThreads.toArray())); Log.d(TAG, "New threads: " + Arrays.toString(newThreads.toArray())); // For all of the notifications that exist and are not to be still shown, let's dismiss them for (long threadId : oldThreads) { if (!newThreads.contains(threadId)) { dismiss(context, (int) threadId); } } // Now let's convert the new list into a set of strings so we can save them to prefs Set<String> newThreadStrings = new HashSet<>(); for (long threadId : newThreads) { newThreadStrings.add(Long.toString(threadId)); } sPrefs.edit().putStringSet(PREV_NOTIFICATIONS, newThreadStrings).apply(); } /** * Displays a notification for a single message */ private static void singleMessage(final Context context, final ArrayList<MessageItem> messages, final long threadId, final NotificationCompat.Builder builder, final ConversationPrefsHelper conversationPrefs, final Integer privateNotifications) { MessageItem message = messages.get(0); if (message.isMms()) { Log.d(TAG, "Message is MMS"); if (message.mSlideshow != null) { Log.d(TAG, "Woah! Slideshow not null"); buildSingleMessageNotification(context, messages, threadId, builder, conversationPrefs, privateNotifications); } else { Log.d(TAG, "Listening for PDU"); message.setOnPduLoaded(new MessageItem.PduLoadedCallback() { @Override public void onPduLoaded(MessageItem messageItem) { Log.d(TAG, "PDU Loaded"); buildSingleMessageNotification(context, messages, threadId, builder, conversationPrefs, privateNotifications); } }); } } else { buildSingleMessageNotification(context, messages, threadId, builder, conversationPrefs, privateNotifications); } } /** * Builds the actual notification for the single message. This code can be called at different points in execution * depending on whether or not the MMS data has been downloaded */ private static void buildSingleMessageNotification(final Context context, ArrayList<MessageItem> messages, long threadId, final NotificationCompat.Builder builder, ConversationPrefsHelper conversationPrefs, final Integer privateNotifications) { MessageItem message = messages.get(0); Intent threadIntent = new Intent(context, MainActivity.class); threadIntent.putExtra(MessageListActivity.ARG_THREAD_ID, threadId); final PendingIntent threadPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 1), threadIntent, PendingIntent.FLAG_UPDATE_CURRENT); Intent readIntent = new Intent(ACTION_MARK_READ); readIntent.putExtra("thread_id", threadId); final PendingIntent readPI = PendingIntent.getBroadcast(context, buildRequestCode(threadId, 2), readIntent, PendingIntent.FLAG_UPDATE_CURRENT); Intent seenIntent = new Intent(ACTION_MARK_SEEN); final PendingIntent seenPI = PendingIntent.getBroadcast(context, buildRequestCode(threadId, 4), seenIntent, PendingIntent.FLAG_UPDATE_CURRENT); int unreadMessageCount = SmsHelper.getUnreadMessageCount(context); String body; String title; NotificationCompat.Style nstyle = null; switch (privateNotifications) { case 0: //Hide nothing body = message.mBody; title = message.mContact; nstyle = new NotificationCompat.BigTextStyle().bigText(message.mBody); break; case 1: //Hide message body = sRes.getString(R.string.new_message); title = message.mContact; break; case 2: //Hide sender & message body = sRes.getString(R.string.new_message); title = "QKSMS"; break; default: body = message.mBody; title = message.mContact; nstyle = null; } builder.setContentTitle(title) .setContentText(body) .setLargeIcon(getLargeIcon(context, Contact.get(message.mAddress, false), privateNotifications)) .setContentIntent(threadPI) .setNumber(unreadMessageCount) .setStyle(nstyle) .setColor(ThemeManager.getColor()) .addAction(R.drawable.ic_accept, sRes.getString(R.string.read), readPI) .extend(RemoteMessagingReceiver.getConversationExtender(context, message.mContact, message.mAddress, threadId)) .setDeleteIntent(seenPI); if (Build.VERSION.SDK_INT < 24) { Intent replyIntent = new Intent(context, QKReplyActivity.class); replyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); replyIntent.putExtra(QKReplyActivity.EXTRA_THREAD_ID, threadId); replyIntent.putExtra(QKReplyActivity.EXTRA_SHOW_KEYBOARD, true); PendingIntent replyPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 0), replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_reply, sRes.getString(R.string.reply), replyPI); } else { builder.addAction(RemoteMessagingReceiver.getReplyAction(context, message.mAddress, threadId)); } if (conversationPrefs.getDimissedReadEnabled()) { builder.setDeleteIntent(readPI); } if (conversationPrefs.getCallButtonEnabled()) { Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:" + message.mAddress)); PendingIntent callPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 3), callIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_call, sRes.getString(R.string.call), callPI); } if (message.isMms()) { Log.d(TAG, "Message is MMS"); SlideshowModel model = message.mSlideshow; if (model != null && model.isSimple() && model.get(0).getImage() != null) { Log.d(TAG, "MMS type: image"); ImageModel imageModel = model.get(0).getImage(); Bitmap image = imageModel.getBitmap(imageModel.getWidth(), imageModel.getHeight()); NotificationCompat.BigPictureStyle style = new NotificationCompat.BigPictureStyle() .setBigContentTitle(message.mContact) .setSummaryText(message.mBody) .bigLargeIcon(getLargeIcon(context, Contact.get(message.mAddress, false), privateNotifications)) .bigPicture(image); if (privateNotifications == 0) builder.setStyle(style); else builder.setStyle(null); } else { Log.d(TAG, "MMS Type: not an image lol"); if (privateNotifications == 0) builder.setStyle(new NotificationCompat.BigTextStyle().bigText(message.mBody)); else builder.setStyle(null); } } NotificationManager.notify(context, (int) threadId, builder.build()); } /** * Creates a notification that contains several messages that are all part of the same conversation */ private static void singleSender(final Context context, ArrayList<MessageItem> messages, long threadId, final NotificationCompat.Builder builder, ConversationPrefsHelper conversationPrefs, final Integer privateNotifications) { MessageItem message = messages.get(0); Intent threadIntent = new Intent(context, MainActivity.class); threadIntent.putExtra(MessageListActivity.ARG_THREAD_ID, threadId); PendingIntent threadPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 1), threadIntent, PendingIntent.FLAG_UPDATE_CURRENT); Intent readIntent = new Intent(ACTION_MARK_READ); readIntent.putExtra("thread_id", threadId); PendingIntent readPI = PendingIntent.getBroadcast(context, buildRequestCode(threadId, 2), readIntent, PendingIntent.FLAG_UPDATE_CURRENT); Intent seenIntent = new Intent(ACTION_MARK_SEEN); PendingIntent seenPI = PendingIntent.getBroadcast(context, buildRequestCode(threadId, 4), seenIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle(); for (MessageItem message1 : messages) { inboxStyle.addLine(message1.mBody); } String notificationTitle = message.mContact; if (!(privateNotifications == 0)) inboxStyle = null; if (privateNotifications == 2) notificationTitle = "QKSMS"; int unreadMessageCount = SmsHelper.getUnreadMessageCount(context); builder.setContentTitle(notificationTitle) .setContentText(SmsHelper.getUnseenSMSCount(context, threadId) + " " + sRes.getString(R.string.new_messages)) .setLargeIcon(getLargeIcon(context, Contact.get(message.mAddress, false), privateNotifications)) .setContentIntent(threadPI) .setNumber(unreadMessageCount) .setStyle(inboxStyle) .setColor(ThemeManager.getColor()) .addAction(R.drawable.ic_accept, sRes.getString(R.string.read), readPI) .extend(RemoteMessagingReceiver.getConversationExtender(context, message.mContact, message.mAddress, threadId)) .setDeleteIntent(seenPI); if (Build.VERSION.SDK_INT < 24) { Intent replyIntent = new Intent(context, QKReplyActivity.class); replyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); replyIntent.putExtra(QKReplyActivity.EXTRA_THREAD_ID, threadId); replyIntent.putExtra(QKReplyActivity.EXTRA_SHOW_KEYBOARD, true); PendingIntent replyPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 0), replyIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_reply, sRes.getString(R.string.reply), replyPI); } else { builder.addAction(RemoteMessagingReceiver.getReplyAction(context, message.mAddress, threadId)); } if (conversationPrefs.getCallButtonEnabled()) { Intent callIntent = new Intent(Intent.ACTION_CALL); callIntent.setData(Uri.parse("tel:" + message.mAddress)); PendingIntent callPI = PendingIntent.getActivity(context, buildRequestCode(threadId, 3), callIntent, PendingIntent.FLAG_UPDATE_CURRENT); builder.addAction(R.drawable.ic_call, sRes.getString(R.string.call), callPI); } notify(context, (int) threadId, builder.build()); } /** * Creates a unique action ID for notification actions (Open, Mark read, Call, etc) */ private static int buildRequestCode(long threadId, int action) { action++; // Fixes issue on some 4.3 phones | http://stackoverflow.com/questions/19031861/pendingintent-not-opening-activity-in-android-4-3 return (int) (action * 100000 + threadId); } /** * Create notifications for multiple conversations */ private static void multipleSenders(Context context, HashMap<Long, ArrayList<MessageItem>> conversations, Set<Long> oldThreads, NotificationCompat.Builder builder) { Set<Long> threadIds = conversations.keySet(); for (long threadId : threadIds) { if (!oldThreads.contains(threadId)) { ConversationPrefsHelper conversationPrefs = new ConversationPrefsHelper(context, threadId); Integer privateNotification = conversationPrefs.getPrivateNotificationsSetting(); if (conversations.get(threadId).size() == 1) { singleMessage(context, conversations.get(threadId), threadId, copyBuilder(builder), conversationPrefs, privateNotification); } else { singleSender(context, conversations.get(threadId), threadId, copyBuilder(builder), conversationPrefs, privateNotification); } } } } /** * Creates a clone of the NotificationCompat.Builder to be used when we're displaying multiple notifications, * and need multiple instances of the builder */ private static NotificationCompat.Builder copyBuilder(NotificationCompat.Builder builder) { return new NotificationCompat.Builder(builder.mContext) .setSmallIcon(builder.mNotification.icon) .setPriority(builder.mNotification.priority) .setSound(builder.mNotification.sound) .setVibrate(builder.mNotification.vibrate) .setLights(builder.mNotification.ledARGB, builder.mNotification.ledOnMS, builder.mNotification.ledOffMS) .setTicker(builder.mNotification.tickerText) .setAutoCancel(true); } /** * Set up the QK Compose notification * * @param override If true, then show the QK Compose notification regardless of the user's preference * @param overrideCancel If true, dismiss the notification no matter what */ public static void initQuickCompose(Context context, boolean override, boolean overrideCancel) { if (sPrefs == null) { init(context); } if (sPrefs.getBoolean(SettingsFragment.QUICKCOMPOSE, false) || override) { Intent composeIntent = new Intent(context, QKComposeActivity.class); PendingIntent composePI = PendingIntent.getActivity(context, 9, composeIntent, PendingIntent.FLAG_UPDATE_CURRENT); NotificationCompat.Builder builder = new NotificationCompat.Builder(context) .setContentTitle(sRes.getString(R.string.quickcompose)) .setContentText(sRes.getString(R.string.quickcompose_detail)) .setOngoing(true) .setContentIntent(composePI) .setSmallIcon(R.drawable.ic_compose) .setPriority(NotificationCompat.PRIORITY_MIN) .setColor(ThemeManager.getColor()); NotificationManager.notify(context, NOTIFICATION_ID_QUICKCOMPOSE, builder.build()); } else { dismiss(context, NOTIFICATION_ID_QUICKCOMPOSE); } if (overrideCancel) { dismiss(context, NOTIFICATION_ID_QUICKCOMPOSE); } } private static int getLedColor(ConversationPrefsHelper conversationPrefs) { int color = Integer.parseInt(conversationPrefs.getNotificationLedColor()); if (color == sRes.getColor(R.color.blue_light) || color == sRes.getColor(R.color.blue_dark)) return sRes.getColor(R.color.blue_dark); if (color == sRes.getColor(R.color.purple_light) || color == sRes.getColor(R.color.purple_dark)) return sRes.getColor(R.color.purple_dark); if (color == sRes.getColor(R.color.green_light) || color == sRes.getColor(R.color.green_dark)) return sRes.getColor(R.color.green_dark); if (color == sRes.getColor(R.color.yellow_light) || color == sRes.getColor(R.color.yellow_dark)) return sRes.getColor(R.color.yellow_dark); if (color == sRes.getColor(R.color.red_light) || color == sRes.getColor(R.color.red_dark)) return sRes.getColor(R.color.red_dark); return sRes.getColor(R.color.white_pure); } /** * Retreives the avatar to be used for the notification's large icon. If the user is running Lollipop, then let's * crop their avatar to a circle */ private static Bitmap getLargeIcon(Context context, Contact contact, Integer privateNotification) { Drawable avatarDrawable = contact.getAvatar(context, new BitmapDrawable(sRes, ContactHelper.blankContact(context, contact.getName()))); int idealIconWidth = sRes.getDimensionPixelSize(android.R.dimen.notification_large_icon_width); int idealIconHeight = sRes.getDimensionPixelSize(android.R.dimen.notification_large_icon_height); Bitmap bitmap = Bitmap.createScaledBitmap(((BitmapDrawable) avatarDrawable).getBitmap(), idealIconWidth, idealIconHeight, true); if (privateNotification == 2) { return null; } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return ImageUtils.getCircleBitmap(bitmap, idealIconWidth); } else { return bitmap; } } } /** * Returns the notification priority we should be using based on whether or not the Heads-up notification should * show */ private static int getNotificationPriority(Context context) { boolean qkreplyEnabled = PreferenceManager.getDefaultSharedPreferences(context) .getBoolean(SettingsFragment.QUICKREPLY, Build.VERSION.SDK_INT < 24); if (qkreplyEnabled) { return NotificationCompat.PRIORITY_DEFAULT; } else { return NotificationCompat.PRIORITY_HIGH; } } }