Let Font Size change in Smartphone Apps

I am experiencing this for Android today but I wonder if does not apply to iPhone or web apps as well. For Android it is a big headache to give users a chance to change font sizes app wide. There are not many google results concerning this topic. How are developers trying to achieve this? The first way is to manipulate or overwrite each and every TextView of an app and set the font size according to user preferences. This assumes that you have all TextViews to manipulate. But in many cases this class is hidden at first glance. You have to find out how to write a custom layout for preferences or dialogs to name a few. Are you sure that you got each text in your application as a TextView? No hidden system font in a hidden TextView?
You can also create your own themes with its special font sizes. But font size is not the only preference witch determines a theme. Imagine you have a dark theme and a light theme. Imagine you have four different font sizes for each theme. You end up with eight themes for only two preferences. Add the next preference and you have much work to do. Since themes are static and can’t be changed at runtime it kills efficiency.
So why not set the font size at OS level? If I were disabled or could not read small fonts, I would try to solve this problem systematically. I still do not know iOS very good. Android has a preference at OS level to set the font size system wide for all apps. You don’t have to set font sizes for each app individually. Accordingly Chrome has a setting to increase the font size for all pages systematically. I am sure iOS will allow the same. Seems to be the best option for each party, users and developers!

NSA im eigenen Auftrag

Ich habe eine neue Möglichkeit entdeckt, Notizen zu machen, nämlich indirekt durch jegliche Eingaben ins Handy. Ich ergoogle ja mein ganzes Leben. Deshalb will ich meine Eingaben protokollieren und zu meinem Pi senden. Sozusagen NSA in eigener Regie. Am Ende des Jahres eine Statistik ansehen können, was ich das Jahr lang im Herzen gehabt habe. Und mit solchen ganz nebenbei erstellten Notizen kann ich ja etwas anfangen, z.B. automatisiert Nachrichten oder Filme suchen. Unter Android kann man die Software-Tastatur selbst programmieren. Ich lade mir den Quellcode von Gingerbread-Keyboard herunter. Nachdem ich den nativen Teil mit dem Android NDK kompiliert habe, kann ich das Projekt mit Eclipse auf meinem Handy installieren und den Quellcode ändern. Ich finde verschiedene Methoden, um den Text zu unterschiedlichen Zeitpunkten aus dem Ziel-TextView zu lesen. Alle Methoden muten frickelig an. Es ist also eine interessante, aber weniger übliche Anwendung eines InputMethodService. Wenn sich das Tastaturfenster nach der Eingabe wieder schließt, wird der eingegebene Text verschlüsselt an meinen Raspberry Pi gesendet und in die Datenbank eingetragen. Ein Problem liegt darin, dass nicht alle Eingaben im Ziel-TextView landen. Bei Autocomplete-TextViews wird ein vervollständigtes Wort direkt ausgewählt statt erst im Zielfenster ankommen zu müssen. Allerdings kann man gerade in meinem wichtigsten Eingabefenster, der Adresseingabe des Chrome-Browsers den autovervollständigten Text zuerst ins Zielfenster schubsen um dann darauf aufbauend weitere Vervollständigungen der Suchphrase zu erhalten. Mit etwas Disziplin schneidet man dann doch den gesamten eingegebenen Text mit.

SSLSocket unter Android

Nach einigen Änderungen kann ich jetzt auch von Android eine Verbindung zu meinem SSLServerSocket aufbauen. Aus meiner gestern generierten Schlüsselaufbewahrungsdatei keystore.jks, welche den privaten und den öffentlichen Schlüssel beherbergt, exportiere ich ein Zertifikat cert.pem

keytool -export -keystore keystore.jks -file cert.pem

Da Android nur mit BouncyCastle-formatierten Keystores etwas anfangen kann, erzeuge ich nun einen solchen, indem ich das eben erzeugte Zertifikat importiere:

wget http://bouncycastle.org/download/bcprov-jdk16-146.jar
keytool -importcert -file cert.pem -keystore android.store -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath bcprov-jdk16-146.jar -storetype BKS

