Skip to content

Instantly share code, notes, and snippets.

@AndSky90
Last active December 27, 2019 07:28
Show Gist options
  • Save AndSky90/8db02d3d6395e603833f024fd948cf0f to your computer and use it in GitHub Desktop.
Save AndSky90/8db02d3d6395e603833f024fd948cf0f to your computer and use it in GitHub Desktop.
Firebase push handling
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="...">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="..."
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/application_label"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"
tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<!-- Standard icon for push notification -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_push_white_24" />
<!-- Standard icon color for push notification -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/colorAccent" />
<!-- Standard notification channel for push notification -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/notification_channel" />
<service
android:name=".FcmService"
android:enabled="true"
android:exported="true"
android:permission="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
object FcmChannelHandler {
@RequiresApi(Build.VERSION_CODES.O)
fun initBroadcastChannel(context: Context) =
NotificationChannel(
BROADCAST_CHANNEL_ID,
context.getString(R.string.push_notifications_category_broadcast_title),
NotificationManager.IMPORTANCE_HIGH
).apply {
description =
context.getString(R.string.push_notifications_category_broadcast_description)
enableLights(true)
lightColor = Color.YELLOW
enableVibration(true)
setShowBadge(true)
}
@RequiresApi(Build.VERSION_CODES.O)
fun initTenantChannel(context: Context) =
NotificationChannel(
TENANT_CHANNEL_ID,
context.getString(R.string.push_notifications_category_tenant_title),
NotificationManager.IMPORTANCE_HIGH
).apply {
description = context.getString(R.string.push_notifications_category_tenant_description)
enableLights(true)
lightColor = Color.YELLOW
enableVibration(true)
setShowBadge(true)
}
}
object FcmConstants {
const val TENANT_PREFIX = "android_tenant_"
const val DEBUG_POSTFIX = "_debug"
const val BROADCAST_CHANNEL_ID = "BROADCAST_CHANNEL_ID"
const val TENANT_CHANNEL_ID = "TENANT_CHANNEL_ID"
}
class FcmService : FirebaseMessagingService() {
@Suppress("UNUSED")
companion object {
const val TAG = "FcmService"
fun retrieveFcmRegistrationToken() {
FirebaseInstanceId.getInstance().instanceId
.addOnCompleteListener { task ->
if (task.isSuccessful && task.result?.token != null) {
SharedPreferencesProvider.putFcmToken(task.result!!.token)
log("PUSH Fcm getInstanceId successful, ${task.result!!.token}")
} else {
log("PUSH Fcm getInstanceId failed, ${task.exception}")
}
}
}
}
override fun onMessageReceived(p0: RemoteMessage) {
log("PUSH Received FCM From: ${p0.from}")
log("PUSH Message contains data: ${p0.data}")
log("PUSH Message contains notification : ${p0.notification?.body}, ${p0.notification?.title}")
NotificationProvider.notificator.receiveMessage(p0)
}
override fun onNewToken(p0: String) {
log("PUSH Refreshed token: $p0")
SharedPreferencesProvider.putFcmToken(p0)
}
}
object FcmTopicHandler {
fun getTenantTopic(tenantId: String) = "${TENANT_PREFIX}$tenantId"
fun getBroadcastTopic() = "android_broadcast"
fun subscribeToTopic(topic: String) {
//подписываемся на оба топика, отображаем только подходящий сборке
try {
FirebaseMessaging.getInstance().subscribeToTopic(topic)
.addOnCompleteListener { task ->
if (!task.isSuccessful) {
Logger.log("PUSH subscribe unsuccessful, topic $topic")
} else {
Logger.log("PUSH subscribe successful, topic $topic, task $task")
}
}
FirebaseMessaging.getInstance().subscribeToTopic("$topic${DEBUG_POSTFIX}")
.addOnCompleteListener { task ->
if (!task.isSuccessful) {
Logger.log("PUSH subscribe unsuccessful, topic $topic${DEBUG_POSTFIX}")
} else {
Logger.log("PUSH subscribe successful, topic $topic${DEBUG_POSTFIX}, task $task")
}
}
} catch (e: IllegalArgumentException) {
Logger.log("forbidden symbols in topic name")
}
}
fun unsubscribeFromTopic(topic: String) {
FirebaseMessaging.getInstance().unsubscribeFromTopic(topic)
Logger.log("topic $topic")
FirebaseMessaging.getInstance().unsubscribeFromTopic("$topic${DEBUG_POSTFIX}")
Logger.log("topic $topic${DEBUG_POSTFIX}")
}
}
class Notificator(
private val context: Context,
private val splashActivityClass: Class<*>
) : INotificationProvider {
companion object {
private const val KEY_TITLE = "title"
private const val KEY_BODY = "body"
private const val KEY_ID = "id"
private const val KEY_TENANT = "tenant"
private const val KEY_KIND = "notification_kind"
}
private var messageId = 0
private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
/**создаем каналы для нотификаций*/
init {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
notificationManager.createNotificationChannel(initBroadcastChannel(context))
notificationManager.createNotificationChannel(initTenantChannel(context))
}
}
override fun getTenantTopic(tenantId: String) = FcmTopicHandler.getTenantTopic(tenantId)
override fun getBroadcastTopic() = FcmTopicHandler.getBroadcastTopic()
override fun subscribeToTopic(topic: String) = FcmTopicHandler.subscribeToTopic(topic)
override fun unsubscribeFromTopic(topic: String) = FcmTopicHandler.unsubscribeFromTopic(topic)
override fun receiveMessage(message: RemoteMessage) {
/**показываем пуш,
* если мы авторизованы
* если тест или продакт топик совпадает с тест или продакт версией апи*/
if (SharedPreferencesProvider.getIsAuthorized()) {
if (message.from?.contains(DEBUG_POSTFIX) == true) {
if (CURRENT_BASE_URL == AppConstants.TEST_BASE_URL) preparePush(message)
else log("PUSH : product api, but test push, skip")
} else {
if (CURRENT_BASE_URL == AppConstants.PRODUCT_BASE_URL) preparePush(message)
else log("PUSH: test api, but product push, skip")
}
} else log("PUSH skipped: user not logged in")
}
private fun preparePush(message: RemoteMessage) {
val tenantId = message.from.orEmpty()
val tenantTitle = message.data[KEY_TENANT].orEmpty()
val messageTitle = message.data[KEY_TITLE].orEmpty()
val messageBody = message.data[KEY_BODY].orEmpty()
val id = message.data[KEY_ID].orEmpty()
when (message.data[KEY_KIND].orEmpty()) {
FcmKind.BROADCAST -> showBroadcastNotification(tenantTitle, messageTitle, messageBody)
FcmKind.TENANT -> showTenantNotification(tenantTitle, messageTitle, messageBody, tenantId)
FcmKind.POLL -> showPollNotification(tenantTitle, messageTitle, messageBody, tenantId, id)
else -> {}
}
}
private fun showBroadcastNotification(
tenantTitle: String,
messageTitle: String,
messageBody: String
) {
showNotification(tenantTitle, messageTitle, messageBody, null, BROADCAST_CHANNEL_ID)
}
private fun showTenantNotification(
tenantTitle: String,
messageTitle: String,
messageBody: String,
tenantId: String
) {
val tenant = tenantId.substringAfter(TENANT_PREFIX).substringBefore(DEBUG_POSTFIX)
val pendingIntent = PendingIntent.getActivity(
context, 0,
Intent(context, splashActivityClass).apply {
putExtra(TENANT_FROM_FCM, tenant)
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
/*Intent.FLAG_ACTIVITY_NEW_TASK */
}, PendingIntent.FLAG_UPDATE_CURRENT
)
showNotification(tenantTitle, messageTitle, messageBody, pendingIntent, TENANT_CHANNEL_ID)
}
private fun showPollNotification(
tenantTitle: String,
messageTitle: String,
messageBody: String,
tenantId: String,
pollId: String
) {
val tenant = tenantId.substringAfter(TENANT_PREFIX).substringBefore(DEBUG_POSTFIX)
val pendingIntent = PendingIntent.getActivity(
context, 0,
Intent(context, splashActivityClass).apply {
putExtra(TENANT_FROM_FCM, tenant)
putExtra(KIND_FROM_FCM, FcmKind.POLL)
putExtra(ID_FROM_FCM, pollId)
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
/*Intent.FLAG_ACTIVITY_NEW_TASK */
}, PendingIntent.FLAG_UPDATE_CURRENT
)
showNotification(tenantTitle, messageTitle, messageBody, pendingIntent, TENANT_CHANNEL_ID)
}
private fun showNotification(
tenantTitle: String,
messageTitle: String,
messageBody: String,
intent: PendingIntent?,
channelId: String
) {
val notificationBuilder =
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationCompat.Builder(context, channelId)
} else {
NotificationCompat.Builder(
context,
context.getString(R.string.notification_channel)
)
}
notificationBuilder
.setSmallIcon(R.drawable.ic_push_white_24)
.setLargeIcon(
BitmapFactory.decodeResource(
context.resources,
R.mipmap.ic_launcher
)
)
.setColor(ContextCompat.getColor(context, R.color.colorAccent))
.setDefaults(DEFAULT_ALL)
.setAutoCancel(true)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setContentTitle(messageTitle) //+ заголовок пуша
.setContentText(messageBody) //+ текст пуша
.setSubText(tenantTitle) //+ добавит доп текст в шапку
.setTicker(messageTitle) //- текст в систембаре (accessibility)
// .setContentInfo(tenantTitle) //-добавит доп текст (справа или сверху)
.setStyle(NotificationCompat.BigTextStyle().bigText(messageBody))
if (intent != null) {
notificationBuilder.setContentIntent(intent)
}
notificationManager.notify(messageId++, notificationBuilder.build()) // показываем пуш
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment