Link Inhalt
KIO4.com Die Installation der Werkzeuge und die Entwicklung einer Beispiel-Extension ist hier sehr gut beschrieben. Leider ein Gemisch aus Englisch und Spanisch, aber trotzdem gut verständlich und vor allen Dingen Schritt für Schritt erklärt. Für den Fall, dass die Seite von Juan Antonio Villalpando (KIO4.com) nicht mehr erreichbar ist, habe ich seine Anleitung PDF ausgedruckt.
Oracle Java Documentation Oracle Java Dokumentation.
AI2 Extension Reference Referenz für die Erstellung von eigenen App Inventor Erweiterungen
AI2 Annotation Source Die Annotation für die Spezifizierung der Extension-Elemente sind nirgendwo gut erklärt. Im Source-Code findet man einiges.
Pura Vida Apps Snippets, Tutorials, Extensions und viel mehr. Umfangreicher Content!
AppyBuilder ... ist eine von mehreren alternativen Entwicklungsplattformen. Es gibt viele Hintergrundinformation, u.a. eine Dokumentation der AI2-Klassen: 3nportal.com/AIBridge/API/
Klassenreferenz Mit Doxigen erstellte Dokumentation mit Stand 23.1.2020.

Screen von einem AI2-Projekt in ein anderes kopieren| Screen von einem AI2-Projekt in ein anderes kopieren

Zunächst beide Projekte auf den eigenen Computer exportieren (.aia-Dateien). Die Dateien sin im ZIP-Format.

Beide Dateien öffnen und den Ordner "src" durchsuchen, bis man in einem Unterordner die Screen-Definitionen findet. Z.B. so:

Liste der Screen

Im Projektmappenexplorer rechte Maustaste auf das Projekt. Im Menüpunkt "Ansicht" gibt es den Unterpunkt "Klassendiagramm anzeigen".


Extensions entwickeln| Extensions entwickeln

Um eigene Erweiterungen entwickeln zu können, muss man einige Werkzeuge installieren. Die Installation der Werkzeuge und die Entwicklung einer Beispiel-Extension ist sehr gut bei KIO4.com beschrieben. Leider ein Gemisch aus Englisch und Spanisch, aber trotzdem gut verständlich und vor allen Dingen Schritt für Schritt erklärt.

Nur bei der Position der Quelldateien ist diese Anleitung etwas veraltet. Das dort vorgeschlagene Verzeichnis

     "~\appinventor-sources\appinventor\components\src\com\google\appinventor\components\runtime"


sollte NICHT benutzt werden! Man richtet statt dessen besser eine eigene Sourcen-Struktur ein, die der Java-Package-Nomenklatur entspricht. Diese Struktur startet, wie die Google-Sourcen, im Ordner

     "~\appinventor-sources\appinventor\components\src\".


Bei mir sieht das dann so aus:

Verzeichnis-Struktur

Die Package-Bezeichnung in den Quellen ist dann z.B. "package de.UllisRoboterSeite.UrsAI2UDPv3;" (Bezeichnung des Projekts ändern).

Auch der App Inventor hat eine Tutorial-Seite. Mehr Details zu Komponenten findet man unter dem Link How to Add a Component.

Für den Fall, dass die Seite von Juan Antonio Villalpando (KIO4.com) nicht mehr erreichbar ist, habe ich seine Anleitung PDF ausgedruckt.

 

Wenn man alles installiert hat, ist es mühselig immer wieder nachzuschauen, wo sich die einzelnen Komponenten befinden. Das folgende kleine Batch-Programm (AI2Ex.cmd) öffnet die entsprechenden Fenster (Pfad zu den Quellen ersetzen!):

cd %userprofile%\appinventor-sources\appinventor
start "" "C:\Program Files\Git\git-bash.exe"
start %userprofile%\appinventor-sources\appinventor\components\build\extensions
start %userprofile%\appinventor-sources\appinventor\components\src\de\UllisRoboterSeite

Zum Erstellen der Extension muss in das Git-Bash-Fenster der Befehl ant extensions eingegeben werden.


Develop Extensions| Develop Extensions

To develop your own extensions, you have to install some tools. The installation of the tools and the development of an example extension is very well described at KIO4.com. Unfortunately, a mixture of English and Spanish, but easy to understand and explained step by step.

With the position of the source files, this manual is outdated. The directory proposed there

     "~\appinventor-sources\appinventor\components\src\com\google\appinventor\components\runtime"

should NOT be used! Instead, it is better to set up your own source structure, which corresponds to the Java Package nomenclature. This structure, like the Google sources, starts in the folder

     "~\appinventor-sources\appinventor\components\src\"
.

 For me it looks like this:

 

Verzeichnis-Struktur

The package name in the sources is then e.g. "package de.UllisRoboterSeite.UrsAI2UDPv3;" (change name of project).

The App Inventor also has a tutorial page too. More details on components can be found under the link How to Add a Component.