Eine beispielhafte Android-Activity sieht nun so aus:

package de.codeschneider.sslmessageclient;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

import android.os.Bundle;
import android.os.StrictMode;
import android.app.Activity;
import android.content.res.Resources.NotFoundException;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
      //passphrase for keystore
        char[] keystorePass="pwd".toCharArray();

        TrustManagerFactory trustManagerFactory = null;
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();

        StrictMode.setThreadPolicy(policy); 
        
        try {
            KeyStore keyStore=KeyStore.getInstance("BKS");
			keyStore.load(getApplicationContext().getResources().openRawResource(R.raw.android),keystorePass);
	        //create a factory
	        trustManagerFactory=TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
	        trustManagerFactory.init(keyStore);
	        SSLContext sslContext=SSLContext.getInstance("TLS");

	        //init context
	        sslContext.init(
	            null,
	            trustManagerFactory.getTrustManagers(), 
	            new SecureRandom()
	        );

	        
	        //create the socket
	        Socket socket=sslContext.getSocketFactory().createSocket("www.meine.schoene.inetadresse.eu",1111);
	        socket.setKeepAlive(true);        

            InputStream inputstream = getApplicationContext().getResources().openRawResource(R.raw.echoclient);
            InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
            BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

            OutputStream outputstream = socket.getOutputStream();
            OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream);
            BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter);

            String string = null;
            while ((string = bufferedreader.readLine()) != null) {
                bufferedwriter.write(string + '\n');
                bufferedwriter.flush();	
            }
	        
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CertificateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        //get context
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    
}

Die Activity nimmt den Keystore aus der Resourcen und übermittelt eine Datei über die hergestellte sichere Verbindung. Man verzeihe mir die Policy, alles zu erlauben. Normalerweise darf man unter Android keine Netzwerkverbindungen im Main-Thread aufbauen, sondern muss dieses nebenläufig tun. Ich wollte aber einfach nur sehen, ob meine Verschlüsselung funktioniert. Hurrah!

Kanonen auf Spatzen: JEE-Notizblock auf dem Raspberry Pi

Weihnachtszeit – Bastelzeit. Für mich lag ein Raspberry Pi unter dem Weihnachtsbaum, und zwar der mit 512 MB Arbeitsspeicher. Lange habe ich davon geträumt, einen eigenen Server im 24/7-Betrieb laufen zu lassen. Es ist wie mit den Smartphones. Irgendwann kam der Augenblick, wo diese Dinger tatsächlich handliche Hochleistungsmaschinen waren und die Internetverbindungen erschwinglich. Der Raspberry kostet mit Gehäuse – den Luxus gönne ich mir -, Netzteil, SD-Card und Netzwerkkabel zum Anschluss an den Router ca. 60 Euro und bei 3,5 Watt Stromverbrauch weniger als 8 Euro pro Jahr an Strom. Er bietet eine große Spielwiese. Da ich ihn kopflos (headless), d.h. remote über SSH, betreibe, kann ich endlich mein 1000-Seiten-Totschläger-Linux-Buch gebrauchen und werde fit im Kommando-Schreiben. Zuerst wollte ich mir eigentlich einen ShevaPlug kaufen. Aber ich habe keinen einzigen Bericht von Benutzern gelesen, denen es nicht durchgeschmort ist, sowohl die alten als auch die neuen Modelle. Der Raspberry ist aber so schwach auf der Brust, dass er kühl bleibt. Das ist mir immer lieber als Hochleistung, aber durchbrennen.

Es gibt genug gleich lautende Anleigung, um JBoss auf dem Raspberry Pi zu installieren. Daher kann ich darauf verzichten. Von mir wie immer nur einen lockeren Overview. Zuerst lade ich mir die eingebettete Version von Java herunter auf den Pi. Das ist eine extra für Arm-Prozessoren angepasste Version. Java ist ja wirklich flott geworden. Es gibt ja auch eine extra virtuelle Maschine für Android. Mein Raspberry-Java hat aber den kompletten Umfang. Dann lade ich mir den aktuellen JBoss herunter und lasse ihn mit dem vorbereiteten Java laufen. So ganz ohne Anwendungen fährt er sehr schnell hoch und der Speicherverbrauch liegt bei schlappen 10%, also ca. 50 MB. Das zum Thema Schwergewicht. Da hätte ich Schlimmeres erwartet. Auch wenn ich noch keine Anwendungen installiert habe, bringt der JBoss eine Management-Anwendung mit, die mit dem gleichen Speicherverbrauch flüssig läuft.

Als nächstes installiere ich den MySQL-Server und richte ihn so ein, dass ich von einem anderen Rechner aus mit MySQL-Workbench komfortabel darauf zugreifen kann. Jetzt schreibe ich mit dieser remote-Verbindung meine Entities mit JPA und lasse meine Datenbankstruktur automatisch erzeugen. Das Deployen ist interessant. Bisher habe ich das immer mit Eclipse-Magic gemacht. Jetzt verpacke ich mein .war-Archiv und entpacke es auf dem Server und erzeuge eine daneben eine .dodeploy-Datei und schon wird die Anwendung installiert. Der Speicherbrauch erhöht sich ein bisschen. Nun startet der JBoss wesentlich langsamer. Die JPA-Performance überzeugt mich leider nicht direkt. Eine einfache Abfrage einer einzigen Tabelle dauert sichtlich Zeit, so dass die Mini-Anwendung oberflächentechnisch verlangsamt wird.

Ich weiß jetzt übrigens auch, wie ich den Webserver meines Pi auch über das Internet zugänglich mache. Den Großteil des Problems löse ich mit meinem Router, der Fritzbox. Hier gebe ich zuerst meinem Raspberry Pi eine feste IP-Adresse, damit er nicht per DHCP bei jedem Reboot eine neue Adresse zugewiesen bekommt. Nun kann ich ein Portforwarding vornehmen. Ich weise die Fritzbox an, eine HTTP-Anfrage an Port 80 aus dem Internet an Port 8080 meines Pi weiter zu leiten. Nun brauche ich noch einen sprechenden Namen für mein Internetangebot und einen Dienst, der die ständig wechselnde IP-Adresse meines Routers mit diesem Namen verknüpft. Dafür gibt es Dienste wie DynDNS, welcher allerdings seit Neustem nicht mehr kostenfrei ist. Im Steuermenü meiner Fritzbox kann ich einige dieser Dienste auswählen. Ich wähle selfhost. Nun sagt die Fritzbox bei jedem IP-Wechsel selfhost Bescheid, so dass ich mit einem gleichbleibenden Namen auf meinen Webserver zugreifen kann. Will man das selfhost im Namen vermeiden, muss man etwas bezahlen und bekommt einen ganz normalen Namen, ohne Werbung. So ganz umsonst ist dieser Service also auch nicht.

Ich plane, den Pi als einen allezeit verfügbaren Notizblock zu benutzen, um meine Ideen zu pflegen und auswerten zu können. Der Webserver und die Datenbankverbindung laufen. Jetzt muss ich noch einen Webservice oder Ähnliches programmieren. Dazu brauche ich noch eine Android-Oberfläche, die mittels gesicherter Verbindung die wichtigen Notizen weiterleitet. Und das so schnell wie möglich. Ich will schließlich nicht meinen ganzen Urlaub am Rechner verbringen. Wenn man im Urlaub programmiert, kann man eben auch noch Schnelligkeit lernen.

Android Studienbibel

