Improve push notification handling

This commit is contained in:
Phillip Thelen 2019-08-15 17:44:22 +02:00
parent fd9240b44e
commit 66ca495763
15 changed files with 229 additions and 68 deletions

View file

@ -15,6 +15,8 @@
<string name="reject_guild_invite" translatable="false">REJECT_GUILD_INVITE</string>
<string name="accept_quest_invite" translatable="false">ACCEPT_QUEST_INVITE</string>
<string name="reject_quest_invite" translatable="false">REJECT_QUEST_INVITE</string>
<string name="group_message_reply" translatable="false">GROUP_MESSAGE_REPLY</string>
<string name="inbox_message_reply" translatable="false">INBOX_MESSAGE_REPLY</string>
<string name="qr_album_name" translatable="false">habitica</string>
<string name="qr_file_name" translatable="false"> habitrpg-qr-code.jpg</string>

View file

@ -945,4 +945,6 @@
<string name="share_challenge_with">Share Challenge with</string>
<string name="app_theme">App Theme</string>
<string name="sent_card">You sent a %s</string>
<string name="group_activity_summary_notif">New messages in %s</string>
<string name="inbox_messages_title">You received %d messages from %s</string>
</resources>

View file

@ -21,7 +21,8 @@ class NotificationOpenHandler {
PushNotificationManager.RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY -> openPrivateMessageScreen(intent.getStringExtra("replyTo"))
PushNotificationManager.CHANGE_USERNAME_PUSH_NOTIFICATION_KEY -> openSettingsScreen()
PushNotificationManager.GIFT_ONE_GET_ONE_PUSH_NOTIFICATION_KEY -> openSubscriptionScreen()
PushNotificationManager.CHAT_MENTION_NOTIFICATION_KEY -> handleChatMention(intent.getStringExtra("type"), intent.getStringExtra("groupID"))
PushNotificationManager.CHAT_MENTION_NOTIFICATION_KEY -> handleChatMessage(intent.getStringExtra("type"), intent.getStringExtra("groupID"))
PushNotificationManager.GROUP_ACTIVITY_NOTIFICATION_KEY -> handleChatMessage(intent.getStringExtra("type"), intent.getStringExtra("groupID"))
}
}
@ -60,7 +61,7 @@ class NotificationOpenHandler {
MainNavigationController.navigate(R.id.prefsActivity)
}
private fun handleChatMention(type: String, groupID: String) {
private fun handleChatMessage(type: String, groupID: String) {
when (type) {
"party" -> MainNavigationController.navigate(R.id.partyFragment)
"tavern" -> MainNavigationController.navigate(R.id.tavernFragment)

View file

@ -0,0 +1,89 @@
package com.habitrpg.android.habitica.helpers.notifications
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.Person
import androidx.core.app.RemoteInput
import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
import net.pherth.android.emoji_library.EmojiParser
import java.text.SimpleDateFormat
import java.util.*
class GroupActivityNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier) {
override fun getNotificationID(data: MutableMap<String, String>): Int {
return data["groupID"].hashCode()
}
override fun configureNotificationBuilder(data: MutableMap<String, String>): NotificationCompat.Builder {
val user = Person.Builder().setName("You").build()
val message = makeMessageFromData(data)
var style = NotificationCompat.MessagingStyle(user)
.setGroupConversation(true)
.setConversationTitle(data["groupName"])
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
val existingNotifications = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
} else null
val oldMessages = existingNotifications?.firstOrNull()?.notification?.extras?.getBundle("messages")?.get("messages") as? ArrayList<Map<String, String>> ?: arrayListOf()
for (oldMessage in oldMessages) {
style = style.addMessage(makeMessageFromData(oldMessage))
}
style = style.addMessage(message)
oldMessages.add(data)
return super.configureNotificationBuilder(data)
.setStyle(style)
.setExtras(bundleOf(Pair("messages", bundleOf(Pair("messages", oldMessages)))))
}
private fun makeMessageFromData(data: Map<String, String>): NotificationCompat.MessagingStyle.Message {
val sender = Person.Builder().setName(data["senderName"]).build()
val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)
val timestamp = dateFormat.parse(data["timestamp"]) ?: Date()
val messageText = EmojiParser.parseEmojis(data["message"]?.trim { it <= ' ' })
return NotificationCompat.MessagingStyle.Message(
messageText,
timestamp.time,
sender
)
}
override fun setNotificationActions(data: Map<String, String>) {
super.setNotificationActions(data)
val groupID = data["groupID"] ?: return
val actionName = context.getString(R.string.group_message_reply)
val replyLabel: String = context.getString(R.string.reply)
val remoteInput: RemoteInput = RemoteInput.Builder(actionName).run {
setLabel(replyLabel)
build()
}
val intent = Intent(context, LocalNotificationActionReceiver::class.java)
intent.action = actionName
intent.putExtra("groupID", groupID)
val replyPendingIntent: PendingIntent =
PendingIntent.getBroadcast(context, groupID.hashCode(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val action: NotificationCompat.Action =
NotificationCompat.Action.Builder(R.drawable.ic_send_grey_600_24dp,
context.getString(R.string.reply), replyPendingIntent)
.addRemoteInput(remoteInput)
.build()
notificationBuilder.addAction(action)
}
override fun configureMainIntent(intent: Intent) {
super.configureMainIntent(intent)
intent.putExtra("type", data?.get("type"))
intent.putExtra("groupID", data?.get("groupID"))
}
}

View file

@ -17,8 +17,8 @@ class GuildInviteLocalNotification(context: Context, identifier: String) : Habit
intent.putExtra("groupID", data?.get("groupID"))
}
override fun setNotificationActions() {
super.setNotificationActions()
override fun setNotificationActions(data: Map<String, String>) {
super.setNotificationActions(data)
val res = context.resources
val acceptInviteIntent = Intent(context, LocalNotificationActionReceiver::class.java)

View file

@ -1,18 +1,15 @@
package com.habitrpg.android.habitica.helpers.notifications
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import androidx.annotation.CallSuper
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.ui.activities.MainActivity
import java.util.*
/**
* Created by keithholliday on 6/28/16.
@ -24,40 +21,40 @@ abstract class HabiticaLocalNotification(protected var context: Context, protect
protected var message: String? = null
protected var notificationBuilder = NotificationCompat.Builder(context, "default")
.setSmallIcon(R.drawable.ic_gryphon_white)
.setAutoCancel(true)
open fun configureNotificationBuilder(data: MutableMap<String, String>): NotificationCompat.Builder {
val path = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
return notificationBuilder
.setSound(path)
}
@CallSuper
open fun notifyLocally(title: String?, message: String?) {
open fun notifyLocally(title: String?, message: String?, data: MutableMap<String, String>) {
this.title = title
this.message = message
val path = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
var notificationBuilder = configureNotificationBuilder(data)
this.notificationBuilder = notificationBuilder
.setSmallIcon(R.drawable.ic_gryphon_white)
.setAutoCancel(true)
.setSound(path)
if (title != null) {
if (this.title != null) {
notificationBuilder = notificationBuilder.setContentTitle(title)
}
if (message != null) {
notificationBuilder = notificationBuilder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
if (this.message != null) {
notificationBuilder = notificationBuilder.setContentText(message)
}
this.setNotificationActions()
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
this.setNotificationActions(data)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager?.createOrUpdateHabiticaChannel()
}
notificationManager?.notify(getNotificationID(), notificationBuilder.build())
val notificationManager = NotificationManagerCompat.from(context)
notificationManager.notify(getNotificationID(data), notificationBuilder.build())
}
fun setExtras(data: Map<String, String>) {
this.data = data
}
protected open fun setNotificationActions() {
protected open fun setNotificationActions(data: Map<String, String>) {
val intent = Intent(context, MainActivity::class.java)
intent.putExtra("notificationIdentifier", identifier)
configureMainIntent(intent)
@ -73,20 +70,7 @@ abstract class HabiticaLocalNotification(protected var context: Context, protect
protected open fun configureMainIntent(intent: Intent) {
}
protected open fun getNotificationID(): Int = 10
}
@RequiresApi(Build.VERSION_CODES.O)
fun NotificationManager.createOrUpdateHabiticaChannel() {
var hasChannel = false
for (channel in notificationChannels) {
if (channel.id == "default") {
hasChannel = true
break
}
}
if (!hasChannel) {
val channel = NotificationChannel("default", "Habitica Notifications", NotificationManager.IMPORTANCE_DEFAULT)
createNotificationChannel(channel)
protected open fun getNotificationID(data: MutableMap<String, String>): Int {
return Date().time.toInt()
}
}

View file

@ -31,6 +31,8 @@ public class HabiticaLocalNotificationFactory {
return new GiftOneGetOneLocalNotification(context, notificationType);
} else if (notificationType.contains(PushNotificationManager.CHAT_MENTION_NOTIFICATION_KEY)) {
return new ChatMentionNotification(context, notificationType);
} else if (notificationType.contains(PushNotificationManager.GROUP_ACTIVITY_NOTIFICATION_KEY)) {
return new GroupActivityNotification(context, notificationType);
} else {
return new GenericLocalNotification(context, notificationType);
}

View file

@ -12,8 +12,8 @@ import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
*/
class PartyInviteLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier) {
override fun setNotificationActions() {
super.setNotificationActions()
override fun setNotificationActions(data: Map<String, String>) {
super.setNotificationActions(data)
val res = context.resources
val acceptInviteIntent = Intent(context, LocalNotificationActionReceiver::class.java)

View file

@ -78,7 +78,7 @@ class PushNotificationManager(var apiClient: ApiClient, private val sharedPrefer
additionalData["identifier"] = remoteMessageIdentifier ?: ""
AmplitudeManager.sendEvent("receive notification", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
notification.setExtras(remoteMessage.data)
notification.notifyLocally(remoteMessage.data["title"], remoteMessage.data["body"])
notification.notifyLocally(remoteMessage.data["title"], remoteMessage.data["body"], remoteMessage.data)
}
}
@ -110,7 +110,6 @@ class PushNotificationManager(var apiClient: ApiClient, private val sharedPrefer
}
companion object {
const val PARTY_INVITE_PUSH_NOTIFICATION_KEY = "invitedParty"
const val RECEIVED_PRIVATE_MESSAGE_PUSH_NOTIFICATION_KEY = "newPM"
const val RECEIVED_GEMS_PUSH_NOTIFICATION_KEY = "giftedGems"
@ -122,6 +121,7 @@ class PushNotificationManager(var apiClient: ApiClient, private val sharedPrefer
const val CHANGE_USERNAME_PUSH_NOTIFICATION_KEY = "changeUsername"
const val GIFT_ONE_GET_ONE_PUSH_NOTIFICATION_KEY = "gift1get1"
const val CHAT_MENTION_NOTIFICATION_KEY = "chatMention"
const val GROUP_ACTIVITY_NOTIFICATION_KEY = "groupActivity"
private const val DEVICE_TOKEN_PREFERENCE_KEY = "device-token-preference"
}
}

View file

@ -5,6 +5,4 @@ import android.content.Context
/**
* Created by keithholliday on 7/1/16.
*/
class QuestBegunLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier) {
override fun getNotificationID(): Int = 1000
}
class QuestBegunLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier)

View file

@ -11,10 +11,12 @@ import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
*/
class QuestInviteLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier) {
override fun getNotificationID(): Int = 1000
override fun getNotificationID(data: MutableMap<String, String>): Int {
return 1000
}
override fun setNotificationActions() {
super.setNotificationActions()
override fun setNotificationActions(data: Map<String, String>) {
super.setNotificationActions(data)
val res = context.resources
val acceptInviteIntent = Intent(context, LocalNotificationActionReceiver::class.java)

View file

@ -1,8 +1,73 @@
package com.habitrpg.android.habitica.helpers.notifications
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
import androidx.core.os.bundleOf
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.receivers.LocalNotificationActionReceiver
import net.pherth.android.emoji_library.EmojiParser
/**
* Created by keithholliday on 7/1/16.
*/
class ReceivedPrivateMessageLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier)
class ReceivedPrivateMessageLocalNotification(context: Context, identifier: String) : HabiticaLocalNotification(context, identifier) {
override fun configureNotificationBuilder(data: MutableMap<String, String>): NotificationCompat.Builder {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
val existingNotifications = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
} else null
val messageText = EmojiParser.parseEmojis(data["message"]?.trim { it <= ' ' })
val oldMessages = existingNotifications?.firstOrNull()?.notification?.extras?.getStringArrayList("messages") ?: arrayListOf()
var style = NotificationCompat.InboxStyle()
for (oldMessage in oldMessages) {
style = style.addLine(oldMessage)
}
style = style.addLine(messageText)
oldMessages.add(messageText)
var notification = super.configureNotificationBuilder(data)
.setExtras(bundleOf(Pair("messages", oldMessages)))
if (oldMessages.size > 1) {
notification = notification
.setContentTitle(context.getString(R.string.inbox_messages_title, oldMessages.size, data["senderName"]))
.setStyle(style)
title = null
}
return notification
}
override fun getNotificationID(data: MutableMap<String, String>): Int {
return data["senderName"].hashCode()
}
override fun setNotificationActions(data: Map<String, String>) {
super.setNotificationActions(data)
val senderID = data["replyTo"] ?: return
val actionName = context.getString(R.string.inbox_message_reply)
val replyLabel: String = context.getString(R.string.reply)
val remoteInput: RemoteInput = RemoteInput.Builder(actionName).run {
setLabel(replyLabel)
build()
}
val intent = Intent(context, LocalNotificationActionReceiver::class.java)
intent.action = actionName
intent.putExtra("senderID", senderID)
val replyPendingIntent: PendingIntent =
PendingIntent.getBroadcast(context, senderID.hashCode(),
intent,
PendingIntent.FLAG_UPDATE_CURRENT)
val action: NotificationCompat.Action =
NotificationCompat.Action.Builder(R.drawable.ic_send_grey_600_24dp,
context.getString(R.string.reply), replyPendingIntent)
.addRemoteInput(remoteInput)
.build()
notificationBuilder.addAction(action)
}
}

View file

@ -4,6 +4,8 @@ import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient
@ -24,11 +26,15 @@ class LocalNotificationActionReceiver : BroadcastReceiver() {
private var user: User? = null
private var groupID: String? = null
private var senderID: String? = null
private var context: Context? = null
private var intent: Intent? = null
override fun onReceive(context: Context, intent: Intent) {
HabiticaBaseApplication.userComponent?.inject(this)
this.intent = intent
groupID = intent.extras?.getString("groupID")
senderID = intent.extras?.getString("senderID")
this.context = context
handleLocalNotificationAction(intent.action)
}
@ -63,6 +69,26 @@ class LocalNotificationActionReceiver : BroadcastReceiver() {
socialRepository.rejectGroupInvite(it).subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
}
context?.getString(R.string.group_message_reply) -> {
groupID?.let {
getMessageText(context?.getString(R.string.group_message_reply))?.let { message ->
socialRepository.postGroupChat(it, message).subscribe(Consumer {
context?.let { c -> NotificationManagerCompat.from(c).cancel(it.hashCode()) }
}, RxErrorHandler.handleEmptyError())
}
}
}
context?.getString(R.string.inbox_message_reply) -> {
senderID?.let {
getMessageText(context?.getString(R.string.inbox_message_reply))?.let { message ->
socialRepository.postPrivateMessage(it, message).subscribe(Consumer { }, RxErrorHandler.handleEmptyError())
}
}
}
}
}
private fun getMessageText(key: String?): String? {
return RemoteInput.getResultsFromIntent(intent)?.getCharSequence(key)?.toString()
}
}

View file

@ -1,7 +1,6 @@
package com.habitrpg.android.habitica.receivers
import android.app.Notification
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
@ -9,6 +8,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import com.habitrpg.android.habitica.HabiticaBaseApplication
@ -18,7 +18,6 @@ import com.habitrpg.android.habitica.data.UserRepository
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.helpers.notifications.createOrUpdateHabiticaChannel
import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.MainActivity
@ -85,12 +84,9 @@ class NotificationPublisher : BroadcastReceiver() {
}
private fun notify(intent: Intent, notification: Notification?) {
val notificationManager = context?.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationManager?.createOrUpdateHabiticaChannel()
}
val notificationManager = context?.let { NotificationManagerCompat.from(it) }
val id = intent.getIntExtra(NOTIFICATION_ID, 0)
notificationManager?.notify(id, notification)
notification?.let { notificationManager?.notify(id, it) }
}
private fun buildNotification(wasInactive: Boolean, registrationDate: Date? = null): Notification? {

View file

@ -1,20 +1,18 @@
package com.habitrpg.android.habitica.receivers
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.media.RingtoneManager
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.helpers.notifications.createOrUpdateHabiticaChannel
import com.habitrpg.android.habitica.ui.activities.MainActivity
import io.reactivex.functions.Consumer
import java.util.*
@ -70,11 +68,7 @@ class TaskReceiver : BroadcastReceiver() {
.setAutoCancel(true)
.setContentIntent(pendingIntent)
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
/* Create or update. */
notificationManager?.createOrUpdateHabiticaChannel()
}
notificationManager?.notify(System.currentTimeMillis().toInt(), notificationBuilder.build())
val notificationManager = NotificationManagerCompat.from(context)
notificationManager.notify(System.currentTimeMillis().toInt(), notificationBuilder.build())
}
}