Apple Kalender mit Java auslesen

Apple Kalender mit Java auslesen.

Motivation

Apple Kalender-Einträge sollen synchron auf meinem Java-Dashboard sowie auf sämtlichen anderen Geräten im Haus gepflegt werden.
Aktuell bietet Apple jedoch nur die Möglichkeit über eine ICS-Datei Kalenderdaten auszulesen. In diesem Blogeintrag wird eine selbstgeschriebene Bibliothek vorgestellt, mit deren Hilfe Informationen aus der Datei zur einfachen Weiterverarbeitung in einer Datenstruktur abgelegt wird.

Benötigte Komponenten

  1. Apple-ID und vorhandener Kalender
  2. Java ab Version 8

Kalender freigeben

Bevor ein Zugriff von außen auf einen Kalender möglich ist, muss dieser zuerst freigegeben werden. Dies ist über die iCloud (https://www.icloud.com) möglich. Anschließend auf Kalender klicken. Alle vorhandenen Kalender werden nun auf der linken Seite gelistet. Über das „Funksymbol“ kann der Kalender nun freigegeben werden.
Adblock
Wird das „Funksymbol“ nicht angezeigt, kann dies am eingeschalteten Adblock liegen.
Apple Kalender freigeben

Kalender einlesen

Benutzung der AppleCalendar- Class

 AppleCalendar ct = new AppleCalendar("caldav-URL hier eintragen");
 HashMap<String, CalendarObject> xx = ct.readCalendarUrl();

Im Constructor muss zuerst die vorher freigegebene Kalender URL eingetragen werden („webcall“ durch https ersetzen).
Mit der read-Methode lässt sich alles in eine HashMap einlesen.

AppleCalendar- Class

Nachfolgend eine Beschreibung der Calendar-Class. Im ersten Schritt wird alles eingelesen und in ein Calendar-Object gelesen.  Die gelieferten Information aus dem Apple Kalender sehen in etwa so aus:

BEGIN:VEVENT
CREATED:20171122T061139Z
DTEND;VALUE=DATE:20171128
DTSTAMP:20210207T114913Z
DTSTART;VALUE=DATE:20171127
LAST-MODIFIED:20210207T114904Z
RELATED-TO;RELTYPE=X-CALENDARSERVER-RECURRENCE-SET:220BBBAE-EAE8-4470-939
 B-D39704B5490A
RRULE:FREQ=YEARLY;UNTIL=20201127
SEQUENCE:0
SUMMARY:Test Geburtstag
UID:08E483F0-9CA7-415E-B3FE-85C728DAE147
END:VEVENT

Aus diesen Metadaten lassen sich sämmtliche Informationen über den Termin rauslesen. RRULE zeigt z.b. die Häufigkeit oder das Endedatum des Termins. Diese Daten müssen jetzt in dem Datenobjekt abgelegt werden. Dies geschieht mit der buildHashmap-Methode.

private CalendarObject buildHashMap(HashMap<String, CalendarObject> calendarObjects,
    CalendarObject currentCalendarObject, String currentLine) {
    if (currentLine.startsWith("BEGIN:")) {
        currentCalendarObject = new CalendarObject();
    } else if (currentLine.startsWith("END:")) {
        writeToHashmap(calendarObjects, currentCalendarObject);
    } else {
        handleCreated(currentCalendarObject, currentLine);
        handleDtend(currentCalendarObject, currentLine);
        handleDtstamp(currentCalendarObject, currentLine);
        handleDtstart(currentCalendarObject, currentLine);
        handleSummary(currentCalendarObject, currentLine);
        handleRrule(currentCalendarObject, currentLine);
        handleUid(currentCalendarObject, currentLine);
    }
    return currentCalendarObject;
}
private void writeToHashmap(HashMap<String, CalendarObject> calendarObjects, CalendarObject currentCalendarObject) {
    if (currentCalendarObject.getRrule() == null || ! currentCalendarObject.getRrule().contains("UNTIL")) {
        calendarObjects.put(currentCalendarObject.getUid(), currentCalendarObject);
    }
}
private void handleUid(CalendarObject currentCalendarObject, String currentLine) {
    String searchString = "UID:";
    if (currentLine.startsWith(searchString)) {
        currentCalendarObject.setUid(substringSearchStringLength(currentLine, searchString));
    }
}
private void handleRrule(CalendarObject currentCalendarObject, String currentLine) {
    String searchString = "RRULE:";
    if (currentLine.startsWith(searchString)) {
        currentCalendarObject.setRrule(substringSearchStringLength(currentLine, searchString));
    }
}

Die untersten Methoden wurden in diesem Tutorial bewusst weggelassen, da sie gleichartig zu handleRrule sind. Das Einlesen startet mit dem „BEGIN:“-Tag. Hiermit wird ein neues Calendar-Object angelegt und alle Informationen des aktuellen Kalendereintrags dort abgespeichert. Sobald „END:“ gelesen wird, ist der Kalendereintrag zu ende und es erfolgt das Schreiben in die Hashmap.
Die readCalendarUrl-Methode verwendet die URL und baut einen Stream auf. Über einen bufferedReader werden die Zeilen nacheinander eingelesen und zur buildHashMap-Methode weitergegeben.

public HashMap<String, CalendarObject> readCalendarUrl() throws MalformedURLException, IOException {
    HashMap<String, CalendarObject> calendarObjects = new HashMap<String, CalendarObject>();
    CalendarObject currentCalendarObject = null;
    URL calendarURL = new URL(url);
    URLConnection connection = calendarURL.openConnection();
    BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
    while (reader.ready()) {
        String currentLine = reader.readLine();
        currentCalendarObject = buildHashMap(calendarObjects, currentCalendarObject, currentLine);
    }
    return calendarObjects;
}

AppleCalendarOperations- Class

Nachdem nun sämmtliche Informationen in einer Hashmap vorliegen, lassen sich verschiedene Operationen darauf ausführen. Beispielsweise lässt sich der Frage nachgehen „Wann hat xy Geburtstag?“

public CalendarObject searchForSummary(String searchString) {
    for (Map.Entry<String, CalendarObject> e : appleCalendarData.entrySet()) {
        String currentSummary = appleCalendarData.get(e.getKey()).getSummary();
        if (currentSummary != null && currentSummary.trim().equals(searchString)) {
            return appleCalendarData.get(e.getKey());
        }
    }
    return null;
}

Da die Class eine Hashmap entgegen nimmt, könnte der vollständige Anwendungscode in etwa so aussehen:

AppleCalendar ct = new AppleCalendar(
    "https://KALENDERURL");
HashMap<String, CalendarObject> xx = ct.readCalendarUrl();
AppleCalendarOperations co = new AppleCalendarOperations(xx);
 CalendarObject result = co.searchForSummary("test");
result.getDtstart();

Dtstart liefert das Tagesdatum des angelegten Termines, in unserem Fall also den Geburtstag.

Fazit

Das Potential dieser Class ist bei weitem nicht ausgeschöpft, es liefert aktuell nur Basisfunktionen, um Personen zu suchen oder abzufragen, wer an einem bestimmten Datum Geburstag hat. Mit Sicherheit gibt es noch deutlich mehr Anwendungsfälle. Diese lassen sich problemlos in der  Operations-Class hinzufügen. Auch das Anlegen eines Termines ist noch nicht berücksichtigt. Nach und nach werden je nach Anwendungsfall weitere Funktionen hinzukommen.
Den ganzen Code gibts auf Github schaut doch mal rein.

Interne Schulungen in Cucumber & Selenium


In geschützter Umgebung lernt und festigt Ihr Team individuelles Wissen für Ihre aktuellen Anforderungen. Damit Ihr Projekt erfolgreich und wie geplant abgeschlossen wird.

Das könnte Sie auch interessieren

Sealed Classes mit Java JDK 17

Sealed Classes mit Java JDK 17 Was sind Sealed Classes? Mit dem kommenden Java Release 17 werden sogenannte Sealed Classes eingeführt. Diese waren zuvor bereits seit...

API Testing mit Java und WireMock

API Testing mit Java und WireMock Motivation Ein bekanntes Problem: Öffentliche APIs werden abgefragt und liefern Daten in Form von JSON oder anderen Formaten zurück....

Behaviour Driven Development (BDD) mit Cucumber und Gherkin Schulung

Behaviour Driven Development (BDD) mit Cucumber Schulung Schulungsbeschreibung In dieser Schulung lernen Teilnehmer*innen den sicheren Umgang mit BDD, Cucumber und...

Fünf Tipps für besseren Testcode

Fünf Tipps für besseren Testcode Good Practices In diesem Blogpost zeige ich fünf Good Practices auf, um den eigenen Testcode zu verbessern. Aber wieso nur "Good...

Akzeptanztests mit Gauge und Java

Akzeptanztests mit Gauge und Java 1. Einführung In diesem Blogeintrag werde ich zunächst auf Gauge eingehen, die Installation vorstellen und anschließend ein Beispiel...

Licht steuern mit Java und Philips Hue

Licht steuern mit Java und Philips Hue Motivation Für meinen selbstprogrammierten Sprachassistenten wollte ich ein Modul für die Lichtsteuerung entwickeln. Ziel war es,...

ReSpeaker 4 LEDs mit Pi4J steuern

ReSpeaker 4 LEDs mit Pi4J steuern Motivation Für ReSpeaker im allgemeinen gibt es bereits zahlreiche Bibliotheken, auch einige die LEDs über die GPIO Pins ansteuern....

Unit Tests mit AI – Machinet Testfallerstellung

Unit Tests mit AI - Machinet Testfallerstellung Werden Tester bald durch KIs ersetzt? Spoiler: Nein In diesem Blogbeitrag möchte ich auf ein paar Grundlagen der KI...

Rolladensteuerung mit Loxone und Java

Automatische Rolladensteuerung mit Java und Loxone Motivation Loxone ist ein bekannter Anbieter für Smarthome Rolläden. Während sich über die mitgelieferte Oberfläche...