Gestern hat mich eine Spam-Email dazu ermutigt, nun auch einmal den Erfolg meiner Android Studienbibel zu erwähnen. Wie der Spamroboter so schön bemerkt, ist meine Bibel-App auf Platz 323 der kostenlosen Top-Apps in der Kategorie Bücher und Nachschlagewerke. Sie wurde 3.442 mal heruntergeladen und ist auf 2.197 Handies aktiv in Gebrauch. Immerhin! Die Kurve der aktiven Installationen auf Geräten ist über die Zeit deutlich steiler geworden. Es hat sich gelohnt, SQLite nativ in C++ anzusprechen, denn 50% der Benutzer sind immer noch bei Android <= 4.0. Die Bewertung der App liegt bei 4.54. Ich habe also doch einige Nutzer glücklich gemacht und die App deckt eine kleine aber echte Nische ab. Als Abstürze habe ich keinen einzigen zu verzeichnen, seitdem ich die Möglichkeit zu geringen Speichers beim Entpacken der Datenbank abgefangen habe. Und das, obgleich meine App über einen nativen, in C++ programmierten, Anteil verfügt und sich durch diese erhöhte Komplexität Fehler einschleichen können.

PowerVR Insider SDK

Ich habe noch eine bessere Möglichkeit gefunden, platformunabhängig Text darzustellen. Als erstes stieß ich durch Zufall auf heise.de auf NVIDIA Nsight Tegra, ein Plugin für Visual Studio, welches die native Entwicklung inclusive Debugging für Android auf komfortable Weise erlauben soll. Mir war das komplizierte Gefummel zuwieder, mit Eclipse Android NDK-Anwendungen zu debuggen. Leider funktioniert das Ganze bei mir nicht, aus der Traum. Das Installationsprogramm vermisste eine Benutzergruppe, die auf amerikanischen Windows-Systemen per default vorhanden ist. Die netten Entwickler haben also deutsche Benutzer vergessen. Nachdem ich diese Tatsache in einem Visual Studio-Protokoll herausgefunden und die Benutzergruppe meinem Rechner hinzugefügt hatte, wollte Visual Studio nicht starten. Das Plugin hatte etwas vermurkst. Die Support-Abteilung von NVIDIA war auch ganz freundlich, aber ein Update habe ich bis heute noch nicht gesehen. Dann muss ich also doch unter Windows mit einem Opengl ES-Emulator entwickeln und nach Android portieren.

Kürzlich habe ich aber einmal mehr gesucht und das PowerVR Insider SDK gefunden. Für die Platformen Windows, Linux, Android und iOS und noch mehr entwickelte Imagination Technologies mit diesem SDK ein Framework, welches die platformspezifischen Details heraus abstrahiert und ein grundlegendes API zur platformunabhängigen Grafikprogrammierung unter Opengl ES bereitstellt . Die Beispiele funktionieren tadellos und alles lässt sich anstandslos kompilieren (in meinem Fall mit dem Visual Studio für Windows und dem aktuellsten NDK für Android). Um Text zu dimensionieren, kann man die API fragen, welche Breite und welche Höhe ein Text einer bestimmten Größe im Bildschirmkoordinatensystem hat. Das ist genau, was ich gesucht habe, zumal dieses Framework auch Opengl ES 2.0 unterstützt. Aber insgesamt erkenne ich nun, dass diese grundlegenden Funktionen doch noch ein bisschen wenig ist, so dass man sehr viel selbst programmieren muss. Ich müsste z.B. erst einmal einen Textviewer programmieren. An Interaktionen ist auch nicht gedacht, sondern nur die grafische Darstellung. Vielleicht doch ein Spiele-Framework …

Text mit dem Android NDK malen

Mein nächstes Vorhaben: Der Betweenness-Algorithmus soll mir für jedes Buch der Bibel die Zentralität der Nomen berechnen und dann will ich die Worte in diesem Bibeltext grafisch entsprechend der Zentralität hervorheben. Dies fällt in den Bereich Computergrafik. Da ich mich, wo möglich, aus der reinen Android-Welt befreien möchte und Computergrafik nun wirklich einen Sache ist, die gerne mit C++ angegangen wird, realisiere ich meine Idee mit dem Android NDK. Ich suche nach einem unmittelbar lauffähigen Beispiel. Nachdem ich die einfachsten Beispiele ausprobiert habe und keines davon läuft, frage ich mich, ob ich nicht eine Game-Engine verwenden möchte. Aber ich bin kein Freund von unnötiger Komplexität und möchte so schnell wie möglich zum Ziel kommen. Um ein bisschen Text ansprechend rendern zu können, brauche ich keine Game-Engine. Mit nur ein wenig Suchaufwand, findet sich eine Library, inklusive Beispielprojekt von Julien Rebetez. Man muss auch ein wenig Arbeit investieren, indem man eine freetype-Library hinzu linkt. Hier gibt es ebenfalls vom gleichen Autor auch eine fertige Bibliothek für Android. Man muss die Android.mk des Beispielprojekts ändern, dass sowohl die Bibliothek zum Darstellen von Text als auch die freetype-Bibliothek gefunden wird. Beide Bibliotheken müssen vorher nur noch mit ndk-build gebaut werden.

