Übersicht Pluginentwicklung

Aus TV-Browser Wiki
Version vom 5. Dezember 2008, 08:40 Uhr von Bananeweizen⧼word-separator⧽⧼parentheses⧽ ⧼parentheses⧽
⧼revision-nav⧽
Wechseln zu: Navigation⧼comma-separator⧽Suche

In dieser Übersicht werden alle Möglichkeiten aufgeführt, wie ein Plugin mit TV-Browser oder anderen Plugins interagieren kann.

Wichtig: Diese Übersicht zeigt, wie man Plugins für das neue Plugin-System von TV-Browser 2.6 schreibt. Ein Plugin das diese neuen Möglichkeiten nutzt, funktioniert nicht in TV-Browser-Versionen kleiner als 2.6, mit Ausnahme von TV-Browser 2.2.4, welcher einige Funktionen der neuen Schnittstelle enthält. Es ist also möglich Plugins zu schreiben, die sowohl unter 2.2.4 als auch unter 2.6 laufen, wenn man nur die Funktionen der Schnittstelle aus TV-Browser 2.2.4 verwendet.

Grundlagen

Um ein TV-Browser-Plugin zu schreiben, muss man eine Klasse erstellen, die von devplugin.Plugin abgeleitet wird. Damit das Plugin geladen werden kann muss es sich an eine bestimmte Namenskonvention halten. Das Plugin muss in einem Paket liegen, dass den gleichen Namen wie die von devplugin.Plugin abgeleitete Klasse hat, allerdings alles in Kleinbuchstaben.

Beispielsweise heißt das Plugin MyPlugin, dann muss das Paket myplugin heißen.

Beispiel:

package myplugin;
import devplugin.*;

public class MyPlugin extends devplugin.Plugin
{
...
}

Die Methoden aus devplugin.Plugin können dann überschrieben werden um verschiedene Funktionen bereitzustellen. Da TV-Browser zwei Verzeichnisse berücksichtigt, die Plugins enthalten können, einmal das plugins-Verzeichnis im Programmverzeichnis und das plugins-Verzeichnis im Einstellungsverzeichnis des Benutzers, ist es ab TV-Browser 2.2.4 und 2.6 notwendig, dass alle Plugins und TvDataServices die Methode

public static Version getVersion()

enthalten. Diese dient zur Überprüfung, welche Plugindatei die aktuellste Version enthält. Die aktuellste Version wird dann von TV-Browser geladen.

Wichtig: Die Versionierung von TV-Browser Plugins ist aus historischen Gründen etwas anders aufgebaut, als man das erwarten würde. Deshalb ist es empfehlenswert die genaue Beschreibung der Versionierung zu lesen.

Außerdem sollte jedes Plugin die Methode

public PluginInfo getInfo()

überschreiben um wenigstens den Namen und die Beschreibung des Plugins festzulegen. Es ist mit dieser Methode aber auch möglich zusätzlich den Autor, eine Internetseite mit einer Hilfe zum Plugin und die Lizenz des Plugins anzugeben.

Was man auf jeden Fall unterlassen muss

Ein TV-Browser Plugin darf niemals Klassen der Kernkomponenten von TV-Browser verwenden. Verwendet werden dürfen nur die Klassen aus den Paketen devplugin, tvdataservice und util (und natürlich aus den Paketen die sich in diesen Paketen befinden). Wir haben uns etwas dabei gedacht, dass manche Funktionen, die die Klassen in den Kernkomponenten bieten, nicht für Plugins zur Verfügung stehen. Wenn also etwas nicht geht, dann macht einen Verbesserungsvorschlag, dass die Plugin-Schnittstelle oder eine Utils-Funktion angepasst werden soll. Es könnte natürlich durchaus vorkommen, dass ein solcher Verbesserungsvorschlag auch abgelehnt wird.

Dafür achten wir in den Paketen devplugin, tvdataservice und util besonders auf Kompatibilität mit bisherigen Versionen der Klassen, so dass man sich darauf verlassen kann, dass ein Plugin, was nur diese Klassen verwendet auch mit einer zukünftigen Version von TV-Browser funktioniert. Natürlich gilt dies nicht ewig, die Klassen/Methoden werden erst deprecated und dann, nach Ankündigung, in einer besonders evolutionären Version von TV-Browser (also Versionen, bei denen sich die erste Stelle der Version ändert) entfernt. Bei Klassen und Methoden aus den Kernkomponenten von TV-Browser achten wir hingegen nicht auf diese Kompatibilität, verwendet also ein Plugin diese Klasse, kann es passieren, dass diese mit einer neuen Version von TV-Browser völlig anders aussieht, da es hier keine Deprecations gibt. Im schlimmsten Fall, könnte also ein Plugin, welches sich nicht an die Regeln hält die Funktion von TV-Browser komplett lahm legen.

Funktionen des Plugins

Auswahl über die Einstellungen

Einstellungen MarkerPlugin.png

Der Einstellungen-Dialog sieht für jedes Plugin, das diese Funktion unterstützt, einen eigenen Karteireiter vor. Auf diese Weise kann der Benutzer ein bestimmtes Plugin konfigurieren.

Implementierung:

public SettingsTab getSettingsTab()

Diese Methode gibt das SettingsTab des Plugins zurück. Ein SettingsTab besteht aus einem JPanel, in dem die Einstellungen vorgenommen werden können, einer Methode zum Abspeichern der Einstellungen, aus einem Titel und einem Icon. Letztere werden dann im Einstellungen-Dialog gezeigt.

Auswahl über das Menü

Plugin Menue.png

Plugins können aber auch direkt über das Menü oder die Symbolleiste aufgerufen werden. Plugins, die diese Funktion unterstützen, erscheinen im Menü und in der Symbolleiste.

Das Erinnerungen-Plugin kann auf diese Weise die gespeicherten Erinnerungen anzeigen. Das Sendungsinfo-Plugin hingegen unterstützt diese Funktion nicht (und wird deshalb auch nicht angezeigt).

Implementierung:

public ActionMenu getButtonAction()

Gibt die Aktion zurück, die ausgeführt werden soll, wenn das Plugin über im Menü angewählt wurde.

Beispiel:

Der folgende Code zeigt einen Dialog, sobald das Plugin im Menü ausgewählt wurde.

public ActionMenu getButtonAction() {
  // Eine Aktion erzeugen, die die Methode showDialog() aufruft, sobald sie ausgelöst wird.
  AbstractAction action = new AbstractAction() {
    public void actionPerformed(ActionEvent evt) {
      showDialog();
    }
  };

  // Der Aktion einen Namen geben. Dieser Name wird dann im Menü und in der Symbolleiste gezeigt
  action.putValue(Action.NAME, "Dialog anzeigen");

  // Der Aktion ein kleines Icon geben. Dieses Icon wird im Menü gezeigt
  // Das Icon sollte 16x16 Pixel groß sein
  action.putValue(Action.SMALL_ICON, createImageIcon("SomeCategory","SomeIcon",16));

  // Der Aktion ein großes Icon geben. Dieses Icon wird in der Symbolleiste gezeigt
  // Das Icon sollte 22x22 Pixel groß sein
  action.putValue(BIG_ICON, createImageIcon("SomeCategory","SomeIcon",22));

  // Das Aktions-Menü erzeugen und zurückgeben
  return new ActionMenu(action); 
}


protected void showDialog() {
  // Einen modalen Dialog erzeugen
  JDialog dlg = UiUtilities.createDialog(getParentFrame(), true);

  // Hier den Dialog füllen
  ...

  // Dialog zeigen
  dlg.show();
}

Auswahl über eine Sendung

Programmtabelle Kontextmenue.png

Klickt der Benutzer mit der rechten Maustaste auf eine Sendung, so werden jene Plugins angezeigt, die auf dieses Ereignis reagieren können. Hier z.B das Erinnerungen-Plugin sowie das Sendungsinfo-Plugin. Der Benutzer kann nun ein Plugin wählen, das ausgeführt werden soll. Das Plugin erhält dann alle vorliegenden Informationen über die jeweilige Sendung. Das Erinnerungen-Plugin beispielsweise fügt die Sendung zu den Erinnerungen hinzu, das Sendungsinfo-Plugin zeigt die Sendungs-Informationen an.

