forked from upstream-mirrors/quik
268 lines
11 KiB
Kotlin
268 lines
11 KiB
Kotlin
/*
|
|
* Copyright (C) 2017 Moez Bhatti <moez.bhatti@gmail.com>
|
|
*
|
|
* This file is part of QKSMS.
|
|
*
|
|
* QKSMS 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 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* QKSMS 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 QKSMS. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package common.util
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.NotificationChannel
|
|
import android.app.NotificationManager
|
|
import android.app.PendingIntent
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.graphics.Color
|
|
import android.net.Uri
|
|
import android.os.Build
|
|
import android.support.v4.app.NotificationCompat
|
|
import android.support.v4.app.NotificationManagerCompat
|
|
import android.support.v4.app.RemoteInput
|
|
import android.support.v4.app.TaskStackBuilder
|
|
import com.moez.QKSMS.R
|
|
import feature.compose.ComposeActivity
|
|
import feature.qkreply.QkReplyActivity
|
|
import receiver.MarkReadReceiver
|
|
import receiver.MarkSeenReceiver
|
|
import receiver.RemoteMessagingReceiver
|
|
import repository.MessageRepository
|
|
import util.Preferences
|
|
import javax.inject.Inject
|
|
import javax.inject.Singleton
|
|
|
|
@Singleton
|
|
class NotificationManagerImpl @Inject constructor(
|
|
private val context: Context,
|
|
private val prefs: Preferences,
|
|
private val colors: Colors,
|
|
private val messageRepo: MessageRepository
|
|
) : manager.NotificationManager {
|
|
|
|
companion object {
|
|
const val DEFAULT_CHANNEL_ID = "notifications_default"
|
|
val VIBRATE_PATTERN = longArrayOf(0, 200, 0, 200)
|
|
}
|
|
|
|
private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
|
|
init {
|
|
@SuppressLint("NewApi")
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
val name = "Default"
|
|
val importance = NotificationManager.IMPORTANCE_HIGH
|
|
val channel = NotificationChannel(DEFAULT_CHANNEL_ID, name, importance).apply {
|
|
enableLights(true)
|
|
lightColor = Color.WHITE
|
|
enableVibration(true)
|
|
vibrationPattern = VIBRATE_PATTERN
|
|
}
|
|
|
|
notificationManager.createNotificationChannel(channel)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the notification for a particular conversation
|
|
*/
|
|
override fun update(threadId: Long) {
|
|
// If notifications are disabled, don't do anything
|
|
if (!prefs.notifications(threadId).get()) {
|
|
return
|
|
}
|
|
|
|
val messages = messageRepo.getUnreadUnseenMessages(threadId)
|
|
|
|
// If there are no messages to be displayed, make sure that the notification is dismissed
|
|
if (messages.isEmpty()) {
|
|
notificationManager.cancel(threadId.toInt())
|
|
return
|
|
}
|
|
|
|
val conversation = messageRepo.getConversation(threadId) ?: return
|
|
|
|
// If QK Reply is enabled, use the default importance so that we don't have a notification
|
|
// card in addition to the popup. Otherwise, use max importance
|
|
val importance = when (prefs.qkreply.get()) {
|
|
true -> NotificationManagerCompat.IMPORTANCE_DEFAULT
|
|
false -> NotificationManagerCompat.IMPORTANCE_MAX
|
|
}
|
|
|
|
val style = NotificationCompat.MessagingStyle("Me")
|
|
messages.forEach { message ->
|
|
val name = if (message.isMe()) null else conversation.getTitle()
|
|
style.addMessage(message.getSummary(), message.date, name)
|
|
}
|
|
|
|
val contentIntent = Intent(context, ComposeActivity::class.java).putExtra("threadId", threadId)
|
|
val taskStackBuilder = TaskStackBuilder.create(context)
|
|
taskStackBuilder.addParentStack(ComposeActivity::class.java)
|
|
taskStackBuilder.addNextIntent(contentIntent)
|
|
val contentPI = taskStackBuilder.getPendingIntent(threadId.toInt() + 10000, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
val seenIntent = Intent(context, MarkSeenReceiver::class.java).putExtra("threadId", threadId)
|
|
val seenPI = PendingIntent.getBroadcast(context, threadId.toInt() + 20000, seenIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
val readIntent = Intent(context, MarkReadReceiver::class.java).putExtra("threadId", threadId)
|
|
val readPI = PendingIntent.getBroadcast(context, threadId.toInt() + 30000, readIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
val readAction = NotificationCompat.Action(R.drawable.ic_done_black_24dp, context.getString(R.string.notification_read), readPI)
|
|
|
|
val notification = NotificationCompat.Builder(context, getChannelIdForNotification(threadId))
|
|
.setColor(colors.themeForConversation(threadId).blockingFirst())
|
|
.setPriority(importance)
|
|
.setSmallIcon(R.drawable.ic_notification)
|
|
.setNumber(messages.size)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(contentPI)
|
|
.setDeleteIntent(seenPI)
|
|
.addAction(readAction)
|
|
.setStyle(style)
|
|
.setSound(Uri.parse(prefs.ringtone(threadId).get()))
|
|
.setLights(Color.WHITE, 500, 2000)
|
|
.setVibrate(if (prefs.vibration(threadId).get()) VIBRATE_PATTERN else longArrayOf(0))
|
|
|
|
if (Build.VERSION.SDK_INT >= 24) {
|
|
notification.addAction(getReplyAction(conversation.recipients[0]?.address.orEmpty(), threadId))
|
|
}
|
|
|
|
if (prefs.qkreply.get()) {
|
|
val intent = Intent(context, QkReplyActivity::class.java)
|
|
.putExtra("threadId", threadId)
|
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
|
context.startActivity(intent)
|
|
}
|
|
|
|
notificationManager.notify(threadId.toInt(), notification.build())
|
|
}
|
|
|
|
override fun notifyFailed(msgId: Long) {
|
|
val message = messageRepo.getMessage(msgId)
|
|
|
|
if (message == null || !message.isFailedMessage()) {
|
|
return
|
|
}
|
|
|
|
val conversation = message.conversation ?: return
|
|
val threadId = conversation.id
|
|
|
|
val contentIntent = Intent(context, ComposeActivity::class.java).putExtra("threadId", threadId)
|
|
val taskStackBuilder = TaskStackBuilder.create(context)
|
|
taskStackBuilder.addParentStack(ComposeActivity::class.java)
|
|
taskStackBuilder.addNextIntent(contentIntent)
|
|
val contentPI = taskStackBuilder.getPendingIntent(threadId.toInt() + 40000, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
val notification = NotificationCompat.Builder(context, getChannelIdForNotification(threadId))
|
|
.setContentTitle(context.getString(R.string.notification_message_failed_title))
|
|
.setContentText(context.getString(R.string.notification_message_failed_text, conversation.getTitle()))
|
|
.setColor(colors.themeForConversation(threadId).blockingFirst())
|
|
.setPriority(NotificationManagerCompat.IMPORTANCE_MAX)
|
|
.setSmallIcon(R.drawable.ic_notification_failed)
|
|
.setAutoCancel(true)
|
|
.setContentIntent(contentPI)
|
|
.setSound(Uri.parse(prefs.ringtone(threadId).get()))
|
|
.setLights(Color.WHITE, 500, 2000)
|
|
.setVibrate(if (prefs.vibration(threadId).get()) VIBRATE_PATTERN else longArrayOf(0))
|
|
|
|
notificationManager.notify(threadId.toInt() + 50000, notification.build())
|
|
}
|
|
|
|
private fun getReplyAction(address: String, threadId: Long): NotificationCompat.Action {
|
|
val replyIntent = Intent(context, RemoteMessagingReceiver::class.java)
|
|
.putExtra("address", address)
|
|
.putExtra("threadId", threadId)
|
|
val replyPI = PendingIntent.getBroadcast(context, threadId.toInt() + 40000, replyIntent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
val responseSet = arrayListOf("Hello", "Hey").toTypedArray().sortedArray()
|
|
val remoteInput = RemoteInput.Builder("body")
|
|
.setLabel(context.getString(R.string.notification_reply))
|
|
.setChoices(responseSet)
|
|
.build()
|
|
|
|
return NotificationCompat.Action.Builder(
|
|
R.drawable.ic_reply_black_24dp,
|
|
context.getString(R.string.notification_reply), replyPI)
|
|
.addRemoteInput(remoteInput)
|
|
.build()
|
|
}
|
|
|
|
/**
|
|
* Creates a notification channel for the given conversation
|
|
*/
|
|
override fun createNotificationChannel(threadId: Long) {
|
|
|
|
// Only proceed if the android version supports notification channels
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
|
|
messageRepo.getConversation(threadId)?.let { conversation ->
|
|
val channelId = buildNotificationChannelId(threadId)
|
|
val name = conversation.getTitle()
|
|
val importance = NotificationManager.IMPORTANCE_HIGH
|
|
val channel = NotificationChannel(channelId, name, importance).apply {
|
|
enableLights(true)
|
|
lightColor = Color.WHITE
|
|
enableVibration(true)
|
|
vibrationPattern = VIBRATE_PATTERN
|
|
}
|
|
|
|
notificationManager.createNotificationChannel(channel)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the notification channel for the given conversation, or null if it doesn't exist
|
|
*/
|
|
private fun getNotificationChannel(threadId: Long): NotificationChannel? {
|
|
val channelId = buildNotificationChannelId(threadId)
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
return notificationManager
|
|
.notificationChannels
|
|
.firstOrNull { channel -> channel.id == channelId }
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Returns the channel id that should be used for a notification based on the threadId
|
|
*
|
|
* If a notification channel for the conversation exists, use the id for that. Otherwise return
|
|
* the default channel id
|
|
*/
|
|
private fun getChannelIdForNotification(threadId: Long): String {
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
val channelId = buildNotificationChannelId(threadId)
|
|
|
|
return notificationManager
|
|
.notificationChannels
|
|
.map { channel -> channel.id }
|
|
.firstOrNull { id -> id == channelId }
|
|
?: DEFAULT_CHANNEL_ID
|
|
}
|
|
|
|
return DEFAULT_CHANNEL_ID
|
|
}
|
|
|
|
/**
|
|
* Formats a notification channel id for a given thread id, whether the channel exists or not
|
|
*/
|
|
override fun buildNotificationChannelId(threadId: Long): String {
|
|
return when (threadId) {
|
|
0L -> DEFAULT_CHANNEL_ID
|
|
else -> "notifications_$threadId"
|
|
}
|
|
}
|
|
|
|
} |