Android Nativ (C++)

Endlich habe ich einen Grund gefunden, nativ (in C++) unter Android zu programmieren. Eine Datenbankabfrage meiner Studienbibel dauert zu lange. Eine 800.000-zeilige Tabelle wird mit einer 400.000-zeiligen Tabelle per JOIN verknüpft und auf dieser Vereinigung findet eine Abfrage statt. Nicht dass diese Abfrage nicht in Java in akzeptabler Geschwindigkeit zu erledigen wäre. Unter Android 4.1 dauert dieses SELECT ca. 2 Sekunden. Aber Android 4.0 braucht 10 Sekunden. Hier wird eine andere SQLite-Implementierung verwendet, die dieses Statement nicht optimiert ausführt. In C++ programmiert, kann ich mir die SQLite-Implementierung aber selbst kompilieren und die Abfrage dauert, unabhängig von der Version des Android-Betriebssystems, wieder 2 Sekunden.

Eclipse und C++ – Nein danke

Bei der Gelegenheit versuche ich, den C++-Code mit dem Debugger untersuchen zu können. Die Tutorials sind nach mehreren Jahren der Existenz des Android NDK diesbezüglich immer noch rar gesät. Ich finde tatsächlich nur eine Anleitung. Mit Eclipse habe ich aber so meine Schwierigkeiten. Nachdem ich die STL-Portierung eingebunden habe, kann ich wegen hartnäckigen Semantikfehlern das native Projekt nicht mehr starten. Ich kann den Code kompilieren, aber Eclipse meint, es liege ein Fehler vor. Empfohlene Lösung: Ich schalte die Syntaxprüfung ab. Dann kann ich auch das Hauptprojekt wieder starten. Super: Eine IDE ohne Syntaxüberprüfung. Eclipse wird langsam nicht mehr wichtig. Die Prozedur, die Anwendung mit Haltepunkten ausführen zu können, ist recht kompliziert. Zuerst starte ich die Anwendung, bis die native Bibliothek geladen wird. Kurz danach halte ich wieder an. Danach starte ich den Debugger per ndk-gdb in der Console. Jetzt kann ich in Eclipse den nativen Debugger starten, der sich per TCP mit dem GDB verbindet. Das Springen von einem Haltepunkt zum nächsten ist sehr zäh und es dauert einige Sekunden pro Haltepunkt. Nachdem der Debugger plötzlich keine Symbole mehr finden kann, gebe ich genervt auf. Ich habe Urlaub! Ich werde die Entwicklung bezüglich Eclipse und natives Debugging unter Android bestimmt weiter verfolgen, aber viel scheint sich nicht zu tun. Schließlich ist das NDK immer noch nur als Notfalllösung geplant, wenn man bereits funktionierenden Code nach Android portieren will.

Visual Studio und C++

Eine bessere Stragie ist es, das Interface meiner Datanbankabfrage zu definieren und die Implementierung dann mit dem Visual Studio vorzunehmen. Ich erstelle eine Consolenanwendung und kann komfortabel entwickeln, ohne frickeln zu müssen. Wenn alles zufriedenstellend läuft, passe ich den Code mit minimalen Änderungen für den Compiler des Android-NDK an.

Eine Datenbankanwendung für Android

ASP.NET MVC – C++/CLI

