Cordova-plugin-firebase: (Android) Clicar na notificação não dispara callback onNotificationOpen

Criado em 21 set. 2016  ·  28Comentários  ·  Fonte: arnesson/cordova-plugin-firebase

Cordova 6.3.1
Plug-in Firebase Phonegap 0.1.12
Dispositivo: LG G5 com Android 6.0.1

Este é o código que estou usando:
`var app = {
// Application Constructor
inicializar: função () {
this.bindEvents ();
},
// Ligar ouvintes de eventos
//
// Vincule quaisquer eventos que sejam necessários na inicialização. Os eventos comuns são:
// 'load', 'deviceready', 'offline' e 'online'.
bindEvents: function () {
document.addEventListener ('deviceready', this.onDeviceReady, false);
},
// deviceready Event Handler
//
// O escopo de 'isto' é o evento. Para chamar o 'receivedEvent'
// função, devemos chamar explicitamente 'app.receivedEvent (...);'
onDeviceReady: function () {
console.log ("Estamos en onDeviceReady");
if (navigator.connection.type == Connection.NONE) {
navigator.notification.alert ('Se requiere conexión a Internet');
} senão {
window.FirebasePlugin.onNotificationOpen (function (notification) {
console.log (notificação);
navigator.notification.alert ("Recibido");
}, função (erro) {
console.log (erro);
});
}
},

};
app.initialize (); `

Recebo notificações na bandeja do sistema em todas as situações: aplicativo em segundo plano, primeiro plano e fechado e quando clico na notificação, o aplicativo é aberto nos três casos, mas o callback não é acionado.

Há algo errado no código?

Desde já, obrigado.

Comentários muito úteis

Reescrevi todo o manuseio de onNotificationOpen e quase funcionou como eu esperava, que é:

  • A notificação chega com o cliente em forground, abre a notificação e onNotificationOpen é chamado no cliente atual (sem recarregar)
  • A notificação chega com o cliente em segundo plano, abre a notificação e o cliente é colocado em primeiro plano e onNotificationOpen é chamado com o cliente atual (sem recarregar)

Resta que a notificação seja entregue imediatamente ao cliente, sem a necessidade de abrir, se isso for possível (ainda não verifiquei).

Eu sou um pouco noob do Android, então pode haver algumas melhorias que podem ser feitas, mas parece funcionar bem, então pensei em compartilhar.

OnNotificationOpe nReceiver: onReceive agora simplesmente chama

FirebasePlugin.onBroadcastReceive(context, intent);

Alterou FirebasePlugin seguinte forma

  • remover WeakReference de callbackContext
  • adicione o método onBroadcastReceive, passa os dados de intenção para onNotificationOpen
  • adiciona o método onNewIntent, passa os dados de intenção para onNotificationOpen
  • restaurou a versão antiga de onNotificationOpen e alterou-a da seguinte maneira

    • linha WeakReference removida

    • Altere os retornos de chamada para usar um PluginResult e chame setKeepCallback (true) no resultado para evitar que o retorno de chamada seja removido após a primeira chamada

    private static CallbackContext callbackContext;
    // ...
    private void registerOnNotificationOpen(final CallbackContext callbackContext) {
        FirebasePlugin.callbackContext = callbackContext;
    }

    // called when in foreground
    public static void onBroadcastReceive(Context context, Intent intent) {
        Log.d("FirebasePlugin", "onBroadcastReceive");
        Bundle data = intent.getExtras();
        FirebasePlugin.onNotificationOpen(data);
    }

    // called when in background
    <strong i="32">@Override</strong>
    public void onNewIntent(Intent intent) {
        Log.d(TAG, "new intent " + intent);
        super.onNewIntent(intent);
        FirebasePlugin.onNotificationOpen(intent.getExtras());
    }

    public static void onNotificationOpen(Bundle bundle) {
        if (FirebasePlugin.callbackContext == null ) {
            Log.d("FirebasePlugin", "no callback context, onNotificationOpen ignored");
            return;
        }
        if (callbackContext != null && bundle != null) {
            JSONObject json = new JSONObject();
            Set<String> keys = bundle.keySet();
            for (String key : keys) {
                try {
                    json.put(key, bundle.get(key));
                } catch (JSONException e) {
                    Log.d("FirebasePlugin", "onNotificationOpen: json exception");
                    PluginResult result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
                    result.setKeepCallback(true);
                    callbackContext.sendPluginResult(result);
                    return;
                }
            }
            Log.d("FirebasePlugin", "onNotificationOpen: send notification to javascript");
            PluginResult result = new PluginResult(PluginResult.Status.OK, json);
            result.setKeepCallback(true);
            callbackContext.sendPluginResult(result);
        }
    }