Implementierung:

public ActionMenu getContextMenuActions(Program program)

Gibt die Aktionen zurück, die das Plugin für die übergebene Sendung anbietet.

ACHTUNG: TV-Browser ruft diese Methode an verschiedenen Stellen mit der Beispielsendung aus dem PluginManager auf. Dies geschieht zum Beispiel zur Darstellung des Kontextmenüeintrags in den Einstellungen des TV-Browser-Kontextmenüs. Dieser Fall muss also vom Plugin berückschtigt werden. Es sollte also, wenn das Beispielprogramm in der Variable program steht, zumindest ein Dummy-Menü zurückgegeben werden, damit der Benutzer das Kontextmenü des Plugins auch aktivieren/deaktivieren kann.

Beispiel:

Angenommen, es soll ein Plugin geschrieben werden, das die Daten einer Sendung als E-Mail verschickt. Dann könnte die Implementierung so aussehen:

public ActionMenu getContextMenuActions(Program program) {
  // Eine Aktion erzeugen, die die Methode sendMail(Program) aufruft, sobald sie aktiviert wird.
  AbstractAction action = new AbstractAction() {
    public void actionPerformed(ActionEvent evt) {
      sendMail(getProgramFromContextMenuActionEvent(evt));
    }
  };

  // Der Aktion einen Namen geben. Dieser Name wird dann im Kontextmenü gezeigt
  action.putValue(Action.NAME, "Sendungsinfos als E-Mail versenden");

  // Der Aktion ein Icon geben. Dieses Icon wird mit dem Namen im Kontextmenü gezeigt
  // Das Icon sollte 16x16 Pixel groß sein
  action.putValue(Action.SMALL_ICON, createImageIcon("SomeCategory","SomeIcon",16));

  // Das Aktions-Menü erzeugen und zurückgeben
  return new ActionMenu(action); 
}


protected void sendMail(Program program) {
  // Hier die Mail verschicken
  ...
}

Soll das Kontextmenü einen Separator enthalten kann man diesen mit folgendem Aufruf dem Menü hinzufügen:

ContextMenuSeparatorAction.getInstance();

Es ist auch möglich festzulegen, dass der Separator nur im Kontextmenü, nicht jedoch im Taskmenü des Sendungsinfobetrachters sichtbar ist, dazu muss folgender Separator zum Kontextmenü hinzugefügt werden:

ContextMenuSeparatorAction.getDisabledOnTaskMenuInstance();

Beispiel:

public ActionMenu getContextMenuActions(final Program program) {
  // Eine Aktion erzeugen, die die Methode sendMail(Program) aufruft, sobald sie aktiviert wird.
  ContextMenuAction contextMenu = new ContextMenuAction("Beispielaktion");

  AbstractAction action1 = new AbstractAction() {
    public void actionPerformed(ActionEvent evt) {
      program.mark(MyPlugin.getInstance());
    }
  };

  // Der Aktion einen Namen geben. Dieser Name wird dann im Kontextmenü gezeigt
  action1.putValue(Action.NAME, "Sendung markieren");

  // Der Aktion ein Icon geben. Dieses Icon wird mit dem Namen im Kontextmenü gezeigt
  // Das Icon sollte 16x16 Pixel groß sein
  action1.putValue(Action.SMALL_ICON, createImageIcon("SomeCategory","SomeIcon",16));

  AbstractAction action1 = new AbstractAction() {
    public void actionPerformed(ActionEvent evt) {
      program.unmark(MyPlugin.getInstance());
    }
  };

  // Der Aktion einen Namen geben. Dieser Name wird dann im Kontextmenü gezeigt
  action2.putValue(Action.NAME, "Sendung ent-markieren");

  // Der Aktion ein Icon geben. Dieses Icon wird mit dem Namen im Kontextmenü gezeigt
  // Das Icon sollte 16x16 Pixel groß sein
  action2.putValue(Action.SMALL_ICON, createImageIcon("SomeCategory","SomeOtherIcon",16));

  // Das Aktion-Menü mit Separator erzeugen und zurückgeben
  return new ActionMenu(contextMenu, new Action[] {action1, ContextMenuSeparatorAction.getInstance(), action2}); 
}

