Multi-Screen iOS Apps with PhoneGap

Posted: January 12th, 2012 | Author: | Filed under: Adobe, Apps, Development, HTML5, iOS, JavaScript, Mobile, open source, PhoneGap | Tags: , , , , , , , | 25 Comments »

Did you know that apps built on top of iOS can have a multi-screen workflow? For example in Keynote, you can have an external screen show a presentation while you control it on your iOS device. In the Jimi Hendrix app, you can view the audio player on an external screen, and in Real Racing HD, you can view the game on an external screen while the iOS device becomes your controller. (among others)

Real Racing HD

This is all made possible by the UIWindow and UIScreen APIs in iOS. Even better, on the iPad 2 and iPhone 4Gs, this can be done wirelessly using Airplay with an Apple TV device. On other iOS devices, you can have a second screen using a VGA output.

One of the benefits of using a cross platform solution like PhoneGap or Flex/Air is that you can build apps with an easier to use/more familiar paradigm.  However, cross platform runtimes don’t always offer access to every API feature that native development enables.

Out of the box, PhoneGap apps are confined to a single screen.  You can use screen mirroring to mirror content on an external screen, but you can’t have a second screen experience.  It’s a good thing you can write native plugins/extensions to enable native functionality within your applications.

ExternalScreen Native Plugin For PhoneGap

I recently did exactly that… I created a PhoneGap native plugin that enables second screen capability for PhoneGap applications.   The plugin listens for external screen connection notifications, and if an additional screen is available, it creates a new UIWebView for HTML-based content in the external screen – complete with functions for injecting HTML, JavaScript, or URL locations.

Why?

You might be wondering “Why?” you would want this plugin within PhoneGap…  this plugin enables the multi-screen experiences described in the apps mentioned above.  They extend the interactions and capabilities of the mobile hardware.   With this PhoneGap native plugin, you can create rich multi-screen experiences with the ease of HTML and JavaScript.   Here are a few ideas of the types of apps that you can build with this approach (scroll down for source code):

Fleet Manager

Let’s first consider a simple Fleet Manager application which allows you monitor vehicles in a mobile app.  This is a similar concept which I’ve used in previous examples.   The basic functionality allows you to see information on the tablet regarding your fleet.   What if this app connected to a larger screen and was able to display information about your vehicles for everyone to see?   Watch the video below to see this in real life.

This application example is powered by Google Maps, and all of the data is randomly generated on the client.

Law Enforcement

Let’s next consider a mobile law enforcement application application which gives you details to aid in investigations and apprehension of criminals.  Let’s pretend that you are a detective who is searching for a fugitive, and you walk into a crowded bar near the last known location of that fugitive.  You connect to the bar’s Apple TV on their big screen TV, pull up images and videos of the suspect, then say “Have you seen this person?”.   This could be incredibly powerful.  Check out the video below to see a prototype in real life.

This law enforcement demo scenario is a basic application powered by the FBI’s most wanted RSS data feeds.

Tip Of The Iceberg

There are lots of use cases where a second screen experience could be beneficial and create a superior product or application.   Using PhoneGap allows you to build those apps faster & with the ease of HTML and JavaScript, using traditional web development paradigms.

How It Works

Now, let’s review what makes this all work…   The client interfaces for both of these samples are written in HTML & JavaScript, and utilize jQuery, iScroll, and Modernizr, with a trick for removing link click delay on iOS devices.

The PhoneGap native plugin is written in Objective C, with a JavaScript interface to integrate with the client application. PhoneGap plugins are actually very easy to develop.  Basically, you have to write the native code class, write a corresponding JS interface, and add a mapping in your PhoneGap.plist file to expose the new functionality through PhoneGap.  There is a great reference on the PhoneGap wiki for native plugins which includes architecture & structure, as well as platform specific authoring and installation of those plugins.    Here are quick links to the iOS-specific native plugin content authoring and installation.

The ExternalScreen plugin creates a UIWebView for the the external screen, and exposes methods for interacting with the UIWebView.   Note: This is just a normal UIWebView, it does not have support for all PhoneGap libraries… just a standard HTML container.

You can read up on multi-screen programming at iOS from these useful tutorials:

Now let’s first examine the native code:

PGExternalScreen.h

The header file shows the method signatures for the native functionality.  The corresponding PGExternalScreen.m contains all of the actual code to make it all work.   Note: If you are using ARC (Automatic Reference Counting), you will need to remove the retain/release calls in PGExternalScreen.m.

@interface PGExternalScreen : PGPlugin {

    NSString* callbackID;
    UIWindow* externalWindow;
    UIScreen* externalScreen;
    UIWebView* webView;
    NSString* baseURLAddress;
    NSURL* baseURL;
}

@property (nonatomic, copy) NSString* callbackID;