Todos 28 comentários

mesmo problema, no Android as notificações chegam, mas dentro do aplicativo nenhum retorno de chamada é chamado.
v. 0.1.12

@voidbrain @Kibukita , teste a versão mais recente do repo. Tentei melhorar a notificação de abertura. Obrigado.

@BugsBunnyBR Acabei de tentar atualizar para a versão mais recente do repo e testei. No Android ainda não está executando o onNotificationOpen.

@superheroben , você poderia fornecer um repositório com uma amostra de como está chamando o código do plugin? Como você está enviando as notificações? Eu testei com mensagens de tópico.

Mesmo problema aqui.
O retorno de chamada getInstanceId () é chamado, a notificação chega, mas o onNotificationOpen () nunca é chamado.

Código do cliente:

if (window.FirebasePlugin)
{
  window.FirebasePlugin.getInstanceId(
    function(token) {
        thiss.saveToken(token);
    }, 
    function(error) {
        console.log(error);
    }
  );

  window.FirebasePlugin.onNotificationOpen(
    function(notification) {                  
      alert("Yeah!!!");                  
    }, 
    function(error) {
      alert("Error!");
      console.error(error);
    }
  );

  window.FirebasePlugin.grantPermission();
}

Minha estrutura de dados de notificação:

(
    [to] => (device token)
    [notification] => Array
        (
            [title] => Title
            [text] => Test message
            [sound] => default
            [vibrate] => 1
            [badge] => 2
        )
)

Meu código do lado do servidor (PHP):

$jsonData = json_encode($data);

$ch     = curl_init("https://fcm.googleapis.com/fcm/send");
$header = array(
    'Content-Type: application/json',
    "Authorization: key=".MY_GCM_API_KEY
);

curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt( $ch,CURLOPT_SSL_VERIFYPEER, true);
curl_setopt( $ch,CURLOPT_RETURNTRANSFER, true );

curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);

$result = curl_exec($ch);
curl_close($ch);

Trabalhando no Cordova Android 5.2.2. O dispositivo usado para testar executa o Android 4.4.2. Versão 0.1.12 do plugin.

@arivanbastos , você tentou apontar para o repositório github?

A instalação a partir do github resolveu parcialmente o problema. Obrigada.

cordova plugin remove cordova-plugin-firebase
cordova plugin add https://github.com/arnesson/cordova-plugin-firebase/

Agora, o retorno de chamada onNotificationOpen () é chamado quando o aplicativo está em segundo plano.
Quando o aplicativo está em primeiro plano, em vez de a documentação diz, a notificação chega e o onNotificationOpen () não é chamado:

O aplicativo está em primeiro plano:
O usuário recebe os dados de notificação no retorno de chamada do JavaScript sem qualquer notificação no próprio dispositivo (este é o comportamento normal das notificações push, cabe a você, o desenvolvedor, notificar o usuário)

Se você enviar um push do tipo "notificação" (sem um corpo de dados), a notificação não deve ser exibida quando o aplicativo está em primeiro plano.

Desculpe, não entendi. Devo apenas adicionar uma seção de "dados" aos meus dados de notificação?

(
    [to] => (device token)
    [notification] => Array
        (
            [title] => Title
            [text] => Test message
        )
    [data] => Array
        (
            [test] => 1
        )
)

Isso não corrigiu o problema.

Renomear a seção "notificação" por "dados" faz o aplicativo travar ("O aplicativo parou") quando a notificação chega:

(
    [to] => (device token)
    [data] => Array
        (
            [title] => Title
            [text] => Test message
        )
)

https://firebase.google.com/docs/cloud-messaging/android/receive
Aqui está muito bem descrito.
Sua notificação vai para a bandeja do sistema se o aplicativo estiver em segundo plano e para onMessageReceived () se o aplicativo estiver em primeiro plano.
Se sua mensagem contiver dados, eles sempre serão transmitidos para onMessageReceived ().

Acho que há um problema na implementação de onMessageReceived ().
Quando vejo o código que interpreto é o seguinte:

  • extrair dados
  • construir uma notificação com NotificationBuilder
  • notificar via NotificationManager

NotificationManager.notify (id, notificação) posta a notificação a ser mostrada na barra de status.

No entanto, para mim, parece que é normal que sempre que recebo uma mensagem do FCM seja exibida uma notificação na barra de status.
Não vejo nenhuma verificação se o aplicativo está em primeiro plano e não vejo nenhuma chamada para o retorno de chamada. Não sou um desenvolvedor Android, então talvez eu esteja errado, mas o comportamento descrito se encaixa nisso.

OK, encontrei o trecho de código que chama o retorno de chamada.
Com o último commit do BugsBunnyBR, houve uma mudança no OnNotificationOpenReceiver, o que explica que arivanbastos obtém seu callback executado ao apontar para o repositório github.

Mesmo assim, a notificação só é enviada (para OnNotificationOpenReceiver e NotificationManager) se houver um texto ou título (na notificação ou nos dados). Isso significa que não é possível enviar dados ao seu aplicativo sem receber a notificação enviada ao NotificationManager, que os mostra no StatusBar.

@arivanbastos
Eu disse algo errado para você. Desculpe por isso.
Você já tentou apontar para esta versão do repo?

@packowitz e @robertarnesson . Ok, minha implementação SEMPRE * tentará mostrar a notificação. Ou a notificação exibida automaticamente do Firebase ou a versão dentro de onMessageReceived será exibida.
O retorno de chamada onNotificationOpen será chamado quando onMessageReceived for chamado ou quando a notificação tiver dados e corpos de notificação ao mesmo tempo. No meu pr que introduziu o onNotificationOpen , tentei explicar que notificações push do tipo Notificação não disparariam o retorno de chamada. A recomendação é sempre incluir um corpo de dados e um corpo de notificação, para que o plug-in possa detectar e disparar o retorno de chamada.

Eu sei que, sempre mostrar a notificação não é o comportamento padrão de notificação do Firebase.

A maioria dos desenvolvedores de aplicativos Android deseja que suas notificações sejam mostradas na bandeja do sistema, mesmo que o aplicativo esteja em primeiro plano. Eu sei que há casos, em aplicativos de bate-papo, em que esse comportamento não é bom.
Quando desenvolvi o recurso de notificação do Android, não me importei com as necessidades dos apps de bate-papo ou em conformidade com o Firebase.

O que pode ser feito é:
1) Desenvolva um sinalizador para ser definido quando o aplicativo for aberto dizendo "ei, eu quero que você mostre a notificação mesmo quando o aplicativo estiver aberto" e use-o para controlar o comportamento. Não precisaria ser persistido em nenhum armazenamento se o aplicativo sempre definisse o sinalizador ao abrir.

2) Basta comentar esta linha e desabilitar a notificação quando o aplicativo estiver em primeiro plano.

_Always_ -> Se o plugin encontrar um "texto" ou "título" no corpo da notificação.

@BugsBunnyBR Gosto da ideia de ter um flag para escolher o comportamento.
Gostaria de desacoplar o NotificationManager do OnNotificationOpenReceiver chamando de volta seu retorno de chamada JS. Minha recomendação é introduzir uma verificação, se há dados na mensagem e se houver, chamar o retorno de chamada com os dados.
Para a notificação, seria bom ter a bandeira.
Obrigada.

@BugsBunnyBR Acabei de testar a mudança para o repositório github e descobri que tocar em uma notificação reinicia meu aplicativo, mesmo que ele já esteja em execução. Acontece mesmo quando o aplicativo está em primeiro plano e eu toco na notificação.
Mas o retorno de chamada funciona agora;)

O mesmo aqui, para mim a versão 0.1.13 está fazendo com que meu aplicativo reinicie apenas quando ele está em primeiro plano. A versão 0.1.12 está funcionando bem.

As notificações simplesmente não funcionam bem na versão repo. O motivo é, creio, que se onNotificationOpen for chamado antes de uma notificação ser aberta, ela será ignorada.

https://github.com/arnesson/cordova-plugin-firebase/blob/master/src/android/FirebasePlugin.java#L123

Em registerOnNotificationOpen ele só registra o retorno de chamada SE houver um notificationBundle esperando

Além disso, o motivo pelo qual o aplicativo parece recarregar se já estiver aberto está em OnNotificationOpenReceiver , diz explicitamente

launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);

https://github.com/arnesson/cordova-plugin-firebase/blob/master/src/android/OnNotificationOpenReceiver.java#L17

que força o cliente a recarregar, quando o notificationBundle está disponível quando onNotificationOpen callback é registrado para que o cliente então receba a notificação.

Reescrevi todo o manuseio de onNotificationOpen e quase funcionou como eu esperava, que é:

  • A notificação chega com o cliente em forground, abre a notificação e onNotificationOpen é chamado no cliente atual (sem recarregar)
  • A notificação chega com o cliente em segundo plano, abre a notificação e o cliente é colocado em primeiro plano e onNotificationOpen é chamado com o cliente atual (sem recarregar)

Resta que a notificação seja entregue imediatamente ao cliente, sem a necessidade de abrir, se isso for possível (ainda não verifiquei).

Eu sou um pouco noob do Android, então pode haver algumas melhorias que podem ser feitas, mas parece funcionar bem, então pensei em compartilhar.

OnNotificationOpe nReceiver: onReceive agora simplesmente chama

FirebasePlugin.onBroadcastReceive(context, intent);

Alterou FirebasePlugin seguinte forma

  • remover WeakReference de callbackContext
  • adicione o método onBroadcastReceive, passa os dados de intenção para onNotificationOpen
  • adiciona o método onNewIntent, passa os dados de intenção para onNotificationOpen
  • restaurou a versão antiga de onNotificationOpen e alterou-a da seguinte maneira

    • linha WeakReference removida

    • Altere os retornos de chamada para usar um PluginResult e chame setKeepCallback (true) no resultado para evitar que o retorno de chamada seja removido após a primeira chamada

    private static CallbackContext callbackContext;
    // ...
    private void registerOnNotificationOpen(final CallbackContext callbackContext) {
        FirebasePlugin.callbackContext = callbackContext;
    }

    // called when in foreground
    public static void onBroadcastReceive(Context context, Intent intent) {
        Log.d("FirebasePlugin", "onBroadcastReceive");
        Bundle data = intent.getExtras();
        FirebasePlugin.onNotificationOpen(data);
    }

    // called when in background
    <strong i="32">@Override</strong>
    public void onNewIntent(Intent intent) {
        Log.d(TAG, "new intent " + intent);
        super.onNewIntent(intent);
        FirebasePlugin.onNotificationOpen(intent.getExtras());
    }

    public static void onNotificationOpen(Bundle bundle) {
        if (FirebasePlugin.callbackContext == null ) {
            Log.d("FirebasePlugin", "no callback context, onNotificationOpen ignored");
            return;
        }
        if (callbackContext != null && bundle != null) {
            JSONObject json = new JSONObject();
            Set<String> keys = bundle.keySet();
            for (String key : keys) {
                try {
                    json.put(key, bundle.get(key));
                } catch (JSONException e) {
                    Log.d("FirebasePlugin", "onNotificationOpen: json exception");
                    PluginResult result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
                    result.setKeepCallback(true);
                    callbackContext.sendPluginResult(result);
                    return;
                }
            }
            Log.d("FirebasePlugin", "onNotificationOpen: send notification to javascript");
            PluginResult result = new PluginResult(PluginResult.Status.OK, json);
            result.setKeepCallback(true);
            callbackContext.sendPluginResult(result);
        }
    }

Humm ... Notei que as notificações não são entregues (ao dispositivo) quando o cliente não está em execução (foi eliminado).

Tente abrir uma notificação apresentada pelo firebase diretamente e veja o comportamento .. Eu acho que eles reiniciam a atividade principal que eu acho que é o comportamento correto.

Desculpe pelo atraso. Tentei com o 0.1.13, mas meu aplicativo é reiniciado depois que a notificação é aberta.

alguma novidade ?

Não consigo obter sucesso ou falha de retorno de chamada onNotificationOpen no Android usando 0.1.17. Nem o envio por meio da API ou GUI com uma carga útil funciona. Alguma sugestão?

Por que não criamos uma solicitação de pull com o código postado por @Mehuge ? Funciona

Tive que atualizar onNewIntent no FirebasePlugin para filtrar intents de inicialização normais, então meu código onNewIntent agora se parece com este