Markieren von Sendungen

Plugins haben die Möglichkeit, Sendungen zu markieren. Diese Sendungen werden dann in der Farbe der höchsten Priorität, mit der die Sendung markiert ist, dargestellt.

Programmtabelle Markierungen.png

Das Plugin kann dabei für alle Sendungen das gleiche Icon oder für jede Sendung ein eigenes Icon oder auch mehrere Icons gleichzeitig verwenden. Die Icons werden dann in der unteren rechten Ecke der Sendung angezeigt, so dass dadurch die Plugins, die die Sendung markieren, erkannt werden können.

Implementierung:

Wenn ein Plugin alle Sendungen mit dem gleichen Icon markieren möchte, muss das Plugin folgende Methode überschreiben:

public ThemeIcon getMarkIconFromTheme() {

Beispiel:

public ThemeIcon getMarkIconFromTheme() {
  return new ThemeIcon("devices", "printer", 16);
}

Das zurückgegebene Icon sollte 16x16 Pixel groß sein. Eine Sendung kann dann durch folgenden Aufruf markiert und ent-markiert werden:

Program program = ...;

// Markieren der Sendung
program.mark(this);

// Ent-Markieren der Sendung
program.unmark(this);

Soll die Sendung dabei in einer anderen als der Standardfarbe markiert werden, muss das Plugin folgende Methode überschreiben:

public int getMarkPriorityForProgram(Program p) {

Beispiel:

public int getMarkPriorityForProgram(Program p) {
  if(p != null) {
    if(p.getTitle().contains("Asterix")) {
      return Program.MAX_MARK_PRIORITY;
    }
  }
  
  return Program.MIN_MARK_PRIORITY;
}

Die Sendung wird dann mit der Farbe, die der Priorität entspricht angezeigt. Sollte die Sendung allerdings noch von einem anderen Plugin mit einer höheren Priorität versehen sein, wird die Farbe dieser Priorität angezeigt. Ein Plugin kann außerdem auch festlegen, dass die Sendung von ihm gar nicht farblich markiert werden soll, dazu muss die Methode folgendes zurückgeben:

public int getMarkPriorityForProgram(Program p) {
  return Program.NO_MARK_PRIORITY;
}

Das Plugin kann die Sendung aber auch mit mehreren Icons oder unterschiedliche Sendungen mit unterschiedlichen Icons markieren. Dazu muss folgende Methode überschrieben werden:

public Icon[] getMarkIconsForProgram(Program p) {

Beispiel:

public Icon[] getMarkIconsForProgram(Program p) {
  if(p != null) {
    if(p.getTitle().contains("Asterix")) {
      return new Icon[] {createImageIcon("emotes,"face-grin",16)};
    }
  }
  
  return new Icon[] {createImageIcon("SomeCategory","SomeIcon",16)};
}

Sendungseigenschaften mit Symbolen kennzeichnen

Neben der Markierung der Sendungen mit Farben und Symbolen, gibt es in TV-Browser die Möglichkeit die Sendungen Symbole zeigen zu lassen, die eine Eigenschaft der Sendung beschreiben. Diese Symbole werden (in der vom Benutzer eingestellten Reihenfolge) am linken Rand einer Sendung unter der Uhrzeit angezeigt. Das Bewertungs-Plugin zeigt zum Beispiel an dieser Stelle die Bewertung der Sendung an. Werden Symbole über diese Funktion in Sendungen eingefügt, hat das keinen Einfluss auf die Markierungsfarbe, da die Sendung von dieser Funktion nicht markiert wird.

Soll nun ein Plugin Symbole hinzufügen muss es folgende Methode überschreiben:

public Icon[] getProgramTableIcons(Program program) {

Außerdem muss dieser Eigenschaft ein Name zugewiesen werden, damit der Benutzer in den Einstellungen von TV-Browser erkennen kann, um welche Funktion es sich handelt. Dazu muss folgende Methode überschrieben werden:

public String getProgramTableIconText() {

Beispiel:

public Icon[] getProgramTableIcons(Program program) {
  if(program != null && program.getStartTime() >= 1200 && program.getStartTime() <= 1380) {
    // Symbole für die Sendung zurückgeben (hier ist es nur ein Symbol), das Symbol muss die Größe 16x16 haben
    return new Icon[] {createImageIcon("SomeCategory","SomeIcon",16)}
  }
  
  return new Icon[0];
}

public String getProgramTableIconText() {
  return "Primetimesendung";
}

Auf Datenupdates reagieren

TV-Browser informiert die Plugins mit 3 verschiedenen Methoden über Updates in den Daten. Ein Plugin kann dann auf dieses Update reagieren, indem es mindestens eine dieser Methoden überschreibt.

Die erste Methode informiert das Plugin über ein abgeschlossenes Datenupdate:

public void handleTvDataUpdateFinished() {

In dieser Methode kann das Plugin zum Beispiel nach neuen Sendungen suchen und diese dann markieren.

Wichtig: Das Plugin sollte diese Methode auch überschreiben, wenn es Sendungen markiert, um zu überprüfen, ob die markierten Sendungen nach einem Datenupdate noch vorhanden sind (es kann ja durchaus vorkommen, dass das Programm eines Senders geändert wurde), dies funktioniert folgendermaßen:

ArrayList<Program> mMyMarkedPrograms = new ArrayList<Program>(); 

...

public void handleTvDataUpdateFinished() {
  Program[] oldList = mMyMarkedPrograms.toArray(new Program[mMyMarkedPrograms.size()]);
  mMyMarkedPrograms.clear();

  for(Program program : oldList) {
    if(program.getProgramState() == Program.WAS_DELETED_STATE) {
      /* Können wir einfach ignorieren, da TV-Browser diese Sendung bereits ent-markiert hat
       * würde das Plugin aber mit der Sendung noch was anderes machen müssen, zum Beispiel sie
       * aus einer Aufnahmeprogrammierung löschen, würde man dies jetzt hier machen. 
       */
    }
    else if(program.getProgramState() = Program.WAS_UPATED_STATE) {
      /* An einer Sendung mit der Status Program.WAS_UPDATE_STATE wurde während
       * des Datenupdates etwas verändert, von einer solchen Sendung müssen wird die neue Instanz
       * laden und unsere Liste hinzufügen. Markieren müssen wir die Sendung nicht, da dies TV-Browser
       * während des Datenupdates bereits gemacht hat.
       */
      mMyMarkedPrograms.add(getPluginManager().getProgram(program.getDate(),program.getID()));
    }
    else {
      /* Wenn wir hier landen, ist die Sendung weder gelöscht noch verändert worden, wir
       * können also die bisherige Instanz weiter benutzen, da dies weiterhin die Aktuelle ist.
       */
      mMyMarkedPrograms.add(program);
    }
  }
}


Die zweite Methode informiert das Plugin über ein neu hinzugefügtes Tagesprogramm eines Senders:

public void handleTvDataAdded(ChannelDayProgram newProg) {

Ein Plugin was diese Methode überschreibt, hat die Möglichkeit Änderungen am Tagesprogramm vorzunehmen. Dies macht zum Beispiel das ShowView-Plugin, was jeder Sendung, zu der eine ShowView-Nummer berechnet werden konnte, diese hinzufügt.


Die dritte Methode informiert das Plugin über die Entfernung eines Tagesprogramms eines Senders:

public void handleTvDataDeleted(ChannelDayProgram oldProg) {

Daten speichern und laden

Viele Plugins arbeiten mit Daten, die sie speichern wollen, damit sie beim nächsten Start wieder geladen werden können. Damit nicht jedes Plugin sich damit herumschlagen muss, wohin es seine Daten speichert, liefert die Plugin-Schnittstelle entsprechende Methoden, die vom TV-Browser zur gegebenen Zeit aufgerufen werden.

Die Daten können dabei auf zwei Arten gespeichert werden: Als Properties und als serialisierte Objekte.

Properties sind einfache Schlüssel-Wert-Paare von Strings (siehe java.util.Properties). Sie haben den Vorteil, dass sie sehr einfach zu benutzen sind. Ein Nachteil ist, dass man damit nur Strings abspeichern kann. Simple Datentypen, wie z.B. Integers, können zwar in String umgewandelt werden, aber komplexe Datenstrukturen lassen sich auf diese Weise nur mit großen Verrenkungen speichern.

Serialisierte Objekte haben den Vorteil, dass man so beliebige Objekte speichern kann (sofern sie java.io.Serializable implementieren). Der Nachteil dabei ist, dass man die genaue Reihenfolge der Speicherung beim Laden wieder einhalten muss. Außerdem muss man darauf achten, dass man auch alte Dateien noch laden kann, selbst wenn sich etwas an diesem Format geändert hat (Wenn man z.B. etwas zusätzliches speichert oder wenn bei einem Objekt ein Feld hinzugekommen ist).

Diese beiden Techniken können auch parallel verwendet werden. Man kann also manche Werte als Properties speichern, andere als serialisierte Objekte.

Implementierung:

public Properties storeSettings()
public void loadSettings(Properties settings)

Speichert bzw. lädt Daten als Properties.

public void writeData(ObjectOutputStream out)
public void readData(ObjectInputStream in)

Speichert bzw. lädt Daten als serialisierte Objekte.

Die Methoden zum Laden der Daten, also loadSettings(Properties) und readData(ObjectInputStream), werden direkt nach der Erzeugung der Plugin-Instanz aufgerufen, also nach dem Konstruktor.

Beispiel (Properties):

private Properties mSettings;

public Properties storeSettings() {
  return mSettings;
}

public void loadSettings(Properties settings) {
  mSettings = settings;
}

private void someOtherMethod() {
  // Eine Property setzen
  String username = ...;
  mSettings.put("username", username);

  // Eine Property lesen
  String username = mSettings.get("username");
}

Beispiel (Serialisierte Objekte):

private String mUserName;

public void writeData(ObjectOutputStream out) {
  out.writeInt(1); // Dateiformat-Version
  out.writeObject(mUserName);
}

public void readData(ObjectInputStream in) {
  int version = in.readInt();
  if (version == 1) {
    mUserName = (String) in.readObject();
  }
}

Seit TV-Browser 2.5.3 können Plugins die Speicherung ihrer Daten auslösen, dazu muss folgende Methode der Superklasse Plugin aufgerufen werden:

protected final boolean saveMe()

Der PluginProxyManager wird dann die Methoden zur Speicherung der Daten aufrufen und true zurückgeben, wenn das Speichern erfolgreich war. Ein Plugin sollte dabei die Daten nur so oft speichern, wie dies erforderlich ist. Nach Möglichkeit sollte das Plugin auch nicht jedes Mal speichern, wenn eine Sendung aus einem Sendungsarray verarbeitet wird, sondern erst nach Verarbeitung des gesamten Arrays. Wird TV-Browser beendet, werden die Methoden zur Datenspeicherung in jedem Plugin automatisch aufgerufen.

Sollte TV-Browser "abgeschossen" werden, gehen nicht gespeicherte Daten verloren.

Sendungen empfangen

Ein Plugin kann Sendungen von anderen Plugins empfangen. Um anzuzeigen, dass es Sendungen empfangen kann muss folgende Methode überschrieben werden:

public boolean canReceiveProgramsWithTarget() {
  return true;
}

Standardmäßig gibt hier jedes Plugin false zurück um anzuzeigen, dass es keine Sendungen empfangen kann.


Um nun Sendungen zu empfangen muss das Plugin die folgende Methode überschreiben:

public void receivePrograms(Program[] programArr, ProgramReceiveTarget target)

Die Variable programArr enthält die Sendungen, die von anderen Plugins gesendet wurden. Diese können nun vom Plugin verarbeitet werden, zum Beispiel indem sie markiert werden:

public void receivePrograms(Program[] programArr, ProgramReceiveTarget target) {
  if(programArr != null) {
    for(Program p : programArr) {
      p.mark(this);
    }
  }
}

Über die Variable target kann das Plugin, das die Sendungen an dieses Plugin sendet eine bestimmte Verarbeitung festlegen. Die möglichen Verarbeitungen legt man fest, indem man im Plugin die folgende Methode überschreibt:

public ProgramReceiveTarget[] getProgramReceiveTargets()

Diese Verarbeitungsmöglichkeiten, kann der Benutzer dann in der Auswahlmaske für das Senden der Sendungen in einem anderen Plugin auswählen. Dieses Plugin sollte dann auf die vorgesehene Weise mit den Sendungen umgehen, die es mit einer Verarbeitungsmöglichkeit empfängt.

Beispiel:

public boolean canReceiveProgramsWithTarget() {
  return true;
}

public ProgramReceiveTarget[] getProgramReceiveTargets() {
  return new ProgramReceiveTarget[] {new ProgramReceiveTarget(this,"Markieren",this.getId()+"mark"),
                      new ProgramReceiveTarget(this,"Ent-Markieren",this.getId()+"unmark")};
}

public void receivePrograms(Program[] programArr, ProgramReceiveTarget target) {
  if(programArr != null && target != null) {
    for(Program p : programArr) {
      if(target.isReceiveTargetWithIdOfProgramReceiveIf(this, this.getId()+"mark")) { 
        p.mark(this);
      }
      else if(target.isReceiveTargetWithIdOfProgramReceiveIf(this, this.getId()+"unmark")) {
        p.unmark(this);
      }
    }
  }
}

Sendungen in der Baumansicht anzeigen

Plugins können Sendungen in der Baumansicht von TV-Browser anzeigen. Dies kann nützlich sein um einen guten Überblick über die Sendungen des Plugins zur Verfügung zu stellen. Das Plugin muss dazu folgende Methode überschreiben:

public boolean canUseProgramTree() {

Wenn hier true zurückgegeben wird, wird ein Zweig für das Plugin in der Baumansicht eingeblendet.

Das Plugin kann dann den eigenen Wurzelknoten aus der Superklasse Plugin benutzen oder einen eigenen Wurzelknoten erstellen.

Beispiel:

public boolean canUseProgramTree() {
  return true;
}

...

public void createMyTree() {
  // Hier holen wir uns den Wurzelknoten aus der Superklasse
  PluginTreeNode rootNode = getRootNode();
  
  // Dann entfernen wir vorsorglich sämtliche Aktionen die auf diesem Knoten registriert sind
  rootNode.removeAllActions();
  // Dann werden auch noch die bisherigen Kinder entfernt
  rootNode.removeAllChildren();

  // Jetzt fügen wir dem Wurzelknoten alle Sendungen hinzu, die das Plugin markiert hat
  for(Program program : mMyMarkedProgramsList) {
    PluginTeeNode programNode = rootNode.addProgram(program);
    /* Mit dem zurückgegebenen Knoten könnte man jetzt noch eine ganze Menge anstellen,
     * zum Beispiel neue Aktionen hinzufügen, die Art der Darstellung ändern oder ein Symbol
     * festlegen. Da das aber den Rahmen hier sprengen würde ist das Lesen der JavaDoc oder
     * das Spicken bei anderen Plugins angeraten.
     */
  }

  // Zum Schluss muss noch ein (graphisches) Update des Baums unterhalb des Wurzelknotens gemacht werden:
  rootNode.update();
}

Eigene Filter/Filterkomponenten zur Verfügung stellen

Plugins können sowohl fertige Filter, als auch eigene Filterkomponenten für TV-Browser zur Verfügung stellen. Ein Filter ist dabei eine abgeschlossene Funktionalität, die nur selbst vom Benutzer im Filtermenü als zu benutzender Filter ausgewählt werden kann. Eine Filterkomponente stellt eine Funktionalität zur Verfügung, die vom Benutzer in seine selbst erstellten Filter eingebunden werden kann.

Filter werden durch Überschreiben der folgenden Methode zur Verfügung gestellt:

public PluginsProgramFilter[] getAvailableFilter() {

Damit die Filter auch wieder entfernt werden können muss das Plugin außerdem die folgende Methode überschreiben:

public boolean isAllowedToDeleteProgramFilter(PluginsProgramFilter programFilter) {

Dies soll sicher stellen, dass kein Plugin, außer dem, das die Filter zur Verfügung stellt, die Filter entfernen kann. Die Methode muss also true zurückgeben, wenn es einen Filter entfernen möchte. Sollen die Filter nicht entfernt werden können, muss diese Methode nicht überschrieben werden, da sie in der Superklasse immer false zurückgibt. (Das Entfernen eines Filters wird über den FilterManager gemacht. Siehe dazu das JavaDoc.)

Filterkomponenten stellt das Plugin zur Verfügung indem es folgende Methode überschreibt:

public Class<? extends PluginsFilterComponent>[] getAvailableFilterComponentClasses()

Beispiel für das Anbieten eines Filters:

public PluginsProgramFilter[] getAvailableFilter() {
  return new PluginsProgramFilter[] {new MyWordFilter(this)};
}

private class MyWordFilter extends PluginsProgramFilter {
  public String getSubName() {
    return "Sendungen mit 'rot' im Titel";
  }
  
  public boolean accept(Program program) {
    if(program != null) {
      return program.getTitle.contains("rot");
    }
    
    return false;
  }
}

Die Filterfunktionen sind sehr umfangreich, so dass man dies erst benutzen sollte, wenn man die grundlegende Programmierung eines TV-Browser-Plugins beherrscht.

Implementierungsdetails

PluginManager

Der PluginManager stellt den Plugins eine Reihe nützlicher Funktionen zur Verfügung. Dabei hervorzuheben ist besonders der FilterManager, der es ermöglicht die Filter in TV-Browser anzusprechen. Wann immer ein Plugin mit dem Hauptprogramm interagieren will, muss es die Methoden des PluginManagers ansprechen, statt direkt auf Dialoge, Filter, Programme oder andere Dinge zuzugreifen.

PluginManager getPluginManager()

Wie lade ich eine Sendung

Sendungen lassen sich laden, indem man im Plugin den PluginManager und darin die Methode getProgram aufruft

Program program = getPluginManager().getProgram(Date date, String programId);

Die Variable date ist ein TV-Browser-Datum, die Variable programId ist eine eindeutige Zuordnung einer Sendung. Möchte man zum Beispiel die heutige Sendung, die beim ZDF um 19:00 Uhr läuft laden, müsste der Aufruf folgendermaßen aussehen:

Program program = getPluginManager().getProgram(Date.getCurrentDate(), "tvbrowserdataservice.TvBrowserDataService_main_de_zdf_19:0");

Die Sendungs-ID setzt sich also aus DATENSERVICE-ID_SENDERGRUPPEN-ID_LANDESKENNUNG_SENDERNAME_STUNDE:MINUTE zusammen.

Die Beispielsendung aus dem PluginManager

Die Beispielsendung erhält man, wenn man im Plugin den PluginManager aufruft, also folgendermaßen:

Program example = getPluginManager().getExampleProgram();

Wie schon im Abschnitt zum Kontextmenü erwähnt, kommt der Beispielsendung aus dem PluginManager eine besondere Bedeutung zu. Überall wo eine Sendung in einer Methode aus der Plugin-Klasse übergeben wird, kann es vorkommen, dass TV-Browser diese Methode mit der Beispielsendung aufruft. Ein Plugin muss dies also berücksichten.

Die Beispielsendung hat zum Beispiel immer die Uhrzeit 14:45 Uhr. Wenn jetzt das Plugin im Kontextmenü von TV-Browser eine Funktion anzeigen soll, die nur für noch nicht abgelaufene Sendungen sichbar ist, könnte es bei Nichtberücksichtigung der Beispielsendung dazu kommen, dass in der Kontextmenüeinstellung von TV-Browser das Kontextmenü eben dieses Plugins nicht mehr zu finden ist. Nämlich dann, wenn die Beispielsendung zum Zeitpunkt des Aufrufens der Einstellungen bereits abgelaufen ist.

Weitere Infos zum PluginManager sind in der JavaDoc-Dokumentation zu TV-Browser zu finden.