Skip to content

Instantly share code, notes, and snippets.

@subversivo58
Last active December 27, 2017 19:37
Show Gist options
  • Save subversivo58/d6b4b6ec9af77cff996c40d927a61113 to your computer and use it in GitHub Desktop.
Save subversivo58/d6b4b6ec9af77cff996c40d927a61113 to your computer and use it in GitHub Desktop.
/**
* @see font: https://developer.android.com/guide/topics/ui/notifiers/notifications.html
*/
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Hello World!");
// Creates an explicit intent for an Activity in your app
Intent resultIntent = new Intent(this, ResultActivity.class);
// The stack builder object will contain an artificial back stack for the
// started Activity.
// This ensures that navigating backward from the Activity leads out of
// your application to the Home screen.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(ResultActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// mId allows you to update the notification later on.
mNotificationManager.notify(mId, mBuilder.build());
/**
* @see font: https://support.insert.io/hc/en-us/articles/209724505-Send-push-notifications-via-Insert-Android-
*/
/**
* use "Insert" with Firebase
*/
public class InsertFirebaseMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if ( remoteMessage.getNotification() != null ) {
RemoteMessage.Notification notification = remoteMessage.getNotification();
// Icon
int iconResId = 0;
final String drawableResourceName = notification.getIcon();
if ( !TextUtils.isEmpty(drawableResourceName) ) {
iconResId = getResources().getIdentifier(drawableResourceName, "drawable", getPackageName());
}
// Sound
Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
final String soundFileName = notification.getSound();
if ( !TextUtils.isEmpty(soundFileName) && !soundFileName.equals("default") ) {
sound = Uri.parse("android.resource://" + this.getPackageName() + "/raw/" + soundFileName);
}
Bundle bundle = new Bundle();
for (Map.Entry<String, String> entry : remoteMessage.getData().entrySet()) {
bundle.putString(entry.getKey(), entry.getValue());
}
// Build Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle("Your App Name")
.setContentText(notification.getBody())
.setAutoCancel(true)
.setSmallIcon(iconResId == 0 ? getApplicationInfo().icon : iconResId)
.setSound(sound)
.setContentIntent(Insert.generatePendingIntentForPush(bundle));
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(0, builder.build());
}
}
}
/**
* use "Insert" with GCM
*/
// registrartion Intent service
public class RegistrationIntentService extends IntentService {
public RegistrationIntentService() {
super("RegistrationIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
try {
// Initially this call goes out to the network to retrieve the token, subsequent calls are local.
// R.string.gcm_defaultSenderId (the Sender ID) is derived from google-services.json (the project number).
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId), GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
Insert.setPushId(token);
} catch (Exception e) {}
}
}
// start the registration service from the main activity
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
// Instance ID listener service
public class InsertInstanceIDListenerService extends InstanceIDListenerService {
@Override
public void onTokenRefresh() {
Intent intent = new Intent(this, RegistrationIntentService.class);
startService(intent);
}
}
// Listener service to display incoming notification
public class InsertGcmListenerService extends GcmListenerService {
@Override
public void onMessageReceived(String from, final Bundle data) {
// Icon
int iconResId = 0;
final String drawableResourceName = data.getBundle("notification").getString("icon");
if ( !TextUtils.isEmpty(drawableResourceName) ) {
iconResId = getResources().getIdentifier(drawableResourceName, "drawable", getPackageName());
}
// Sound
Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
final String soundFileName = data.getBundle("notification").getString("sound");
if ( !TextUtils.isEmpty(soundFileName) && !soundFileName.equals("default") ) {
sound = Uri.parse("android.resource://" + this.getPackageName() + "/raw/" + soundFileName);
}
// Build Notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle("Your App Name")
.setContentText(data.getString("message"))
.setAutoCancel(true)
.setSmallIcon(iconResId == 0 ? getApplicationInfo().icon : iconResId)
.setSound(sound)
.setContentIntent(Insert.generatePendingIntentForPush(data));
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(0, builder.build());
}
}
/**
* use "Insert" with Cordova (Push Notification plugin)
*/
// Cordova command line:
cordova plugin add phonegap-plugin-push --variable SENDER_ID=<sender-id>
// Add the following code after "onDeviceReady"
var push = PushNotification.init({
"android": {
"senderID": "<sender-id>"
},
"ios": {
"alert": "true",
"badge": "true",
"sound": "true"
},
"windows": {}
});
push.on('registration', function(data) {
window.plugins.Insert.setPushId(data.registrationId);
});
push.on('notification', function(data) {
// this function will be called when the push notification is received by the app
});
push.on('error', function(e) {
console.log(e.message);
});
<DOCTYPE html>
<html lang="en-US">
<head>
<charset="UTF-8">
<title>Web Pus Test</title>
<!-- Bootstrap 4 beta 2 -->
<link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
</head>
<body>
<section class="subscription-details js-subscription-details is-invisible">
<p>Uma vez que você inscreveu seu usuário, você enviará esta inscrição para o seu servidor para armazenar em um banco de dados para que, quando você desejar enviar uma mensagem, você pode pesquisar pela assinatura e enviar uma mensagem para ela.</p>
<pre><code class="js-subscription-json"></code></pre>
</section>
<button disabled class="js-push-btn btn btn-info rounded-0">
Enable Push Messaging
</button>
<!-- JavaScript -->
<script>
'use strict';
// VAPID public key
const applicationServerPublicKey = 'BHx3slN8kpDixb_mCc7QgzZ4MvkvFCIZvfEiWt3DGb2QL2ACTDPdJItVAarXMnrKLk2H8Qp-AFFVW2uQT6TcjoA';
// referenciar o botão
const pushButton = document.querySelector('.js-push-btn');
// pré-armazenar uma variável que define o status da subscrição
let isSubscribed = false;
// pré-armazenar uma variável para o objeto do registro
let swRegistration = null;
// converter base64URL em UInt8Array
function urlB64ToUint8Array(base64String){
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// atualizar texto no botão
function updateBtn() {
// verificar se a permissão do Navegadro para usar Notificações foi negada
if ( Notification.permission === 'denied' ) {
// atualizar botão
pushButton.textContent = 'Push Messaging Blocked.';
// desativar o botão
pushButton.disabled = true;
// mostrar objeto da subscrição
showSubscriptionObject(null);
return;
}
// mudar o botão conforme o status da subscriçao
if ( isSubscribed ) {
pushButton.textContent = 'Disable Push Messaging';
} else {
pushButton.textContent = 'Enable Push Messaging';
}
// reativar o botão
pushButton.disabled = false;
}
/**
Para este exemplo esta função mostra no (adiciona ao) HTML em um bloco de código
o objeto da inscrição (subscrição)
*/
function showSubscriptionObject(subscription) {
// referenciar elementos
const subscriptionJson = document.querySelector('.js-subscription-json');
// verificar se existe um objeto da subscrição
if ( subscription ) {
subscriptionJson.textContent = JSON.stringify(subscription, null, 4);
}
}
// inscrever o usuário
function subscribeUser() {
swRegistration.pushManager.subscribe({
// obrigatório... declara que vai mostrar uma notificação sempre que manipular um evento push
userVisibleOnly: true,
// credencias
applicationServerKey: urlB64ToUint8Array(applicationServerPublicKey)
}).then(function(subscription) {
// enviar para o servidor
fetch('/push/subscribe', {
method: 'post',
mode: 'cors',
referrerPolicy: 'origin',
credentials: 'include',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
}).then(function(response) {
return response;
}).then(function(text) {
console.log('User is subscribed.');
// mostrar objeto da subscrição
showSubscriptionObject(subscription);
// redefinir o status da subscrição
isSubscribed = true;
// atualizar o botão
updateBtn();
}).catch(function(error) {
console.log('Failed to subscribe the user: ', err);
// atualizar o botão
updateBtn();
});
}).catch(function(err) {
console.log('Failed to subscribe the user: ', err);
});
}
// remover inscrição
function unsubscribeUser(){
swRegistration.pushManager.getSubscription().then(function(subscription) {
// verificar se há um objeto da subscrição
if ( subscription ) {
// enviar para o servidor
fetch('/push/unsubscribe', {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(subscription)
}).then(function(response) {
if ( response.ok ) {
return response.json();
}
throw new Error('Failed unsubscrible Push Notifications! Return server code: ' + response.status);
}).then(function(json) {
// mostrar objeto da subscrição
showSubscriptionObject(null);
console.log('User is unsubscribed.');
// redefinir o status da subscrição
isSubscribed = false;
// atualizar o botão
updateBtn();
// remover inscrição do objeto... retorna uma Promessa e em caso de erro pode ser caputurado pelo "catch"
subscription.unsubscribe();
}).catch(function(error) {
console.log('Error unsubscribing', error.message);
});
}
});
}
function initialiseUI() {
// observar o evento "click" no botão
pushButton.addEventListener('click', function() {
// desativar o botão
pushButton.disabled = true;
if ( isSubscribed ) {
// se o usuário já está inscrito... remover inscrição
unsubscribeUser();
} else {
// inscrever o usuário
subscribeUser();
}
});
// obter o status da subscrição
swRegistration.pushManager.getSubscription().then(function(subscription) {
// sobreescrever (ou não) o status da subscrição
isSubscribed = !(subscription === null);
// mostrar objeto da subscrição
showSubscriptionObject(subscription);
if ( isSubscribed ) {
console.log('User IS subscribed.');
} else {
console.log('User is NOT subscribed.');
}
// atualizar botão
updateBtn();
});
}
// verificar se o Navegador oferece supporte a Service Worker
if ( 'serviceWorker' in navigator && 'PushManager' in window ) {
// inicializar o Service Worker
navigator.serviceWorker.register('./sw.js').then(function(swReg) {
// reescrever a variável para deixar o objeto do registro acessível "globalmente"
swRegistration = swReg;
// ajustar a UI
initialiseUI();
}).catch(function(error){
console.error('Service Worker Error', error);
});
} else {
console.warn('Push messaging is not supported');
pushButton.textContent = 'Push Not Supported';
}
</script>
</body>
</html>
/* as rotas são acessadas pelo método POST e devem conter no minimo 2 campos:
-- endpoint {String}
-- keys {Object}
"keys" por sua vez deve conter duas propriedades:
-- p256dh: {String}
-- auth: {String}
*/
const express = require('express')
const router = express.Router()
// assumindo que a chave VAPID já foi gerada e esta armazenada por exemplo nas variaveis
webpush.setVapidDetails(
'mailto:example@yourdomain.org', // fallback para o qual os serviços podem (ou não) reportar uma falha na entrega da mensagem.
process.env.PUSH_PUB, // VAPID chave publica
process.env.PUSH_PRI // VAPID chave privada
)
// rota de inscrição
router.post('push/subscribe', (req, res, next) => {
//
const subscription = {
endpoint: req.body.endpoint,
keys: {
p256dh: req.body.keys.p256dh,
auth: req.body.keys.auth
}
}
// para utilizar o p256dh como um identificador (ao salvar no bando de dados por exemplo)
let pushId = subscription.keys.p256dh.replace(/-/g,'').replace(/=/g,'')
// supondo que a aplicação ira salvar esta chave e no caso de sucesso ira então enviar uma notificação...
// definir o payload
let payload = JSON.stringify({
title: 'Um titulo legal',
body: 'Alguma coisa aqui...',
icon: 'imagem.png' // caminho absoluto ou relativo (relativo ao servidor)
})
// definir um TTL em segundos
let options = {
TTL: 60 // 1 minuto
}
// enviar...
webpush.sendNotification(subscription, payload, options).then(response => {
// tratar o successo
}).catch(e => {
// tratar o erro
})
})
// rota para remover inscrição
router.post('/push/unsubscribe', (req, res, next) => {
// limpar a chave p256dh para procurar no banco de dados
let pushId = req.body.keys.p256dh.replace(/-/g,'').replace(/=/g,'')
// após executar a tarefa de remoção devolver uma resposta ao front-end:
})
// VAPID public key
const applicationServerPublicKey = 'BKXyMNOcPJMEfNnYWUrErN86WCacx4jdfepDR23x-cHkLP7TUj2cZ6Sp_UFRHFZYSfx7-Bk4XJHWcPgGi7DaASc';
// uma simples função para "estender" objetos
function extend() {
try {
let hasOwnProperty = Object.prototype.hasOwnProperty,
target = {}
for (let i = 0; i < arguments.length; i++) {
let source = arguments[i]
for (let key in source) {
if ( hasOwnProperty.call(source, key) ) {
target[key] = source[key]
}
}
}
return target
} catch(e) {
return {}
}
}
// converter base64URL em UInt8Array
function urlB64ToUint8Array(base64String){
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
// observar quando uma notificação chegar
self.addEventListener('push', function(event){
console.log('[Service Worker] Push Received.');
// definir propriedades de fallback caso ocorra erro ao resgatar a mensagem
let defautOptions = {
title: 'Site Name',
body: 'Mensagem padrão',
// imagens podem ter urls relativas ou absolutas
icon: './img/icons/icon.png',
badge: './img/icons/icon-badage.png',
//image: './img/push-banner-1600x1100.jpg', // imagem banner
//vibrate: [200, 100, 200], // caso seja um dispositivo mobile
//sound: './media/audio/incoming.m4a', // caso seja um dispositivo mobile
dir: 'auto', // direção do texto: auto, ltr (esquerda para direita), rtl (direita para esquerda)
tag: 'default', // define se uma mensagem é única ou se será enfileirada em grupo
requireInteraction: false, // "true" obriga uma iteração (para desktop)
renotify: false, // "true" requer obrigátoriamente uma etiqueta (tag)
silent: false // "true" não vibra, não dispara som nem dispara o led luminoso
}
// obter o payload
var data = event.data.text()
// utilizar o bloco try/catch para tratar errors
try {
// transformar o payload em um {Object} JavaScript
let object = JSON.parse(data)
// verificar se há a propriedade data {Object}
if ( 'data' in object ) {
//...
}
// add timestamp
object.timestamp = new Date().getTime()
// redefinir a variável "data"
data = object
} catch(ex) {
// redefinir a variável "data"
data = {};
// extender usando as o~pções padrão
data = extend(data, defaultOptions)
}
// disparar a notificação
event.waitUntil(self.registration.showNotification(data.title, data));
});
// observar o evento click na notificação
self.addEventListener('notificationclick', function(event){
console.log('[Service Worker] Notification click Received.');
// close notification
event.notification.close();
// verificar se há conteúdo
if ( Object.keys(event.notification).length > 0 ) {
// verificar se há a propriedade "data"
if ( 'data' in event.notification ) {
let data = event.notification.data;
/**
Como é possível adicionar uma propriedade "data" ao payload e esta pode conter valores arbitrários...
é possível adicionar urls para abrir no evento click ou mesmos executar outras instruções
definidas em seu Service Worker.
Exemplo:
if ( data.openWindow ) {
event.waitUntil(
clients.openWindow(data.url)
);
}
*/
}
}
});
/**
esta etapa deve ser executada apenas uma vez visto que, o navegador
do usuário irá utilizá-la para se registrar no "serviço" e se a chave
for alterada será necessário recadastrar o navegador do usuário...
como isto é tratado pelo front-end através de uma iteração do usuário,
usuários cadastrados com uma chave antiga não receberão notificações
disparadas usando uma chave nova
*/
// web-push biblioteca
const webpush = require('web-push')
// criar chaves
const vapidKeys = webpush.generateVAPIDKeys()
// um simples exemplo mostrando para o console (você deve salvar o par de chaves no banco de dados ou em um arquivo)
console.log({
pub: vapidKeys.publicKey,
pri: vapidKeys.privateKey
})
// a saída irá gerar algo como:
{
pub: 'BKXyMNOcPJMEfNnYWUrErN86WCacx4jdfepDR23x-cHkLP7TUj2cZ6Sp_UFRHFZYSfx7-Bk4XJHWcPgGi7DaASc',
pri: 'sXwt3VYs_XKe0ST8fwH5CkBzdaT6mXuUlRImcJQAWak'
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment