Thursday, December 27, 2012

Templated PDF Printing for Appcelerator based iOS Apps


Content

  1. Motivation
  2. Idea of a solution
  3. Detailed explanation
  • IOS Module
  • HTML-Template
  • Inline-Images
  • HTML Page-Formatting

Motivation

With Javascript being the language of choice within the appcelerator framework,
a huge variety of libraries and techniques for common problems, the catch being,
that many of these depend on the context of a web browser to funtion properly.
When it comes to creating PDF data within Appcelerator apps, so far the approach
had therefore been to small pure JS libs, such as jsPDF (http://jspdf.com/).
Yet, because of it's lowlevel approach (of drawing rectangles and lines) and
rendering text, it can be cumbersome to create a fully layouted document.

Idea of a solution

As an alternative, I propose to make use of Appcelerators module framework, i.e.,
making use of native code, to pass an html string to an IOS module that will
render the content in a UIWebview object and subsequently to a PDF file, that
is returned to the application layer.

IOS Module

The module used for this solution has a method "setHTMLString", that can be
called from the application layer with the HTML string to be rendered in
a UIWebview object. Upon loading, the method "webViewDidFinishLoad" is called,
where the page then is rendered to a PDF file, subsequently firing an event
"pdfready", including a path to the file. This is necessary, as events in
Appcelerator can only carry JSON serializable data. Because the UIWebkit library
is not thread safe, all the calls in "setHTMLString" and "webViewDidFinishLoad"
have to be queued on the main thread by using "dispatch_async". For details check
the source code from github, referenced below. For general information about
developing iOS modules, check https://wiki.appcelerator.org/display/guides/iOS+Module+Development+Guide

HTML Template

Since we are passing an HTML string from the application layer and basically
render it in a browser window, all known HTML traits apply and the document
can be layouted, as applicable. But, since there usually will be no webserver
running to pull resources from, you have to inline any CSS or JS code into the
HTML string, you pass to the IOS module. Based on such a template you might
create your final HTML string by replacing certain placeholders, such as
<!--PLACEHOLDER--> with content data. Or you choose to just construct your
HTML string, as needed. Consider the following Appcelerator code:
 var html2pdf = require('com.factisresearch.html2pdf');  
 Ti.API.info("module is => " + html2pdf);  
   
 html2pdf.addEventListener('pdfready', function(e) {  
   var emailDialog = Ti.UI.createEmailDialog();  
     emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));  
     emailDialog.open();  
 });  
   
 var html = '<html><body><p>Hello World!</p></body></html>';  
   
 html2pdf.setHtmlString(html);  


Inline Images

Of course it should be possible to add images to the document. Because generally
it will not be possible to pull resources, the images also have to be embedded
into the HTML string. How this can be done is explained here:
http://www.techerator.com/2011/12/how-to-embed-images-directly-into-your-html/
and in countless other places. Consider the following code:
 var html2pdf = require('com.factisresearch.html2pdf');  
 Ti.API.info("module is => " + html2pdf);  
   
 html2pdf.addEventListener('pdfready', function(e) {  
   var emailDialog = Ti.UI.createEmailDialog();  
     emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));  
     emailDialog.open();  
 });  
   
 var html = '<html><body>';  
   
 var imageData = Titanium.Filesystem.getFile(Ti.Filesystem.getResourcesDirectory(), 'Default.png').read();  
 var iv = Ti.UI.createImageView({  
     image: imageData  
 });  
 var image = iv.toImage();  
 var imageB64 = Ti.Utils.base64encode(image);  
       
 html += '<p><img src="data:image/png;base64,'+imageB64+'"></p></body></html>';  
   
 html2pdf.setHtmlString(html);  


HTML Page-Formatting

The IOS module described above uses static page sizes and bounds for the printable
area. You may however want to customize this for different pages. On this account
CSS offers a range of customization options, such as the "@page" attribute, where
margins can be defined or the "page-break-before" style, that can set on an html
element to enforce the subsequent content to be displayed on a new page in the
resulting pdf.
-------------------------------
 var html2pdf = require('com.factisresearch.html2pdf');  
 Ti.API.info("module is => " + html2pdf);  
   
 html2pdf.addEventListener('pdfready', function(e) {  
   var emailDialog = Ti.UI.createEmailDialog();  
     emailDialog.addAttachment(Ti.Filesystem.getFile(e.pdf));  
     emailDialog.open();  
 });  
   
 var html = '<html><body>';  
   
 var works = queryDBForWorks();  
 for (var i in works) {  
     var entry = {};  
     var rs = db.getWorkInfo(works[i]);  
       
     html += ('<h2 '+(i == 0 ? '>' : 'style="page-break-before:always;">')+''+rs.fieldByName('TITLE')+' - '+rs.fieldByName('ARTIST')+'</h2>');  
     html += ('<img src="data:image/jpg;base64,'  
             +Ti.Utils.base64encode(rs.fieldByName('THUMBNAIL'))+'">');  
     html += ('<p>'+''+rs.fieldByName('TEXT')+'</p></div>');  
     rs.close();  
 }   
   
   
 html2pdf.setHtmlString(html);  

