[Tutorial Fortgeschrittene Folge 2] Fortsetzung RSS-Feed Firebase Cloud Messaging und Realtime Database

  • Antworten:6
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:06:30 via Website

Hier erweitern wir das Projekt von mir aus diesem Thread: [Tutorial für Fortgeschrittene] RSS-Feed zu Json und Weiterverarbeitung in Apps.
Wir werden die Firebase Realtime Database verwenden um das doppelte versenden von Push-Nachrichten über Firebase Cloud Messaging zu vermeiden.

TEIL 6

Erstellt euch auf der Firebase Console, entweder ein "neues Projekt" oder importiert ein "Google-Projekt".

User uploaded photo


Wenn das geschafft ist - was, denke ich, selbsterklärend ist - gehen wir, wenn nicht automatisch, in unser Projekt. Dazu klickt ihr einfach auf User uploaded photo


In nächstem Schritt wählt ihr "Firebase meiner Android-App hinzufügen"

User uploaded photo

Wodurch ihr nun eure App-Daten eintragen könnt, wichtig ist das ihr exakt den Paketnamen von eurem Android-Projekt eintragt. Der SHA1-Wert ist in unserem Fall nicht wichtig. Mit dem Klick auf Hinzufügen wird eine Datei (google-services.json) erstellt und in eurem Browser heruntergeladen.

User uploaded photo


Die Datei (google-services.json) ist sehr wichtig, für den weiteren Verlauf, für das Android-Projekt. In dem Fenster wird euch beschrieben wo ihr diese Datei (google-services.json) einfügen müsst und was in den build.gradle Dateien ergänzen werden muss, bitte mach das wie beschrieben.


Eure App wurde nun hinzugefügt und ist als Modul dargestellt. In diesem Modul ist, in der rechten oberen Ecke, ein Drei-Punkte-Menu, darauf klicken und Verwalten auswählen.

User uploaded photo


Oben in der Navigationsleiste findet ihr den Punkt Allgemein von da benötigen wir die Project ID und Cloud Messaging unter diesem ist der Serverschlüssel aufgelistet.
(!) Diesen Schlüssel nicht öffentlich stellen (!)

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.Andy N.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:07:58 via Website

TEIL 7

Bevor mit der Android-App weiter gemacht wird, kümmern wir uns um die serverseitigen Funktionen. Zunächst erstellen wir eine Utility-Datei (utils.php) um unseren Code übersichtlich zu halten. Sie wird im laufe immer mal wieder erweitert.

Euch wird vielleicht aufgefallen sein, dass die Datumanzeige nicht der Deutschenformatierung entspricht.

IST: Wed, 13 Jul 2016 18:49:02 GMT
SOLL: 13.07.2016 18:49:02

utils.php

<?php

    function createDate($oldDate) {
        return DateTime::createFromFormat('D, d M Y H:i:s', substr($oldDate, 0, strlen($oldDate)-4))->format('d.m.Y H:i:s');
    }
?>

in der rss.php muss folgendes geändert werden:
aus

$json['pubDate'] =         $parentElement->getElementsByTagName('pubDate')->item(0)->firstChild->nodeValue;

wird

$json['pubDate'] =         createDate($parentElement->getElementsByTagName('pubDate')->item(0)->firstChild->nodeValue);

aus

$pubDate = $item->getElementsByTagName('pubDate')->item(0)->firstChild->nodeValue;

wird

$pubDate = createDate($item->getElementsByTagName('pubDate')->item(0)->firstChild->nodeValue);


Über dem Eintrag header("Content-type: application/json; charset=utf-8"); fügen wir nun die require_once('./utils.php'); Zeile ein, dadurch ist die Funktion in der rss.php aufrufbar.

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

Pascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:09:58 via Website

TEIL 8

Es folgt jetzt der Grundaufbau für die Firebase Cloud Messaging in PHP. Der einfach halber nennen wir sie index.php. Für das Script benötigen wir den Serverschlüssel und die Firebase Cloud Messaging Url.

index.php