//Public Instance Methods (visible in phonegap API)
- (void) setupScreenConnectionNotificationHandlers:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options ;
- (void) loadHTMLResource:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) loadHTML:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) invokeJavaScript:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) checkExternalScreenAvailable:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

//Instance Methods
- (void) attemptSecondScreenView;
- (void) handleScreenConnectNotification:(NSNotification*)aNotification;
- (void) handleScreenDisconnectNotification:(NSNotification*)aNotification;
@end

PGExternalScreen.js

The PGExternalScreen.js file defines the native methods that are exposed through PhoneGap.   You invoke the function, and can add success/fail callback function references.

var PGExternalScreen = {

    setupScreenConnectionNotificationHandlers: function (success, fail) {
        return PhoneGap.exec(success, fail, "PGExternalScreen", "setupScreenConnectionNotificationHandlers", []);
    },

    loadHTMLResource: function (url, success, fail) {
        return PhoneGap.exec(success, fail, "PGExternalScreen", "loadHTMLResource", [url]);
    },

    loadHTML: function (html, success, fail) {
        return PhoneGap.exec(success, fail, "PGExternalScreen", "loadHTML", 1);
    },

    invokeJavaScript: function (scriptString, success, fail) {
        return PhoneGap.exec(success, fail, "PGExternalScreen", "invokeJavaScript", [scriptString]);
    },

    checkExternalScreenAvailable: function (success, fail) {
        return PhoneGap.exec(success, fail, "PGExternalScreen", "checkExternalScreenAvailable", []);
    }

};

The Client

You can call any of these functions from within your PhoneGap application’s JavaScript just by referencing the exposed method on the PGExternalScreen instance.

// check if an external screen is available
PGExternalScreen.checkExternalScreenAvailable( resultHandler, errorHandler );

//load a local HTML resource
PGExternalScreen.loadHTMLResource( 'secondary.html', resultHandler, errorHandler );

//load a remote HTML resource (requires the URL to be white-listed in PhoneGap)
PGExternalScreen.loadHTMLResource( 'http://www.tricedesigns.com', resultHandler, errorHandler );