You may pull an implementation, along with an example from
https://github.com/fscz/html2pdf

Sunday, October 7, 2012

Developing Medical Software in Scala and Haskell

Just recently, I gave a talk at the CUFP (Commercial Users of Functional Programming) workshop about our experience with developing software in the functional programming languages Haskell and Scala. The slides of the talk are available here, and there is even a video recording. Enjoy! Any feedback is welcome, just post a comment below.

By the way, CUFP hosted a whole bunch of very interesting talks. They are all available on youtube, just search for "CUFP 2012".

Thursday, August 9, 2012

RedmineR - Redmine iOS App

Wir sind vor kurzem auf die neue Bug-Tracking Software "Redmine" umgestiegen. Über den Tracker lassen sich sehr bequem Projekte, Aufgaben und Bugs verwalten. Redmine lässt sich von einem PC oder einem Tablet einfach und übersichtlich bedienen, auf Smartphones verliert man jedoch schnell die Übersicht. Deshalb haben wir eine iPhone-App ("Redminer") geschrieben, die einfach aufgebaut ist und mobilen Zugriff auf den Redmine-Server ermöglicht. Aus Redminer kann man dann Tickets ansehen (alle Tickets, Tickets pro Projekt, meine Tickets), Tickets bearbeiten und neue Tickets anlegen. Der Zugriff erfolgt über HTTP/HTTPS via Redmine-REST-API, die der Remine-Admin vor der Verwendung freischalten muss. Alle Anfragen werden auf dem iPhone lokal gespeichert, sodass auch ohne Internetverbindung auf die Tickets zugegriffen werden kann.

Die Verwendung der App ist denkbar einfach: Zuerst muss "Redminer" kostenlos aus dem App Store geladen werden. Danach wird der API-Zugriffschlüssel benötigt - dieser wird in der normalen Redmine-Oberfläche unter "Mein Konto > API-Zugriffsschlüssel" angezeigt. Der Zugriffschlüssel wird dann auf dem iOS-Gerät unter "Einstellungen > Redminer" bei "API-Key" eingetragen. Außerdem muss hier noch die Tracker-URL eingetragen werden.

Download und weitere Informationen: Redminer für iPhone

Screenshots:




Tuesday, July 24, 2012

Android-App: Anrufweiterleitung


Anrufweiterleitung - einfach und unkompliziert Anrufe auf ein anderes Handy oder eine Festnetz-Nummer weiterleiten und wieder ändern.
Viele Unternehmen müssen auch außerhalb der üblichen Bürozeiten eine ständige Erreichbarkeit für den Kunden sicher stellen. Häufig wird deshalb mit Rufumleitungen auf Handys gearbeitet, so z.B. auch in der ambulanten Pflege. Hier übernehmen einzelne Mitarbeiter außerhalb der Bürozeiten, die sogenannte Rufbereitschaft. Kunden können dann auf einer Notfallnummer anrufen und die Mitarbeiterin, die in der Rufbereitschaft ist, nimmt das Telefonat auf ihrem Handy entgegen und kümmert sich um die Bearbeitung der Anfrage. Die Notfallnummer ist entweder auf ein speziell dafür vorgesehen Rufbereitschafts-Handy geschaltet oder die Nummer wird weitergeleitet auf das jeweilige Handy der Mitarbeiterin.
All diese Fälle unterstützt die App Anrufweiterleitung, indem schnell auf die letzte gespeicherte Rufnummer, einer Nummer aus dem Adressbuch oder auf eine neue Nummer Anrufe weitergeleitet werden. Und mit einem einzigen Klick ist die Weiterleitung wieder ausgeschaltet.
Und für den privaten Einsatz? Lässt sich die App genauso einfach uns sicher einsetzen wir im geschäftlichen Bereich.
Hinweis: die App zeigt immer zuverlässig an, ob weitergeleitet wird oder nicht. Auch wenn die Weiterleitung über eine andere App oder direkt über das Handy-Menü ein- bzw. ausgeschaltet wird. Das gibt die nötige Sicherheit, wenn im geschäftlich aber auch im privaten Bereich die Erreichbarkeit sicher gestellt sein muss.

Sunday, June 3, 2012

UIs for Hierachical iPad Apps


If you're in a hurry: tl;dr version