<strong i="6">@Override</strong>
public void onNewIntent(Intent intent) {
  Log.d(TAG, "new intent " + intent);
  super.onNewIntent(intent);
  Bundle data = intent.getExtras();
  if (data != null) {
    String id = data.getString("id");
    if (null != id) {
      FirebasePlugin.handleNotificationBundle(data);
    } else {
      Log.d(TAG, "Not a notification intent, ignored");
    }
  }
}

Não estou totalmente feliz com isso. Ele funciona procurando uma propriedade id no pacote de intent que meu código de servidor sempre envia. Não parece haver nada que o GCM ou FBM adicione de forma confiável ao pacote que possa ser usado para identificar o intent como uma notificação / mensagem. Às vezes, obtemos algumas propriedades do Google adicionadas (se estivermos abrindo uma notificação da bandeja), mas para mensagens e notificações entregues diretamente ao cliente quando está em primeiro plano, não há nada além dos dados na mensagem que informam que é uma intenção de inicialização por causa de uma notificação, pelo menos que eu pude ver.

Provavelmente existe uma maneira melhor de lidar com isso.

@Mehuge , você se importaria de criar gists com todos os arquivos que você tem?

Ok, aqui está https://gist.github.com/Mehuge/374ee24d9e18a6c7ccc171d3e521b7ad

Observe, entretanto, que existem alguns bits que são específicos para nosso aplicativo. Acabei movendo o código para o nosso projeto porque estava mudando muito. Em retrospecto, eu provavelmente deveria ter feito o fork do plugin e modificado dessa forma, mas neste ponto, eu estava muito atrasado e farto de tudo, então peguei o caminho mais rápido para fazer algo funcionar. Os bits personalizados são bastante óbvios, portanto, devem ser fáceis de excluir.

Observe também que, da forma como implementei o plug-in, ele precisa saber quando o cliente está pausado (ou, mais especificamente, não pausado) para que possa decidir se deve criar uma notificação ou entregar a mensagem diretamente. Você pode ou não precisar dessa funcionalidade, mas em nosso caso, não queríamos que as notificações que fossem enviadas quando o cliente estava em primeiro plano criassem notificações do Android, mas que fossem entregues diretamente ao cliente para lidar com elas. Para informar o plugin do estado de pausa do cliente, adicione o seguinte código à sua atividade principal.

<strong i="9">@Override</strong>
protected void onResume() {
    FirebasePlugin.setPaused(false);
    super.onResume();
}

<strong i="10">@Override</strong>
protected void onPause() {
    FirebasePlugin.setPaused(true);
    super.onPause();
}

Há outro problema com o qual você pode ter que lidar: se uma notificação chegar enquanto estiver em segundo plano, mas o usuário iniciar o aplicativo diretamente, em vez de abrir a notificação do Android, talvez seja necessário lidar com isso de alguma forma. Em nosso caso de aplicativos, eu poderia simplesmente limpar todas as notificações não abertas. Sua situação pode ser diferente.

Não estou totalmente satisfeito com o resultado final, algumas das chaves extras adicionadas, por exemplo, eram experimentais e não estão realmente sendo usadas, eu simplesmente não tive tempo de removê-las.

Estaria interessado em qualquer feedback ou melhorias sugeridas ou em apontar quaisquer falhas. Eu estaria particularmente interessado em encontrar uma maneira de detectar melhor uma intenção de lançamento com uma carga útil de GCM de um lançamento normal. Descobri que as propriedades do Google são adicionadas apenas em algumas circunstâncias. Além disso, minhas tentativas de detectar os diferentes tipos de notificações (is_push, is_notify, broadcast) não estão realmente funcionando.

veja # 108

Para mim, eu poderia resolver fazer a solicitação desta forma:

{
  "registration_ids": [...tokens],
  "notification" : {
        "title": "Notf title",
        "body": "Notification body"
     },
     "data": {
        "click_action": "/call/dwEugLJ9PTVdcFb064CX"
     }
}

Mas eu tive que pegar o click_action como parâmetro e fazer o redirecionamento manualmente (eu estava usando o Cordova com o app react).

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

rlz picture rlz  ·  4Comentários

matthitachi picture matthitachi  ·  5Comentários

michaelreiser picture michaelreiser  ·  5Comentários

rolinger picture rolinger  ·  5Comentários

JonSmart picture JonSmart  ·  3Comentários