//load a HTML string
PGExternalScreen.loadHTML('</pre>
<h1>HTML</h1>
<pre>this is html content', resultHandler, errorHandler );

//invoke a JavaScript (passed as a string)
PGExternalScreen.invokeJavaScript('document.write(\'hello world\')', resultHandler, errorHandler );

The full code for the ExternalScreen PhoneGap native plugin, as well as both client applications and a basic usage example is available on github at:

Be sure to read the README for additional setup information.

(Update: source code link changed)


25 Comments on “Multi-Screen iOS Apps with PhoneGap”

  1. 1 Raymond Camden said at 10:07 PM on January 12th, 2012:

    Fraking epic!

  2. 2 Tegan Snyder said at 11:20 PM on January 12th, 2012:

    I literally can’t wait to get to work tomorrow and start playing with this. Excellent stuff!

  3. 3 Dawesi said at 1:07 AM on January 13th, 2012:

    now all we need is android, bb, etc… ;-)

  4. 4 Nicolas BUI said at 5:55 AM on January 13th, 2012:

    Awesome :)

  5. 5 Andrew said at 9:35 AM on January 13th, 2012:

    Do any of the other platforms support multiple screens? I’ve never seen a demo of it. I’ll have to look into it and see if its even possible.

  6. 6 Multi-Screen iOS Apps with PhoneGap | HTML5 Game Development said at 5:15 PM on January 14th, 2012:

    [...] http://www.tricedesigns.com/2012/01/12/multi-screen-ios-apps-with-phonegap/ ShareFacebookRedditDiggStumbleUponPrintEmail [...]

  7. 7 Alan said at 6:53 PM on February 17th, 2012:

    I’m running your basic sample on iPhone4S, and an AppleTV on same network, but the UIScreen count is always 1. I can share photos to the tv tho. Is there anything I’m missing? maybe a setting somewhere?

  8. 8 Alan said at 8:19 PM on February 17th, 2012:

    I sorted it out. Works great! So I’m having an issue with entering text once a second screen is in place. Have you run into this or found a solution?

  9. 9 Andrew said at 10:58 PM on February 19th, 2012:

    What issue ar you seeing? You won’t be able to set input focus on the second screen.

  10. 10 Andrew Thorp said at 3:48 PM on February 24th, 2012:

    Working great in loading the content, but external assets (css, images, etc) do not seem to be appearing correctly.

    I am using the following:

    But the styles aren’t making it. If i copy them to inline styles they work, but the url’s to images do not work (assets/img/img-here.png).

    Any ideas?

  11. 11 Andrew Thorp said at 4:45 PM on February 24th, 2012:

    Followup to the previous question, I am trying to load a ‘local’ html file.

    This is a HTML file that needs to have a background image and needs to be able to execute jQuery. Can I not use external assets in a ‘local’ html file that is loaded via loadHTMLResource();

    Thanks!

  12. 12 Andrew said at 4:48 PM on February 24th, 2012:

    Hi Andrew, I think it might be a bug in the plugin… I’ll have to look into it further. This is an issue w/ the root for the UIWebView. I’ll look into it and get back to you.

  13. 13 Andrew Thorp said at 5:12 PM on February 24th, 2012:

    Andrew,

    I have been searching for it today and have come up with similar findings. baseURL might not be set correctly.

    I really like PhoneGap and love your plugin – and am glad that I won’t have to abandon it!

    I have a demo coming up very soon and am looking forward to showing off the abilities of these tools.

    Thanks again!
    Andrew

    p.s. Sorry for bugging you about it a couple of times. :)

  14. 14 Andrew said at 6:35 PM on February 24th, 2012:

    It has been fixed, and the latest version is now in github at: https://github.com/triceam/phonegap-plugins/tree/master/iPhone/ExternalScreen I’ve initiated a “pull request” to have the external screen plugin updated in the main phonegap-plugins repo.

  15. 15 Andrew Thorp said at 6:49 PM on February 24th, 2012:

    Thanks! Works great!

    One question: Do I have any control over the resolution that it is displayed on the second screen at?

    Thanks for all of your help!
    Andrew

  16. 16 Andrew said at 10:46 PM on February 24th, 2012:

    It is not exposed through the phonegap plugin, but it can be controlled in native code. See this article for details: http://mattgemmell.com/2010/06/01/ipad-vga-output/

  17. 17 Andrew Thorp said at 12:32 AM on February 25th, 2012:

    Gotcha – so would it be possible for me to tie into your plugin and/or to PhoneGap to accomplish this without having to write a native iPad application?

    Thanks!
    Andrew

  18. 18 Andrew said at 3:30 PM on February 25th, 2012:

    You would have to modify the native code within the plugin to detect & change screen size. It’s not that complicated, and you wouldn’t have to write an entirely new native app from scratch.

  19. 19 Andrew said at 2:53 PM on February 26th, 2012:

    The latest bug fix, which fixes relative paths of local content (due to an incorrect base url) has been merged in the main phonegap-plugins repository.

  20. 20 Andrew Thorp said at 6:35 PM on February 26th, 2012:

    Hey,

    I have the VGA output working, but it is only allowing me to choose 720×480 when I connect it to my HDTV.

    If I don’t do anything with resolution picking, your webView is filling up the whole screen at 16:9. Any idea why availableModes is only returning a single resolution?

    (I realize you are not the person who developed that iPadVGAOutputTest, but you have just been so helpful!)

    Cheers!
    Andrew

  21. 21 Andrew said at 10:12 PM on February 26th, 2012:

    720×480 is the resolution of a 480p HD video. Perhaps that is the only video resolution supported by your TV? Are you connecting to the external screen via HDMI, VGA, or Airplay?

    The UIScreen.availableModes property “contains one or more UIScreenMode objects, each of which represents a display mode supported by the screen.” – this is from the docs at: https://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIScreen_Class/Reference/UIScreen.html

  22. 22 Andrew Thorp said at 10:15 PM on February 26th, 2012:

    Yeah, I do understand that it is returning what is supported by the screen, but it’s odd that if I don’t set it (just use your plugin code) – it seems to use the screens ‘native’ resolution, which is much larger than 720×480.

    It actually outputs at 16:9, and 720×480 is 3:2, so I know it CAN feed it out at 16:9.

    I’m connecting with VGA…

    Thanks!

  23. 23 Andrew Trice » Blog Archive » PhoneGap Native Plugins said at 2:44 PM on March 1st, 2012:

    [...] [...]

  24. 24 Andrew Thorp said at 2:18 PM on March 12th, 2012:

    Hey Andrew,

    I have another question.

    The ‘handleScreenConnectNotification’ method. Wouldn’t it be more useful if that sent a notification back to the JavaScript that I could listen for?

    If the display is disconnected and reconnected mid application, I am not aware of it in the app itself. Am I supposed to test every second to see if there is a second screen? Or am I missing something?

    My code looks like this: https://gist.github.com/2023751

    When I first load the application, whether there is a second screen or not, I see: “External Screen Notification Handlers initialized.”

    However, if I plug my second screen in after that, I see a white screen with “loading…” on it, but the resultHandler is never fired again.

    Thanks!

  25. 25 Andrew said at 11:29 PM on March 12th, 2012:

    You can add a call to [self writeJavascript: @"put javascript here"]; inside of both handleScreenConnectNotification and handleScreenDisconnectNotification, and it will do this for you. If I have time, I can add them later this week, but it should be pretty easy to add yourself to get started w/o waiting on me. The writeJavascript function will execute any JS string within the PhoneGap web view.


Leave a Reply

  •