At factis research we developed FRLayeredNavigationController, a view controller compositor which enables you to develop -- as we think -- great iPad apps. Influenced by the UI of the Twitter and Soundcloud iPad apps we designed a new navigation concept for our product Checkpad MED. Since we love open source software and know what we owe the community we'll again try to give something back: FRLayeredNavigationController gives iPad developers a simple drop-in replacement for UINavigationController for great UIs. This screencast shows a very simplistic app made with FRLayeredNavigationController.

Full-blown post

Motivation

The Apple iPad is a great mobile device which is due to its big screen capable of displaying a lot of content at the same time. This capability makes the iPad a great device for apps with complex, structured data. A question Apple leaves to its developers is how to guide the users through deeply hierachical content.

On the iPhone the usual way is to use UINavigationControllers. Because of the small screen, UINavigationControllers are great on the iPhone, only the currently most relevant information is displayed. The sole trace of the navigation path from app start to the current content is the title of the second-to-last view controller in the back button at the upper left side of the screen.

On the iPad UINavigationControllers alone are fine, too, when all your view controllers really have an iPad-screenful of content. But, most apps have some kind of low-content view controllers which are primararilly made to bring the users to the actual content, stepping through several layers of the hierarchy. Often, these controllers are simple UITableViews which look bad when they are displayed fullscreen on the iPad. To solve this problem, Apple gave us UISplitViewController: the navigation-only part on the smaller left side (or even in a UIPopoverController) and the content right next to the navigation in a somewhat bigger frame. This concept works great and looks good as demonstrated at least by the iPad Settings and Mail apps. Compare them to their iPhone counterparts and you will see how nicely they work on these very different devices with minor changes only.

UISplitViewControllers work great when your content hierarchy's depth is two: One navigation layer and one content layer. Three layers is still okay, since you can use a UINavigationController inside the left or right side of the UISplitViewController. But if the depth of your content hierarchy exceeds three, it does not fit nicely in the UISplitviewController somehow, you have to go other ways.

At factis research we particularily liked the Twitter and Soundcloud iPad apps and decided to go a similar path with our product Checkpad MED. We called our solution FRLayeredNavigationController because it enables developers to build schrieblesque apps with multiple hierarchy layers.

How does it work?

The basic idea is not to completely hide a view controller when a new view controller is being shown.  FRLayeredNavigationController still shows some pixels of the view controllers the user passed through until reaching the currently active one. This will give the user a good idea of where (s)he currently is in the navigation hierarchy. If the user should need more information of these partly covered view controllers, (s)he can always reveal the covered pixels by intuitive pan gestures. The user will think of a stack of paper and has similar interaction options. Probably you get a better idea when looking at these screenshots:

regular view
after revealing more information in the level-2 view controller (the "Teams" view controller) by swiping  to the right
Also watch these two iPad screencasts to get the idea: http://youtu.be/v_tXD_mL05E and http://youtu.be/q66HX2td_uc .

Technically, it uses view controller composition as introduced in iOS 5. It manages multiple "Layers" which are UIViewControllers you provide. The moving of the layers is triggered by a UIPanGestureRecognizer and some logic when to move which layer. It looks and feels very nice due to the nice iOS animation support.

Can I use FRLayeredViewController, too?

Sure, it's open sourced and available at GitHub! If you have any problems, feel free to contact us.

What is the API like?

Have a look at the API documentation, it's quite similar to UINavigationController's API from Apple.

Say, you have a UIViewController and you're using UINavigationController from Apple. To show a new layer to the user, you would do:

[self.navigationController pushViewController:test animated:YES];

with FRLayeredNavigationController it's quite similar:

[self.layeredNavigationController pushViewController:test
                                           inFrontOf:self
                                        maximumWidth:NO
                                            animated:YES];

The maximumWidth parameter is how you tell FRLayeredNavigationController whether the new view controller is content (give it the maximal width still available) or not.

Just as UINavigationController, it also supports UIBarItems and a configurable title view. For example, a UIViewController which has been pushed onto a FRLayeredNavigationController could have the following viewWillAppear method to add a back button on the upper left corner:

- (void)viewWillAppear:(BOOL)animated
{
    self.layeredNavigationItem.leftBarButtonItem =
        [[UIBarButtonItem alloc]
         initWithImage:[UIImage imageNamed:@"back-button.png"]
                 style:UIBarButtonItemStylePlain
                target:self
                action:@selector(backButtonClicked)];

    self.layeredNavigationItem.leftBarButtonItem.style =
        UIBarButtonItemStyleBordered;
}

Have a look at FRLayeredNavigationController and FRLayeredNavigationItem to see what's possible with FRLayeredNavigationController.

How can I embed FRLayeredViewController in my project?

It's explained step-by-step in the README file and in this screencast.