<?php 
    define(SERVER_KEY, "");                 // euer Serverschlüssel
    define(TOPIC_ADRESS, "/topics/info");   // identifiziert unsere Nachricht als unsere und sendet an alle Geräte die registriert sind
    define(FIREBASE_SERVER_FCM, "https://fcm.googleapis.com/fcm/send");
    header("Content-type: application/json; charset=utf-8");

    $result_rss    = requireToVar('./rss.php');        // hier wird unsere rss.php eingelesen um den generierten JsonString zu verwenden
    $items         = json_decode($result_rss, true)['item'];  // filtert nur die Item's

    foreach($items as $item) {
        if (substr($item['pubDate'], 0, strlen($item['pubDate'])-9) == date('d.m.Y')) {       
            echo "heute";
        } else {
            echo "anderer Tag";
        }
    }

    //
    // einlesen von Server eigenen Dateien
    //
    function requireToVar($file){
        ob_start();      // Ausgabepufferung aktivieren, verhindert das unsere rss.php gleich ausführt wird
        require($file);
        return ob_get_clean();
    }
?>

Mit er foreach-Schleife durchlaufen wir alle Item's Objekte, zunächst benötigen wir erstmal nur das Datum, welches in TEIL 7 in das Deutschformat geändert wurde.
In der if-Abfrage schauen wir, ob das Datum im Feed mit dem deutschen Server-Datum übereinstimmt. Warum nutzen wir aber substr? Ganz einfach unser pubDate hat die Uhrzeit mit im String, die muss da raus.

Wenn ihr das Script nun ausführt sollte erst heute aufgelistet werden und danach anderer Tag.

Alles O.K.? Gut dann zur utils.php und eine Funktionen hinzufügen, sie sendet nachher unsere Nachricht -> Sending topic messages from the server

utils.php

function send($title, $creator, $link) {
    $post_content = array('to' => TOPIC_ADRESS, 'data' => array('title' => $title, "creator" => $creator, "link" => $link));
    $httpheader = array('Content-Type:application/json', 'Authorization:key='.SERVER_KEY);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, FIREBASE_SERVER_FCM);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_content));
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}

In der if-Abfrage von der index.php ergänzen wir eine Zeile.

index.php

echo send($item['title'], $item['creator'], $item['link']);

Den echo-Befehl habe ich nur zur Kontrolle drin, dieser sollte bei einem Produktivsystem entfernt werden.
Unser PHP-Script ist nun sendebereit.

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:12:31 via Website

TEIL 9

Es folgt jetzt der Grundaufbau für die Firebase Realtime Database in PHP. Für die Ergänzung benötigen wir die Firebase Realtime Database Url und die Project ID.
Beide tragen wir in die index.php ein.

index.php

<?php 
    define(SERVER_KEY, "");             // euer Serverschlüssel
    define(TOPIC_ADRESS, "/topics/info");
    define(FIREBASE_SERVER_FCM, "https://fcm.googleapis.com/fcm/send");
    define(FIREBASE_SERVER_DATABASE, "https://{Prject ID}.firebaseio.com/");        // eure Project ID eingeben

In der utils.php ergänzen wir zwei Funktionen. Die Erste soll helfen unsere Datenbank abzufragen ob ein Feed schon gesendet wurde und die Zweite schreibt welcher Feed schon gesendet wurde.

utils.php

function url_get_contents ($url) {
    if (!function_exists('curl_init')){ 
        die('CURL is not installed!');
    }
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $output = curl_exec($ch);
    curl_close($ch);
    return $output;
}

function put($key, $value) {
    $firebase_server = FIREBASE_SERVER_DATABASE."feeds.json";
    $httpheader = array('Content-Type:application/json');
    $post_content = array($key => $value);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $firebase_server);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_content));
    $response = curl_exec($ch);
    curl_close($ch);
    return $response;
}

Die index.php muss, in der if-Abfrage, nun vervollständigt werden. In der Datenbank selber schreiben wir den Schlüssel aus dem md5-Wert und zur Kontrolle das Datum vom Feed. In der Variable $file_get wird der Rückgabewert zugewiesen, so das in einer weiteren if-Abfrage der Wert auf null geprüft werden kann. Wenn er null ist, wissen wir, dass der Feed noch nicht gesendet wurde.