Nachdem ich so eine tolle Möglichkeit gefunden habe, mit Boost in C++ so schön und schnell die Betweenness Centrality der Nomen eines Textes zu berechnen, könnte ich doch meine Studienbibel, die ich in ASP.NET MVC verfasst habe, mit dieser Möglichkeit erweitern. Bei meinem letzten Job hatte ich schon einmal die Verknüpfung von C# und C++ über C++/CLI gemeistert und ich will an diesen Erfolg anknüpfen. Auf meinem Rechner klappt das super. Ich erstelle eine gemischte managed/unmanaged DLL, welche über ein managed Interface für die C#-Assembly verfügt und darüber nativen Code ausführen kann. Aber als ich meine Anwendung auf den Server veröffentlichen will, wird beim Start der Ausführung aber eben jene gemischte DLL vermisst. Je mehr ich im Internet zu dem Problem recherchiere, desto mehr Lösungsmöglichkeiten eröffnen sich. Darunter finden sich doch recht viele Meinungen, wie höllisch kompliziert die Mischung C++ und c# mittels C++/CLI doch sei. Nachdem ich einen Sonntag geopfert habe und sich noch mehr Möglichkeiten und Unklarheiten auftun, halte ich inne. Das ist eben doch ein Nachteil an Microsoft. Wäre so ein Server frei, könnte ich ihn mir einfach herunterladen und in einer virtuellen Maschine laufen lassen und den ausgeführten Code auf dem Server per Debugger untersuchen. Die vielen einfachen Lösungsmöglichkeiten hatte ich natürlich ausprobiert, aber um hier weiter zu kommen, brauche ich mehr als spielerisches Interesse an technischen Herausforderungen. Ich bin eben doch kein Technokrat. Was will ich eigentlich? Muss ich unbedingt der Welt zeigen, dass ich ein C++/CLI-Spezialist sein könnte? Sinnlos! Ich will eine Studienbibel programmieren und cool wäre das doch eigentlich für Android! Dann wäre ich nicht vom Internet abhängig und könnte auch im Wald oder in sonstiger Natur die Bibel auf besondere und komfortable Weise lesen. Eben überall, wo ich gerade bin.

Android NDK

Ich probiere ein Hello-World-NDK-Beispiel unter Android aus. Um SQLite nativ zum Laufen zu bringen, muss ich die riesige SQLite C-Datei meinem Makefile hinzufügen und neu kompilieren. Dazu gibt es Beispiele und nach einigem Gefummel habe ich SQLite und die Boost/Graph-Bibliothek nativ unter Android am Laufen. Der Nachteil von nativen Programmen unter Android ist aber die Geschwindigkeit. Während es an meinem Desktop-Rechner eine halbe Sekunde dauert jedes Wort eines Bibelbuches inklusive seiner 10 Parameter aus der Datenbank einzulesen und aus den Worten der Nominalphrasen einen Graphen aufzubauen und die Betweenness-Centrality sowohl der Kanten als auch der Knoten des Graphes auszurechnen, sind es auf meinem schon betagten Nexus S ca. 30 Sekunden. Diese Geschwindigkeit ist nicht akzeptabel. Auch hier wieder: Was soll der Spieltrieb? Tatsächlich sagt auch die Android Developer-Doku, dass man schon einen triftigen Grund haben sollte, um nativ unter Android zu programmieren, da es mit Sicherheit die Komplexität der Entwicklung erhöht, nicht aber unbedingt die Geschwindigkeit der Ausführung. Muss es also unbedingt dynamisch sein? Es reicht doch, wenn ich vorher die Betweennes Centrality für Kanten und Knoten ausrechne und die Ergebnisse in der Datenbank festhalte. Bei der Bibel handelt es sich schließlich um statischen Inhalt und es ist nicht damit zu rechnen, dass noch mehr Inhalt hinzugefügt wird. Was seit tausenden von Jahren penibelst vor Veränderungen geschützt wurde, wird wahrscheinlich nicht plötzlich geändert werden. Wenn ich vorher die Zentralitäten ausrechne, kann ich dies sogar für die gesamte Bibel tun. Ich kann alle ca. 800.000 Worte der Bibel einlesen und einen Graphen über die gesamte Bibel aufbauen und errechne mir damit Worte, die bibelweit zentral sind. Wenn ich diese Zentralitäten für meine Studienbibel benutze, helfe ich dem Benutzer die Bibel im Überblick zu behalten.