In the event that the page of Juan Antonio Villalpando (KIO4.com) is no longer available, I have printed his instructions to PDF.

 

When everything is installed, it is tedious to keep checking where the individual components are. The following small batch program (AI2Ex.cmd) opens the needed windows (replace the path to the sources!):

cd %userprofile%\appinventor-sources\appinventor
start "" "C:\Program Files\Git\git-bash.exe"
start %userprofile%\appinventor-sources\appinventor\components\build\extensions
start %userprofile%\appinventor-sources\appinventor\components\src\de\UllisRoboterSeite

To build the extension, you have to enter the command ant extensions in the git-bash window.


Android Log schreiben / anzeigen / löschen| Android Log schreiben / anzeigen / löschen

Log schreiben

Das Schreiben von Log-Einträgen zu Debug-Zwecken ist recht einfach. Zunächst muss das entsprechende Package eingebunden werden:

import android.util.Log;

Das Schreiben in das Log erfolgt über die statischen Methoden v(), d(), i(), w() und e() der Klasse Log. Die einbuchstabigen Methodennamen spiegeln gleichzeitig eine Priorität wieder und bedeuten:

  • v: verbose (ausführlich)
  • d: debug (Test)
  • i:  information
  • w: warning
  • e: error (Fehler)

Alle diese Methoden gibt es in drei Parameter-Konfigurationen:

static int X(String tag, String msg)
static int X(String tag, Throwable tr)
static int X(String tag, String msg, Throwable tr)

Der Parameter tag ist eine beliebige Zeichenfolge, die ein späteres Filtern erlaubt. Am bestem definiert man hierfür eine String-Konstante, damit man garantiert immer den gleichen Wert übergibt:

private static final String LOG_TAG = "MyLogTag";

Ein typischer Aufruf sähe dann etwa so aus:

Log.i(LOG_TAG, "Eintritt in Methode mmm. Parameter aaa: " + aaa);

Die Klasse Log enthält weitere nützliche Methoden. Z.B. lässt sich ein aktueller Stack-Trace als String abrufen. Eine ausführliche Dokumentation gibt es in der Android Referenz Log.

Log anzeigen

Das Anzeigen des Logs geschieht auf dem PC über das Programm adb. adb gehört zu den Android SDK Platform Tools. Die Platform Tools wiederum sind Teil der Command line tools. Hier sind eine Reihe weiterer nützlicher Tools für Entwickler enthalten.

Wenn man das Android Studio installiert werden die Tools und die notwendigen Treiber mit installiert (ggf. als Option). Zu finden sind sie (Version 3.6) im Verzeichnis "C:\Users\<username>\AppData\Local\Android\Sdk\platform-tools". Man kann die Tools aber auch unabhängig vom Android Studio herunter laden und benutzen (s. Links). Es gibt Windows- und Linux-Versionen. Ggf. sind noch Treiber zu installieren. Wegen der unterschiedlichen Android Version sollte man hier nach aktuellen Information per Suchmaschine suchen.

Auf dem Android-Gerät muss das USB-Debugging aktiviert sein und das Gerät muss per USB mit dem PC verbunden sein.

Zur Anzeige des Logs ruft man adb logcat [weitere Optionen] in einer DOS-Box (PowerShell-Box) auf. Genaue Spezifikationen findet man in der Android Referenz zu logcat. Ein typischer Aufruf ist

adb logcat "MyLogTag":I *:S

Alle Log-Einträge mit dem Tag MyLogTag der Stufe I und höher (also I, W und E, jedoch nicht V und D) werden angezeigt. Alle anderen Tags werden nicht angezeigt ("*:S", S = Silent). Verschiedene Tags lassen sich mit verschiedenen Stufen kombinieren. Die Ausgabe sieht dann etwa wie folgt aus:

04-17 18:07:01.880 27088 27088 W MyLogTag: text.length() == 0
04-17 18:07:13.790 27088 27088 I MyLogTag: Eintritt in Methode mmm. Parameter aaa: 47
04-17 18:09:42.934 27450 27450 I MyLogTag: Eintritt in Methode mmm. Parameter aaa: 723

Löschen

Das Löschen aller Log-Einträge erfolgt über die Anweisung

adb logcat -b all -c

Auch hier kann man feiner spezifizieren (s. Dokumentation).


Standard-Error-Handling in Extensions| Standard-Error-Handling in Extensions

Anstatt eigene Error-Events anzubieten, kann man das Standard-Fehler-Ereignis ErrorOccurred der Screen-Komponente auslösen.

error-occurred

Der Vorteil ist, dass, wenn in der App kein Ereignis-Handler für dieses Ereignis implementiert ist, ein Hinweisfenster (Notifier) aufgeblendet wird. Die Dokumentation hierzu, leider nur als Kommentar im Code zu finden, sagt:

If dispatchEvent returned false, then no user-supplied error handler was run. If in addition, the screen initializer was run, then we assume that the user did not provide an error handler. In this case, we run a default error handler, namely, showing a notification / message dialog to the end user of the app. The app writer can override this by providing an error handler.

Das Ereignis kann folgendermaßen ausgelöst werden:

  1. Die Basisklasse für Komponenten (AndroidNonvisibleComponent) stellt über das geschützte Feld form eine Referenz auf das Screen-Objekt zur Verfügung, in das die Extension eingebunden ist.
  2. Das Auslösen des Ereignisses geschieht dann über die Methoden
    1. form.ErrorOccurred(Component component, String functionName, int errorNumber, String message)

      löst

        new Notifier(this).ShowAlert("Error " + errorNumber + ": " + message);

      aus, wenn kein Event-Handler für das Ereignis implementiert ist.

    2. form.ErrorOccurredDialog(Component component, String functionName, int errorNumber, String message, String title, String buttonText)

      löst

         new Notifier(this).ShowMessageDialog("Error " + errorNumber + ": " + message, title, buttonText);

      aus, wenn kein Event-Handler für das Ereignis implementiert ist.

Beispiele:

Aufruf von ErrorOccurred ohne implementierten Event-Handler:
form.ErrorOccurred(this, "TestErrorOccurred", 777, "Testing ErrorOccurred");

show-error-message

Aufruf von ErrorOccurredDialog ohne implementierten Event-Handler:
form.ErrorOccurredDialog(this, "TestErrorOccurred", 777, "Testing ErrorOccurred", "Title of Dialog", "Button Text");

show-error-dialog

Aufruf von ErrorOccurred bzw. ErrorOccurredDialog mit implementierten Event-Handler:

Ergibt folgende Ausgabe:

Error:
de.ullisroboterseite.mycomponent.MyComponent@5e1d2eb
TestErrorOccurred
777
Testing ErrorOccurred

"5e1d2eb" ist die ID der spezifischen Komponente. Unterschiedliche Komponenten haben unterschiedliche IDs.


Google App Engine starten| Google App Engine starten

Die Google App Engine kann auch per Kommando-Zeile gestartet werden. Dort stehen eine Reihe von Optionen zur Verfügung, die der Launcher nicht bietet, z.B. die Spezifizierung der Host-Adresse und der Port über den die Anwendung kommunizieren soll. Wenn Google App Engine korrekt installiert wurde, befindet sich dev_appserver.py im Order C:\Program Files (x86)\Google\google_appengine\ und die Umgebungsvariable PATH enthält einen Eintrag zu dem Ordner. Wenn nicht, neu installieren, Pfad selbst eintragen oder bei den Kommandos explizit angeben.

 Nun wechselt man in das Verzeichnis, in dem sich die zur Applikation gehörende Datei app.yaml befindet. Dann z.B.

dev_appserver.py app.yaml --host 192.168.178.99 --port 8080

Nun kommuniziert die Applikation nicht mehr über localhost sondern über 192.168.178.99.

Alle möglichen Einstellungen bekommt man per

dev_appserver.py -h

Tiefergehende Informationen| Tiefergehende Informationen

Die Dokumentation für die Entwicklung von eigenen Extensions ist recht dürftig. Mit solchen Informationen kommt man dann deutlich weiter. Z.B.:

Der Konstruktor einer Extension-Klasse erhält eine Referenz container auf die Instanz einer ComponentContainer-Klasse. Diese bietet u.a. die beiden Methoden container.$context() und container.$form(). Dies sind Referenzen auf die zu Grunde liegende Activity bzw. Form-Instanz. Die Form-Klasse wiederum bietet viele zusätzliche Methoden, z.B. registerForOnPause(OnPauseListener component) mit der man sich informieren lassen kann, wenn die App in den Zustand Paused wechselt. Leider muss man in den Quellcode schauen, um zu verstehen, was genau passiert.

Ich habe mit Doxigen eine Klassenreferenz erstellt zum Stand, der am 23.1.2020 gültig war. Es lohnt sich, hier ein wenig zu stöbern.

Nicht jede Methode ist sauber dokumentiert. Weitere Informationen erhält man, wenn man von Verzeichnis "C:\Users\<meinUser>\appinventor-sources\appinventor\components\src\com" mit findstr nach Stellen sucht, wo die gesuchte Klasse oder Methode benutzt wird, und dann in der entsprechenden Quelle nachschaut:

findstr /S /C:"funktionxyz" *.java

Dit und Dat| Dit und Dat

Bei der Annotation @DesignerProperty kann man einen defaultValue angeben. Dieser Wert ist dann die Voreinstellung der Eigenschaft im Designer-Fenster des App Inventor. Für Eigenschaften vom Typ boolean (editorType = PropertyTypeConstants.PROPERTY_TYPE_BOOLEAN) sind der Wert für die Voreinstellung true der Text "True" (mit großem "T"!). Alle anderen Angaben führen zu Voreinstellung false.

Voreinstellung