index.php

foreach($items as $item) {
    if (substr($item['pubDate'], 0, strlen($item['pubDate'])-9) == date('d.m.Y')) {
        $file_get = url_get_contents(FIREBASE_SERVER_DATABASE."feeds/" . md5($item['title'].$item['pubDate']) . ".json");

        if ($file_get == "null") {
            echo send($item['title'], $item['creator'], $item['link']);
            put(md5($item['title'].$item['pubDate']), $item['pubDate']);
        }
    }
}

Vorerst war das der Teil zu den PHP-Scripts.

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:12:52 via Website

Die beiden Dateien sollten fertig so aussehen.

index.php

<?php 
    define(SERVER_KEY, "");
    define(TOPIC_ADRESS, "/topics/info");
    define(FIREBASE_SERVER_FCM, "https://fcm.googleapis.com/fcm/send");
    define(FIREBASE_SERVER_DATABASE, "https://{Projct ID}.firebaseio.com/");
    header("Content-type: application/json; charset=utf-8");

    $result_rss    = requireToVar('./rss.php');
    $items         = json_decode($result_rss, true)['item'];

    foreach($items as $item) {
        if (substr($item['pubDate'], 0, strlen($item['pubDate'])-9) == date('d.m.Y')) {
            $file_get = url_get_contents(FIREBASE_SERVER_DATABASE."feeds/" . md5($item['title'].$item['pubDate']) . ".json");

            if ($file_get == "null") {
                echo send($item['title'], $item['creator'], $item['link']);
                put(md5($item['title'].$item['pubDate']), $item['pubDate']);
            }
        }
    }

    function requireToVar($file){
        ob_start();
        require($file);
        return ob_get_clean();
    }
?>

utils.php

<?php

    function createDate($oldDate) {
        return DateTime::createFromFormat('D, d M Y H:i:s', substr($oldDate, 0, strlen($oldDate)-4))->format('d.m.Y H:i:s');
    }

    function url_get_contents ($url) {
        if (!function_exists('curl_init')){ 
            die('CURL is not installed!');
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $output = curl_exec($ch);
        curl_close($ch);
        return $output;
    }

    function send($title, $creator, $link) {
        $post_content = array('to' => TOPIC_ADRESS, 'data' => array('title' => $title, "creator" => $creator, "link" => $link));
        $httpheader = array('Content-Type:application/json', 'Authorization:key='.SERVER_KEY);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, FIREBASE_SERVER_FCM);
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_content));
        $result = curl_exec($ch);
        curl_close($ch);
        return $result;
    }

    function put($key, $value) {
        $firebase_server = FIREBASE_SERVER_DATABASE."feeds.json";
        $httpheader = array('Content-Type:application/json');
        $post_content = array($key => $value);
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $firebase_server);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_content));
        $response = curl_exec($ch);
        curl_close($ch);
        return $response;
    }
?>

— geändert am 16.07.2016, 10:13:14

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

15.07.2016, 21:15:59 via Website

TEIL 10

Im TEIL 6 haben wir die build.gradle vorbereitet. Jetzt fehlt uns, in der {project}/{app}/build.gradle Datei, noch die Library aufnahme von Firebase Cloud Messaging.
Ergänzt im Tag dependencies { ... } und Synchronisiert mit Sync now

compile 'com.google.firebase:firebase-messaging:9.2.1'

Im nächsten Abschnitt werden wir uns mit der "Registrierung" im Firebase-System beschäftigen. Viel Code ist es aber nicht ;)
Firebase Cloud Messaging bietet dafür gute Klassen, was uns eine menge Arbeit spart. Wir benötigen ganze zwei Klassen zur Erweiterung, also ran an die Arbeit und fertig werden :D