Normalisiert vs. Unormalisiert

Soll ich nun meine Datenbank normalisieren oder unnormalisiert lassen. An jeder Informatik-Uni lernt man, dass man Daten in der Datenbank natürlich normalisieren sollte. Aber das gilt nur für Daten, die sich vermehren. Eine unnormalisierte Datenbank verschlingt zuviel Platz, da Einträge vielfach vorkommen können und die Datenbank schlecht gewartet werden kann. Andererseits spart man sich auch komplizierte Join-Anfragen. Wenn ich wirklich meine Datenbank normalisieren würde, würde ich aus der zentralen Tabelle 5 Tabellen machen und müsste damit über ein 5-fach verschlachteltes Join-Statement 5 Tabellen abfragen. Ich habe gehört, dass sich das nicht sonderlich positiv auf die Performance auswirkt. Allerdings werde ich das noch ausprobieren. Denn der Benutzer lädt sich auch nicht gerne 100 MB herunter. Allerdings muss ich meine SQLite-DB indexieren. Wenn ich einen Index über häufig gefragte Spalten erstelle, antwortet SQLite in akzeptabler Geschwindigkeit auf eine Abfrage. Andernfalls dauert es einige Sekunden, bei jeder Anfrage erneut 800.000 Datensätze durchsuchen zu müssen.

Daily Activity

Mein Ziel: Eine garantierte Activity pro Tag, nämlich wenn das Smartphone angeschaltet wird und die Activity zum Einschaltdatum noch nicht aufgerufen wurde. Solche Dinge sollte man natürlich sehr bewusst programmieren, um keinen Benutzer zu nerven. In diesem Fall bin ich aber der Benutzer.
In der Referenz des Android SDK finde ich ein Systemereignis, welches in Frage kommt, um von einem Broadcast Receiver empfangen zu werden: ACTION_SCREEN_ON. Während ich mittels der AndroidManifest.xml für viele Ereignisse ganz zentral Broadcast Receiver während der Installation einer Anwendung im Betriebssystem registrieren kann, ist dies aber ausgerechnet für dieses Ereignis nicht möglich. Ich habe einige Codebeispiele gesehen, in denen Broadcast Receiver in der OnCreate von Activities registriert wurden. Doch möchte ich den Receiver von der aufgerufenen Activity entkoppeln. Nach weiterer Suche entdecke ich das Ereignis ACTION_USER_PRESENT, welches auch ein mittels der AndroidManifest.xml registrierter Broadcast Receiver empfangen kann.

Wird das Ereignis mit nachfolgendem ActivityLauncher empfangen, wird das Datum des heutigen Tages mit dem eingelesenen Datum aus einer Datei verglichen. Unterscheiden sich die Angaben, wird das aktuelle Datum in die Datei geschrieben und die Activity wird aufgerufen.

public class ActivityLauncher extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
      
        if (! (intent.getAction().equals(Intent.ACTION_USER_PRESENT))) 
          return;
        
        String date = GetDateFromFile(context);
    
        Date dateNow = new Date ();
        SimpleDateFormat dateformatYYYYMMDD = new SimpleDateFormat("yyyyMMdd");
        StringBuilder nowYYYYMMDD = new StringBuilder( dateformatYYYYMMDD.format( dateNow ) );
        String now = nowYYYYMMDD.toString();
        
        if (date.compareTo(now) != 0)
        {
          WriteCurrentDateToFile(context, now);
         
            Intent startActivity = new Intent();
            startActivity.setClass(context, MyActivity.class);
            startActivity.setAction(MyActivity.class.getName());
            startActivity.setFlags(
                    Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            context.startActivity(startActivity); 
        }
    }
}