Die erste ist die FireBase_Instance_ID_Service.java, diese erweitern wir mit FirebaseInstanceIdService. Durch die erstellte Klasse werden wir im Firebase-System registriert und zeigen Firebase Cloud Messaging, in seiner Instanz, welches Topic registriert werden soll. Den Topic haben wir in der index.php schon definiert. Beide müssen identisch sein. Dazu muss die onTokenRefresh-Methode überschrieben werden.

FireBase_Instance_ID_Service.java

public class FireBase_Instance_ID_Service extends FirebaseInstanceIdService {

    protected final static String TOPIC = "info";

    @Override
    public void onTokenRefresh() {
        super.onTokenRefresh();
        FirebaseMessaging.getInstance().subscribeToTopic(TOPIC);
    }
}

Nun benötigen wir noch die Empfangsklasse FireBase_Messaging_Service.java diese wird mit FirebaseMessagingService erweitert.
Was wollen wir? Eine Notification soll erscheinen, die uns dann durch daraufklicken in den Artkiel befördert.
Auch hier müssen wir eine Methode (onMessageReceived) überschreiben. In der Methode fangen wir die Nachricht vom Topic, aber, bevor wir jede Topic-Nachricht in unsere Notification einbringen prüfen wir mittels if-Abfrage ob es auch unsere ist die gebraucht wird.

FireBase_Messaging_Service.java

public class FireBase_Messaging_Service extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        if (remoteMessage.getFrom().equals("/topics/" + FireBase_Instance_ID_Service.TOPIC)) {
            Notification notification = new NotificationCompat.Builder(this)
                    .setContentTitle(String.format("von %s", remoteMessage.getData().get("creator")))
                    .setContentText(remoteMessage.getData().get("title"))
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setDefaults(Notification.DEFAULT_ALL)
                    .setAutoCancel(true)
                    .setStyle(new NotificationCompat.BigTextStyle().bigText(remoteMessage.getData().get("title")))
                    .build();
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(remoteMessage.getData().get("link")));
            PendingIntent contentIntent = PendingIntent.getActivity(getBaseContext(), 0, intent, 0);

            NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
            notification.contentIntent = contentIntent;
            notificationManager.notify(getID(), notification);
        } 
    }

    private final static AtomicInteger c = new AtomicInteger(0);

    public static int getID() {
        return c.incrementAndGet();
    }
}

Der letzte Schritt! Der Eintrag in der AndroidManifest.xml

<service android:name=".FireBase_Messaging_Service">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT"/>
    </intent-filter>
</service>

<service android:name=".FireBase_Instance_ID_Service">
    <intent-filter>
        <action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
    </intent-filter>
</service>

— geändert am 09.10.2016, 13:04:53

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.

Antworten
Ludy
  • Admin
  • Forum-Beiträge: 7.958

16.07.2016, 09:56:32 via App

TEIL 11

Um eine automatisiertes Verfahren zu realisieren, verwenden wir einen Cronjob. Da nicht jeder Webhoster, das anbietet hab ich mal diesen hier gewählt -> https://cron-job.org/de/. Registriert euch und erstellt euch ein Cronjob. Die maximale Zeit für das ausführen des Scripts darf 30 Sekunden nicht überschreiten, daher werden wir unsere Textausgabe in der index.php entfernen.

Entfernt das "echo" vor dem send(...); und setzt ein break, das bewirkt, dass immer nur ein Feed-Item angestoßen wird um einen Timeout aus dem Wege zu gehen. Probiert es euch aus.

aus

echo send($item['title'], $item['creator'], $item['link']);

wird

send($item['title'], $item['creator'], $item['link']);

        if ($file_get == "null") {
            send($item['title'], $item['creator'], $item['link']);
            put(md5($item['title'].$item['pubDate']), $item['pubDate']);
            break; // verhindert das ein weiters Item abgehandelt wird
        }

Das vollständige Projekt findet ihr hier.

— geändert am 16.07.2016, 11:05:14

Gruß Ludy (App Entwickler)

Mein Beitrag hat dir geholfen? Lass doch ein "Danke" da.☺

☕ Buy Me A Coffee ☕

Lebensmittelwarnung-App

✨Meine Wunschliste✨

📲Telegram NextPit News📲

swa00Pascal P.

Antworten