Appstore Blogs

Appstore Blogs

Want the latest?

appstore topics

Recent Posts

Archive

Showing posts by Russell Beattie

April 06, 2015

Russell Beattie

The New Web App Starter Kit Features - Subcategories, YouTube Support, Live Streaming, in App Purchases and More - Make It Even Easier to Create Engaging Fire TV Media Apps

Launched last December, the Web App Starter Kit for Fire TV is an open source project intended to help developers get up to speed quickly creating simple media-oriented apps for the Amazon Fire TV and Fire TV Stick. The Starter Kit provides a basic media app template that is designed for the 10-foot user experience and includes UI controls, support for the Fire TV remote, sample UI components, support for various data formats and more. Since its release, the Starter Kit has been used to create dozens of apps already live in the Amazon Appstore for Fire TV, including Wall Street Journal Live, University of California TV, University of Washington TV, Gear Live TV, and Motorz TV.

You can find the Starter Kit on the GitHub project page.

The latest release, version 1.2, just launched and we want to give you an update on some of the new additions that benefit both developers and end users alike. These include technical improvements such as a streamlined build process and code enhancements to help reduce app development time, as well as a host of new client features. The Starter Kit now has support for YouTube videos, podcast feeds, a cleaner user interface, continuous video playback, search, live streaming videos and more. The end result is a richer code base that improves the developer experience and helps jump start the creation of great looking, functional, full-featured media apps.

Here are some more details about some of the cool new features we've added.

Easily Create & Manage Multiple Apps

In addition to general bug fixes, code optimization and documentation cleanup, we've also completely reorganized and streamlined the original build process to make it much easier to create and manage multiple apps. This gives developers more flexibility, and gives us the ability to include a variety of sample media apps that show off all the features and functionality available in the Starter Kit. Apps are now self-contained in a src/projects folder, which hold unique files for that app, while pulling files from the src/common folder during the build process. The result is a much more organized project structure, allowing developers to create more than one media app within the same source tree, enabling both code reuse and the capability of easily individualizing each app as needed.

We've also made it much easier for developers to customize the functionality and look of their apps without having to dig into the project code as much. The build process allows for customizing the included CSS and assets, and the project's init.js configuration file allows developer to enable/disable various options such as button display, player types, data models, logo and more. For more specifics on all the various options available for developers, check out the documentation hosted on the GitHub project page.

Quickly Create Fire TV Apps Based on YouTube Content

One of the most important new features that we've added to the Starter Kit is the ability to use YouTube as the source for an app's media content. We've integrated the official YouTube API and the YouTube HTML5 video player into the Starter Kit template so that developers can easily create YouTube-based Fire TV web apps. By simply adding in their YouTube API Developer Key, choosing a channel, and adjusting the project's initialization options, a developer can create a full-featured media app in very little time. The project gives developers the flexibility to choose to automatically add categories based on a channel's current section setup, or create a customized category menu with selected playlists, channels and even search terms. Below are screenshots of an example app created from the official Amazon Web Services YouTube channel, with the logo and categories automatically imported using the YouTube API.

When the user has selected a category (such as "Featured"), the screen displays a horizontal list of videos to browse and play. It's incredibly easy for content owners and developers to use the Starter Kit to quickly create custom media apps for their YouTube content.

Built-in Search Helps End Users Discover Content

The Starter Kit now gives developers the option to enable integrated search functionality, with no extra dev effort needed. For media apps that use a static data feeds such as JSON or MRSS, the search will be done on the client, returning results derived from the feed's media meta-data such as title and description. For apps using the YouTube template, the search will use the YouTube API to dynamically return results from the YouTube server. Below is a screenshot of a search for "Fire" in the above AWS YouTube channel app, with the results shown displayed in the same format as a category list.

Differentiate Your App With Custom Themes

Developers can easily customize their web apps using CSS to give a unique look and feel. We've provided several sample themes in the Starter Kit that show how easy it is to include a custom theme.css file to a project. Below is a screenshot of the AWS media app using a sample theme with a dark background, colored content titles and a custom category menu list that hovers over the list of videos rather than hiding them under a solid bar.

Build Apps From Thousands of Video Podcasts with Built-In Media RSS Support

The Starter Kit has also added support for using Media RSS as a data source. This common XML data format used by thousands of websites was created for podcasting, and adopted by a variety of content providers as a standard media feed format. Developers can now easily create media apps from these feeds in minutes. Below is a screenshot of a sample media app created using the public video podcast feed for a popular tech blog This Week In Tech.

Make App Content More Discoverable With Subcategories

The first version of the Starter Kit only supported one row of media within a given category. This worked well for many apps, but most need one or more levels of content below that. We've now added the ability for apps to have multiple levels of subcategories contained within the media list. This allows apps based on the Starter Kit to support navigation hierarchies such as Show/Season/Episode, Genre/Subgenre/Movie or simply create playlists within categories to present more organized content to viewers. Below is a screenshot of an example app using the official Kindle YouTube channel. Each playlist within the "All Playlists" category is represented by a thumbnail image and a description, presented in the familiar horizontal format. When the user selects a playlist, they get a list of videos within that subcategory which they can select and play, or by hitting the back button, go back up to the playlist selection screen again.

Enhanced Media Playback Controls

In addition to the navigation enhancements above, the Starter Kit now has added nicer playback controls for when the end user is viewing a video. We've improved the skip functionality and interface, providing the developer with better control over the length of the skip, and the end user with a clearer indicator of how far they have skipped back or forward. We also added the option for videos to play one after another within a category. As a video is nearing its end, a dialog will appear with the title, description and thumbnail of the next video to be played, and a countdown timer shows how long before it automatically starts. The user can activate the next video immediately by hitting the select button on their remote, or wait for the countdown and not have to touch their remote at all. The screenshots below show these new user interface options in action with a video from AWS.

Stream Your Content With Live Streaming Support

The Starter Kit includes a new custom HTML5 player that supports live internet video streams. The screenshots below show an example app created using NASA's popular live stream. The stream is displayed in a category list like a normal video, but is marked as live video. During playback, the stream is marked as being live, and controls that wouldn't work in a live stream (such as skip forward) are removed, making the user interface simpler and cleaner for end users.

An Easy Template for Implementing In-App Purchasing

Included in the Starter Kit is a new sample media app that shows how the Amazon In-App Purchasing system works for Fire TV web apps. Because of the potential complexity of subscriptions or media purchases, we decided to start with a more basic sample app that shows how IAP works by enabling users to purchase "upgrades" - in this case, custom app backgrounds. Below are screenshots of the IAP sample app in action. When a user clicks on the (large and obvious) Upgrades button, they are presented with the option to buy additional backgrounds for 99 cents each. If they select a background, they'll go through the standard Fire TV purchase process and the background will be unlocked for them to use. Though somewhat limited, the sample shows all the basic steps a developer will have to go through - adding graphics, descriptions, prices, etc. - to enable IAP content in their media app.

We hope you like the new features added so far and look forward to continuing to expand the Starter Kit functionality over the months to come. We'd love to hear your thoughts, so if you have any questions, comments or suggestions, please use the GitHub project page to send them to us.

Ready to Build a Web App for Fire TV?

 

 

December 12, 2014

Russell Beattie

Amazon Fire TV, including both the Fire TV and Fire TV Stick, now supports HTML5 web apps. The Web App Starter Kit for Fire TV is a new open source project intended to help developers get up to speed quickly creating a simple media-oriented app for this exciting new web platform. Features of the project include an example user interface designed for the 10-foot user experience, support for the Fire TV remote control, and sample components to create and customize a media app. You can find this project on its GitHub project page here.

Here's an example of a media app using video content from the recent AWS re:Invent conference:

Background

Over the summer of 2014, we ran an early-access beta program with a dozen or so web app developers who were interested in collaborating with us to bring their HTML5 media apps to Fire TV. The program was a huge success, pinpointing technical issues that needed to be resolved before we opened access to web apps to the larger developer community. One of the most important pieces of beta feedback was the request for some sort of template to target the "10-foot Experience" on Fire TV. Though web browsers have been on TVs since the mid 90s, developers have never really targeted big screens and thus there is a notable vacuum of examples, guides or code available that web app developers can use to get started.

To address this need, we decided to create an open source HTML5 web app "starter kit" that developers can use to quickly create a simple media app, ready to be customized and published in the Amazon Appstore. The project contains code developers can re-use in their apps, as well as a design template for the couch-focused user experience as well. Interacting with HTML5 via a remote control is a very different experience than that of a mouse or touch screen.

Create TV-Based Web Apps

The Web App Starter Kit for Fire TV is self-contained, open source HTML5 project for creating real-world TV-based web apps. Hosted on Amazon's GitHub account, the project is comprised of the HTML, JavaScript, CSS and support files needed to create a media browser style app.

The project is written as a simplified MVC-based web app with few external dependencies. It's been componentized so that developers can pull functionality into in their own app, such as support for the Fire TV remote, or they can decide to simply use the code "as-is" and only customize the existing app by modifying the style and image files.

Baked into the starter kit is the code needed to both provide the large screen experience consumers expect, as well as to pass Amazon Appstore testing during the app submission process. This includes support for remote control key codes, warnings before quitting the app, handlers for when the app is backgrounded, full-screen media playback, and more. At the bare minimum, the developer only needs to provide access to their media sources as a JSON document that includes a list of media files, thumbnail images, titles, descriptions and categories. The app then uses that data to display the media as a selectable list of categories and a rotating carousel of media content.

We've included documentation covering all aspects of the starter kit: A ReadMe document introduces the project, and architecture and styling guides walk developers through the options they have to use or customize the app for their needs.

Being on GitHub also opens the opportunity to both improve the documentation as time goes on, as well as receiving "pull requests" from those in the developer community who wish to help improve the starter kit in the future.

Getting Started

Let's run through the basic steps in getting a media web app up and running using the starter kit.  (See the documentation on GitHub for full details.) For an introduction to Web Apps on Fire TV be sure to read our other blog post, Publishing HTML5 Apps to the Fire TV.

First, you'll need to grab the code from the Amazon GitHub repository using git - the open source version control system. (If you haven't learned how to use git yet, GitHub has a great online tutorial which will walk you through the basic steps, or you can download one of their easy to use GUI clients.)

Local Web Server. Once you've cloned the repository to your system, you'll need to launch a local development web server to test it out. This can be done through various means, including using either Python or Node.js. Open a terminal or command line, and change into the base directory of the project, and use one of the following methods to create a local web server:

Using Python, create a simple HTTP server with this command:

Using Node and NPM, install the Serve package, then create the server:

Web App Tester. Though we can do development using our desktop browser by navigating to http://localhost:3000, you'll want to see what the app looks like on a big screen powered by a Fire TV device. To do that, we'll be using the Web App Tester to view the content. The Tester is an app provided by Amazon which you can install on your Fire TV. It lets you test your web app on a real-world big screen before you submit the app to the Amazon Appstore.

Once you've installed the app, note the IP address of your computer where you are serving the starter kit's content, then navigate to http://your-local-ip:3000, highlight the Test button and press select and the sample app will be displayed .

Customize Your App

Now that you have the starter kit installed and running, you will want to customize the look and feel of the app as well as provide your own content to populate the video list and category list. Below are the files you'll need to change.

Logo. To modify the logo, use your favorite text editor to open the index.html file found in the root of the project and search for the "app-logo" class. This is where you will add the URL to your new logo.

Later, once you are familiar with the design of the web app, you can modify more of the HTML to better reflect your content or brand. Again, the documentation provided with the starter kit will point out the overall architecture of the project.

Look and Feel. The project's CSS file is automatically generated from a Sass template found in the root of the project called firetv.scss. Check out the Sass website for info on how to install and use sass templates. Rather than needing to go through the entire CSS file looking for things to change, there is a variable file called _variables.scss which allows you to change the fonts, colors, etc. of the app by simply changing the values used in the main Sass file. Once you have modified the variables, you'll need to generate a new CSS file to be used by using Sass from the command line:

You can find more details on how to style an app in the project's documentation.

Content. The last step to creating your first big screen web app for the Fire TV is to create a JSON file with the details of your media content, providing the file names, URLs and meta data needed. A sample JSON file can be found in the ./assets folder of the project and can be modified, or the URL can be changed completely by editing the index.html page to change the settings.dataURL value.

The JSON file contains an array of these basic fields:

Note:

  • The URL fields (imgURL and videoURL) can be local files or external resources.
  • The categories field is an array of topics used for filtering videos, which will be automatically grouped and displayed in the main menu of the app.

For more information about the JSON format and other architectural questions, check out the project's documentation. To find out more about developing web apps from scratch for the Fire TV, check out the Developer Portal's Getting Started with Web Apps for Fire TV, which has lots of useful information and notes you should be aware of.

Submitting your app to the Amazon Appstore

Once you've customized the Starter Kit and have your Fire TV web app created, you can publish it to the Amazon Appstore within minutes - without having to do any native development. Developers have a choice of either hosting the app's asset files on their own web server and submitting just the URL, or uploading the assets to Amazon's servers, where it will be bundled into a standalone packaged app.

Follow the steps below to submit your web app:

1. Create a Free Amazon Developer account. 

2. Go to the New Web App Submission page and provide details about your Fire TV web app:

  1. General Information (title, category, support details, privacy URL)
  2. Availability and Pricing  (release date and base list price)
  3. Description
  4. Images and Multimedia (screen shots, icons)
  5. Content rating

3. Choose whether or not to submit a hosted web app or a packaged web app. (In this case, the latter).

4. Upload the web app files (HTML, JavaScript, CSS, content files/resources) as a zip file.

5. Choose both the Fire TV and Fire TV Stick in the Device Support section.

6. Submit!

For information on submitting both hosted and packaged apps, see Submitting or Updating Your Web App to the Amazon Appstore

Summary

The Web App Starter Kit for Fire TV is just in its beginning stages, so if you have any questions, suggestions, or (most importantly) pull requests, please use the Amazon GitHub project page to send them to us. We really look forward to seeing all the cool new media apps created by web developers!

Related Links

 

August 14, 2014

Russell Beattie

The Amazon Appstore now provides web app developers with the option to submit self-contained, packaged web apps. Developers only need to wrap the assets for their web app (i.e. HTML, CSS, JavaScript, images etc.) into a standard zip file and Amazon will offer that web app to millions of Kindle Fire and Amazon Appstore customers.

This new service expands on our previous web app solution, which enabled hosted mobile web sites to publish apps to the Amazon Appstore. This works well for thousands of mobile web developers who are already serving content to the web, and we'll be continuing to offer hosted web apps in the future. But for many other developers, hosted apps aren't an ideal solution - they might not have a server, don't want to deal with the extra burden and cost of maintaining a server-side host for their app, or want their app to work seamlessly while offline.

Amazon's packaged web app solution solves this problem by letting web developers bundle all the files needed for their app into a zip file. Similar to other pure HTML5 solutions, web developers can submit apps to the Amazon Appstore without any need to worry about native packaging or distribution. Developers can concentrate on creating cutting edge web apps that take advantage of the advanced capabilities of our Chromium-based Amazon WebView available on our Kindle Fire tablets, or create more cross platform web apps that run on any device where Amazon Appstore is installed.  Web developers can focus on creating great apps, and Amazon takes care of the native app creation and hosting process. 

How to Package Your HTML5 Web App

Creating a new packaged app is as simple as creating any other type of zip file. Make sure the app has an index.html file as its starting page, or indicate the path to the app's main launch page when submitting the app.  To ensure the app works offline, include all those assets as well.

A packaged HTML5 app project looks and works like a normal web project folder on a server. For example:

./project/index.html
./project/js/main.js
./project/js/libs/jquery.min.js
./project/css/main.css
./project/images/logo.png
./project/images/picture.jpg
...
etc.

You can use a GUI application like WinZip, or using zip from the command line. This command for zipping a folder will result in the basic zip file you need:

$ zip -r project.zip ./project
 
adding: project/ (stored 0%)
adding: project/css/ (stored 0%)
adding: project/css/main.css (stored 0%)
adding: project/images/ (stored 0%)
adding: project/images/logo.png (deflated 7%)
adding: project/images/picture.jpg (deflated 1%)
adding: project/index.html (deflated 47%)
adding: project/js/ (stored 0%)
adding: project/js/libs/ (stored 0%)
adding: project/js/libs/jquery.min.js (deflated 65%)
adding: project/js/main.js (deflated 69%)

Testing Your Packaged Web App

The Amazon Web App Tester has been upgraded to handle packaged web apps as well. If you're not familiar with the Web App Tester, check out this blog post, Getting the most out of the Web App Tester, which shows you how to use it to test your web app before submission, including enabling and using the Remote Development Tools and other tips and tricks.

In addition to entering URLs for hosted web apps, developers can now load their zipped web app files on to the device, which will be pulled in by the Web App Tester for viewing/debugging. Using the Android File Transfer tool, copy the zipped web app files you want to test to a folder in the root of the device:

/amazonwebapps

The app package file should then show up in Web App Tester, where you can click Extract and use the app as per normal.  You can also enter a URL pointing to a packaged zip file instead and follow the same steps above.

Submitting your HTML5 Packaged Web App to the Amazon Appstore

Developers need to follow the following simple steps to submit their web apps:

1. Create a Free Amazon Developer account. 

2. Go to the New Web App Submission page and provide details about the packaged web app:

  1. General Information (title, category, support details, privacy URL)
  2. Availability and Pricing  (release date and base list price)
  3. Description
  4. Images and Multimedia (screen shots, icons)
  5. Content rating

3. Choose whether or not to submit a hosted web app or a packaged web app. (In this case, the latter).

4. Upload the web app files (HTML, JavaScript, CSS, content files/resources) as a zip file.

5. Submit!

Final Thoughts

That's it! Creating HTML5 packaged apps for the Amazon Appstore is straight forward, but if you have any problems, be sure to check out the web app support forum, where you can find answers or post questions.

Amazon's packaged web apps are a great step forward for the open web. Creating cross platform apps using HTML5 technologies will enable a whole new class of apps created by a whole new generation of developers. We're excited to launch this new functionality and can't wait to see what developers do.

-Russ

June 17, 2014

Russell Beattie

The Cordova Push Notifications Plugin is a project that lets hybrid web app developers create apps that respond to custom push notifications from services such as the Apple Push Notification Service (APNS), Google Cloud Messaging (GCM) and most recently Amazon Device Messaging (ADM). This enables hybrid web apps to be notified when an online service has information for it - such as a personal message, sports score or appointment - even if the app is not currently running.

If you're unfamiliar with Amazon Device Messaging, it's a simple, efficient and free messaging service from Amazon that lets developers send push notifications from the cloud to Kindle Fire devices, helping app creators more deeply engage with their customers by keeping them up to date and involved.

You can find the plugin, and complete documentation on the Cordova PushPlugin Github repository, but here are the basics on getting started using the plugin for Amazon Fire OS.

Overview

Macintosh HD:Users:beattier:Desktop:cordova_adm:adm-cordova.png

Amazon Device Messaging works similarly to the other push notification services and has three main parts involved in the process:

  • Your server sends notifications (messages, updates, scores, etc.) to the ADM Servers to be routed to the client apps.
  • ADM Servers queue and route notifications to client apps.
  • Your  app receives the notification on a device.

The Cordova Push Notifications Plugin allows your hybrid web app to use JavaScript to register with the ADM Server and receive notification events when sent from your server.

Please note that ADM only works with Fire OS devices and the Cordova Amazon Fire OS platform target. For more information about setting up a Fire OS Cordova project, please see our earlier blog post,  Building Higher Performance Cordova-based Fire OS Apps By Implementing Amazon WebView Support.

ADM Credentials

In order for your server to send notifications to your app, both the server and app, need to be authenticated/registered with the ADM Server:

  1. Create a sample Cordova app for the Amazon Fire OS platform so you have the app name, package name and the debug build ready.
  2. Create a developer account on the Amazon Developer Portal if you don't already have one
  3. Fill out the New App Submission form and turn Device Messaging switch to ON.
  4. Create Security Profile to obtain ADM credentials for your app.
    Note: For development, you can use the MD5 signature of the debug apk created by Cordova build process found in <project_path>/platforms/amazon-fireos/ant-build.

You will need to make note of the OAuth Credentials (Client ID and Client Secret) to be used on your notifications server and the API Key to be used in your client app. These can be found on the Security Profile Management pages in the areas marked in green below:

Macintosh HD:Users:beattier:Desktop:cordova_adm:credentials.png

Below is a graphic which shows a general overview of the authentication process and how your client app and your server to authenticate and register with the ADM Server:

Macintosh HD:Users:beattier:Desktop:cordova_adm:authentication2.png

Install Push Notifications Plugin

Once you have created an initial Cordova app for the Amazon Fire OS platform, use the standard plugin command from your project directory to install the Push Notifications Plugin.

cordova plugin add https://github.com/phonegap-build/PushPlugin.git

This will download the plugin and install it in your app's project directory. There will be a new folder called <com.phonegap.plugins.PushPlugin> inside your project's plugin directory.  This folder contains: the source files for the plugin, an Example directory that has a sample web app you can use to receive ADM notifications on the client, and a Node.js script you can use to send notifications, simulating what a server would normally do.

Before you can start testing notifications, you will need to follow the steps below to download and install the ADM support libraries and add the proper credentials to your project.

Install Amazon Device Messaging Library

The push notifications plugin needs the support files found in the ADM API. Follow these steps to download the SDK and install the library files in your Cordova project:

  1. Download the Amazon Mobile App SDK and unzip.
  2. Create a folder called ext_libs in <project_path>/platforms/amazon-fireos folder.
  3. Copy amazon-device-messaging-x.x.x.jar into the <project_path>/platforms/amazon-fireos/ext_libs
  4. Create a new text file called ant.properties in the <project_path>/platforms/amazon-fireos folder, and add a java.compiler.classpath entry pointing at the ADM library. For example:
    java.compiler.classpath=./ext_libs/amazon-device-messaging-1.0.1.jar
    
  5. Create a new text file called api_key.txt in the <project_path>/platforms/amazon-fireos/assets folder, and copy the API Key you obtained in the Credentials section above. Note: Be careful not to add any extra spaces to the text file.

Create Sample App

Here's a basic web app that can be used to test receiving notifications on the client. Create new index.html and index.js files in your project's www directory and copy the markup/code below.

The index.html page is simply a container for the messages to be displayed on the device:

index.html:

<!DOCTYPE html>
<html>
<head>
	<title>ADM Test</title>
</head>
<body>
	<h1>ADM Test</h1>
	<pre id="container" style="word-wrap:break-word"></pre>
	<script src="cordova.js"></script>
	<script src="index.js"></script>
</body>
</html>

The index.js script uses the PushPlugin to display notifications received on the client.

index.js:

var container = document.getElementById("container");
var pushNotification;

document.addEventListener("deviceready", handleDeviceReady, true);

function handleDeviceReady(){

    pushNotification = window.plugins.pushNotification;
    pushNotification.register( handleSuccess, handleError, {"ecb":"handleNotification"});

}

function handleNotification(e) {

    log("**** EVENT RECEIVED: " + e.event );

    if(e.event == "registered"){
        if(e.regid.length > 0){
            log("REGISTRATION ID:  <br>" + e.regid);
            log("<a href=\"mailto:?subject=ADM%20Registration%20ID&body=" + encodeURIComponent(e.regid) + "\">Email Registration ID</a>");
        }
    } else if(e.event == "message"){
        if ( e.foreground ) {
            log("FOREGROUND NOTIFICATION");
        } else {
            if ( e.coldstart )  {
                log("COLDSTART NOTIFICATION");
            } else {
                log("BACKGROUND NOTIFICATION");
            }
        }
        log("MESSAGE: " + e.payload.message );
        log("TIME: " + e.payload.timeStamp );
        log("URL: " + e.payload.url );
    } else if(e.event == "error"){
        log("ERROR: " + e.msg );
    }
}

function handleSuccess(result) {
    log("Plugin Success: " + result );
}

function handleError(error) {
    log("Plugin Error: " + error );
}

function log(msg){
    container.innerHTML += msg + "<br>";
    console.log(msg);
}

Notes about the JavaScript:

  • The handleDeviceReady method will use the pushNotification object to register the client with the ADM Server. This requires that the api_key.txt file contains the correct information.
  • The Registration ID that is returned to the client is used by your server to send notifications to the correct device/app combination. The ID is very long, so there's a link to email the ID to yourself to be used in testing.
  • The notification event has a flag to tell your app whether it was in the foreground, background or not started when the notification arrived.
  • The ADM notification event contains a payload object consisting of arbitrary fields populated by your server. In this example, the server will send a message, timestamp and URL, but each app can be different.
  • If there are errors with authentication or other issues such as connectivity problems, the app should log to the screen any error messages sent to your app.

Once you have your client app ready, use Cordova's command line to install the app on your test device (usually using the "cordova run" command). If you have set up everything correctly, the app will start, authenticate with the ADM Server and display the Registration ID unique to your device/app to be used by your notifications server.

Test ADM Notifications

Normally, you will have a dedicated server which sends app notifications via the ADM Server, using the Registration ID to uniquely identify each instance of your app running on a specific device. Your app will be responsible for getting the Registration ID from each registered device to your server.

For testing purposes, we are instead going to use the pushADM.js Node script found in the <project_path>/plugins/com.phonegap.plugins.PushPlugin/Example/server folder. This script acts in place of your server, authenticating with Amazon and then pushing notifications to the ADM Servers to be delivered to your app. (Node is a prerequisite for Cordova, so you should already have it installed on your system.)

Before we can use the script, we need to add in your OAuth Credentials and Registration ID. Here is a snippet from the top part of the test script that you will be editing:

pushADM.js:

// Client ID and Client Secret received from ADM portal
var CLIENT_ID = ""; 
var CLIENT_SECRET = "";

// Registration ID, received on device after it registers with ADM server
var REGISTRATION_IDS = [""];

// Message payload to be sent to client
var payload = {
        data: {
            message: "PushPlugin works!!",
            url: "http://www.amazon.com",
            timeStamp: new Date().toISOString()
        },
        consolidationKey: "my app",
        expiresAfter: 3600
};
...

Edit the pushADM.js script :

  1. Copy the OAuth credentials - Client ID and Client Secret - you obtained in the Credentials section above into the CLIENT_ID and CLIENT_SECRET variables.
  2. Copy one or more Registration IDs from a registered app into the REGISTRATION_IDS array. Note: You can fill this var with IDs from multiple devices for testing. Using the sample app above, you can email the Registration ID to yourself and then copy/paste it into the test script.
  3. Optionally edit the payload object with custom values to be sent to your app.

Run the pushADM.js script from a command line using Node:

node pushADM.js

The result on the command line should be a series of log messages which shows the script authenticating with the ADM Server and then sending a message. The app running on your Kindle Fire should then instantly show the message if it is in the foreground, or add a notification alert to the top bar if it is in the background or not running. You can run this script as many times as you need to test your app.

Here is what the of the sample web app above looks like after receiving a notification:

Macintosh HD:Users:beattier:Desktop:cordova_adm:Screenshot_2014-05-28-14-17-15.png

Summary

The steps above should give you a good start towards implementing ADM push notifications into your app. Once you have your app working, it will be very easy to customize the notification payload as you need to create compelling new apps and services for your customers.

If you have trouble, please make sure to check out the links below for information and support:

Good luck!

-Russ

 

 

May 12, 2014

Russell Beattie

For the past several years, HTML5 web app developers have been using the Apache Cordova project to create an incredible array of apps targeting native platforms. Many, such as the Wikipedia Foundation's mobile app, have had tens of millions of downloads across platforms. Cordova is the fundamental tool which enables developers to use open web technologies - HTML5, CSS3 and JavaScript - to create both fundamental and innovative mobile apps. This is why Amazon supports the Cordova project and enables developers to target the Amazon Fire OS platform. We want to enable web developers to take advantage of the advanced capabilities of the Chromium-based Amazon WebView integrated into Kindle Fire tablets running Fire OS.

We've covered the basics of setting up your Amazon Fire OS development environment for Cordova, so for this post, I'm going to create a new web app from scratch, covering all the varied little details you'll need to know to get your app from idea to Appstore. To do this, I created a simple drawing app that shows off both mobile and platform specific capabilities, and used Cordova to wrap it up into a native app ready for publication.

For the sake of simplicity, I developed an app targeting just Amazon devices. Though much of the power of Cordova is the fact that it allows developers to create cross platform apps, it can also be used as a way of quickly creating apps targeted at a single specific platform. This lets me use some more advanced HTML5 capabilities available on the Fire OS platform - in this case the accelerated 2D drawing canvas - and it also lets me cut some corners and use my rusty Java skills to add a few lines of native code to enable some needed functionality. In the future, I'll take this bit of native code and turn it into a plugin that can be used to target a wider range of devices.

I called the app Simple Draw, and it's available on the Amazon Appstore right now, downloadable for free. If you have a Kindle Fire device, go grab the app and try it out. It's a very simple drawing app targeted at young children - you can draw with your finger in various neon colors, clear the screen, or save the drawing as an image, which you can view in your Photos and share with others.

Here's everything you'd need to do to create this app yourself.

Setup

First, we'll start with the basics - setting up a new Cordova project and adding in support for Fire OS: 

$ cordova create simpledraw com.example.html5.simpledraw SimpleDraw
$ cd simpledraw
$ cordova platform add amazon-fireos

This will create our new Cordova project directory, pre-fill it with the skeleton folders, and then add in the appropriate Fire OS support files (amazon-fireos). (The first time you run through this process, you'll be prompted to download the AWV SDK found here) Make sure you change the package name to be unique to you or your company, rather than com.example.html5 in the above example.

The resulting directory should look like the following - I expanded a couple important folders to show where we're going to be tinkering.

Web Assets

Now that we've got somewhere to put our app's files, we can go in and clear out the placeholder files and folders that are in the project’s www folder. This is where your app's web files go. The Simple Draw web app above only needs an index.html and a main.js JavaScript file, but other apps could have various text files, images, sound files, etc.

Let's take a quick look at both files.

HTML

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Draw</title>
        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
        <style>
            html, body { height: 100%; margin: 0; padding: 0; background: #fff; color: #fff}
        </style>
    </head>
    <body>
        <script src="main.js"></script>
    </body>
</html>

You'll notice there's not much there. It's really just a placeholder for a full-screen app that uses the HTML5 Canvas, so it doesn't need to be very complicated. It's important to set the viewport meta tag correctly, so my app knows that it's meant to be used on a handheld device, and to then call the JavaScript file at the end to make sure the DOM is loaded by the time I want to start manipulating it.

JavaScript

Next is the JavaScript, which is a bit longer, but is relatively straight forward and doesn't rely on any external libraries. The script creates a new full-screen canvas element, then listens for touch events that are used to draw on and to select from the menu bar at the bottom. There is a colorful gradient to choose your color and a couple icons at the bottom left which are used to clear the screen or save the image to your device's Pictures directory. Everything except that last bit can be done using pure JavaScript.

var canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
var context = canvas.getContext("2d");
context.fillStyle = "#000";
context.lineCap = 'round'

document.body.appendChild(canvas);

var pictureCanvas = document.createElement("canvas");
var pictureContext = pictureCanvas.getContext("2d");

var width;
var height;

var menuSize = 40;
var lineColor = '#fff';
var lineWidth = 6;
var isDown = false;
var points = [];

// listen for events
window.addEventListener("resize", reset, false);

canvas.addEventListener("touchstart", pointerDown, false);
canvas.addEventListener("touchmove", pointerMove, false);
canvas.addEventListener("touchend", pointerUp, false);

document.body.addEventListener("touchcancel", pointerUp, false);

//set up and begin
reset();
requestAnimationFrame(animate);

// functions
 function reset(e){
    width  =  window.innerWidth;
    height = window.innerHeight;
    canvas.width = width;
    canvas.height = height;
    pictureCanvas.width = width;
    pictureCanvas.height = height - menuSize;
    context.fillRect(0,0, width, height);
    drawMenuBar();
    lineColor = "#fff";
    points = [];
 }

function drawMenuBar(){
    // color gradient
    var grad = context.createLinearGradient(menuSize * 2, height - menuSize, width, height);
    grad.addColorStop(0, 'black');
    grad.addColorStop(1 / 8, 'red');
    grad.addColorStop(2 / 8, 'orange');
    grad.addColorStop(3 / 8, 'yellow');
    grad.addColorStop(4 / 8, 'green')
    grad.addColorStop(5 / 8, 'aqua');
    grad.addColorStop(6 / 8, 'blue');
    grad.addColorStop(7 / 8, 'purple');
    grad.addColorStop(1, 'white');
    context.fillStyle = grad;
    context.fillRect(menuSize * 2, height - menuSize, width, height);

    // icons
    var refreshIcon = new Image();
    refreshIcon.onload = function(){
        context.drawImage(refreshIcon, -2 , height - menuSize);
    }

 

    refreshIcon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAACg0lEQVRYCe2XMa8pQRTHx/MSjRBRENEpCBEJEoUQvgO1UqGlFirxaURoVSqh0IioFKIRiYRCKObt2XdnMruse8bam1vsSTZzdpzzPz9nszOzDkIIVa5fa39+LdkXmA1o9gnZHbQ7aLYDZvP/mhWA/FQqReLxOAmFQqrcfr8nq9WKLJfLT8irCzUs1lKXx+Oh3W6XbrdbamTwW6fToRArqy/Ey4FBYrVapYfDwYjrYR5iIUcoKuPLAbbb7QcA7ATkvgGJB2w0Gg8s8BhbrRZNJpPU7XarF/gw9+zxg4YkJA4wk8nQ+/2uAez3+9TlchkWhN8gRjTQAC0JSBzgZDIR69Bms4kuArGigdZHAXO5nKhPB4OBTAE1FnJEA00k5Pcd7PV6ojaNRqNYcR4Xi8U0GqD5McDZbMbFF4sFVvghDnKZgSYGEHWaCYfDitZ/m8/nzJUexVxR85UQCtDv93ON4/HIfVlHzBU1X+mgAN8RflZUhBI1n8WyORTgbrdj8SSbzXJf1hFzRc1XOihAZd3iGul0mihvMb/HOspbTCCXmajJ5ozGb98m2XXQ6XTSWq2m0bV0HVT+GdXvJJFIRAMAMeyCNe50OvFt0PKdBAqLe/F4POYwDIqNlUqFLXUU/B/biwEATiLX65UadS+RSNDz+cwBL5cL95lj2WmGdahUKj3tntfrpZvNhnGoo/70Y/l5kEHqR4fDQUejkQZOvPnRE7UeDu7r9brIo/HX67WpbxLHV0FlMGfBYJAUi0VSKBRIuVxWv/KUzpLb7UYCgQBR3uq3CnwMUF/d5/ORfD6vQg+HQzKdTvUhqHvLAFHVEUGorQ6hY1mIDWi2tXYH7Q6a7YDZ/H90B3qb+wyF3wAAAABJRU5ErkJggg==";

    var downloadIcon = new Image();
    downloadIcon.onload = function(){
        context.drawImage(downloadIcon, menuSize, height - menuSize);
    }
    downloadIcon.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAABCklEQVRYCe2X0QmDUAxFtegA4hCKfy4huIvgCG4hiLMIziHuoP4LqQFfwVLa2puWR0ngka/7OJ4EVNdxHNqOtXWxlmwHU0B0QmpQDaIG0LzIDlZV5RDR4aBgJi8CaC77RldA1KoaVIOoATSvO6gGUQNoXndQDaIG0Lz1O8gPyD/up06SJLQsy/aN+l61bXvq/juec3AmnOc5rev6krDve/J9//eADFqW5VPAcRwpDEMEjrOfGTS5pmkeQs7zTHEco3A4oOd51HXdAZJHn2WZBBwOyCaDIKBhGG6QRVFIwckAMmQURTRNE9V1LQknB8iQaZoSj9zsp0R390u2ZmdZ/yZRQHRx1ODfG7wCaGXbMjKT0dAAAAAASUVORK5CYII=";

    //icons border
    context.strokeStyle = "#fff";
    context.lineWidth = 2;
    context.beginPath();
    context.moveTo(menuSize, height-menuSize);
    context.lineTo(menuSize, height);
    context.moveTo(menuSize * 2, height-menuSize);
    context.lineTo(menuSize * 2, height);
    context.stroke();
}

function saveImage(){
        if(confirm('Save Image?')){
            pictureContext.drawImage(canvas, 0, 0);
            var dataUrl = pictureCanvas.toDataURL("image/png");
            if(typeof SimpleDraw !== 'undefined'){
                SimpleDraw.savePNG(dataUrl);
            }
        }
}

function eraseImage(){
    if(confirm('Erase Image?')){
        reset();
    }
}

function drawLine(){
    if(points.length > 1){
        context.strokeStyle = lineColor;
        context.lineWidth = lineWidth;
        context.lineCap = 'round'
        context.lineJoin = 'round';
        context.shadowBlur = lineWidth/2;
        context.shadowColor = lineColor
        context.beginPath();
        context.moveTo(points[0].x, points[0].y);
        for (var i = 1; i < points.length; i++) {
            context.lineTo(points[i].x, points[i].y);
        }
        context.stroke();
    }
}

// events
function animate(time) {
    drawLine();
    requestAnimationFrame(animate);
}

function pointerUp() {
    isDown = false;
    points = [];
}

function pointerDown(e) {
    e.preventDefault();

    var point = {};
    point.x = e.targetTouches[0].pageX;
    point.y = e.targetTouches[0].pageY;

    if(point.y > height - menuSize){
        if(point.x < menuSize){
            eraseImage();
            return;
        }
        if(point.x > menuSize && point.x < menuSize * 2){
            saveImage();
            return;
        }
        if(point.x > menuSize * 2){
            var data = context.getImageData(point.x, point.y, 1, 1).data;
            lineColor = 'rgb(' + data[0] + ',' + data[1] + ',' + data[2] + ')';
        }
    }  else {
        isDown = true;
        points.push(point);
    }

}

function pointerMove(e) {
    e.preventDefault();

    var point = {};
    point.x = e.targetTouches[0].pageX;
    point.y = e.targetTouches[0].pageY;

    if(isDown && point.y < height - menuSize){
        points.push(point);
    }
}

A few notes about the Javascript:

  • For the menu, I used an image program to create some basic icons, which I converted into data:// URLs. This makes the script a bit more contained. Images still take time to load even from a data URL, so I added onload listeners to draw the icons to the canvas.
  • Rather than draw to the canvas every time the event fired, I call the drawLine() function using requestAnimationFrame. This makes sure the screen is ready to be redrawn, and makes the app a bit more performant.
  • Since modern desktop browsers enable touch-event emulation, I didn't bother adding in mouse events as I'm targeting only mobile devices.
  • The saveImage() function has a reference to a SimpleDraw object. This doesn't exist in the web engine until we add it using the Java wrapper (below).

So we're all set with the main functionality of the app, and can quickly test the script by building/running the application on the device.

Please note: Cordova apps for Fire OS don't currently support emulation because of the integrated nature of Amazon WebView.

Plug your Kindle Fire into your computer via a USB cable and perform:

$ cordova run

On first run, this will compile the application using the Android Developer Toolkit, then push the app's debug version to your Kindle Fire so you can test.

Hey, it works! Well, almost - it doesn't actually save the images, so we'll have to wire that up next.

Native

When I first started testing out the functionality of the app, I assumed I'd be able to use the Cordova FileSystem API plugin to save the resulting image to the local file system. So I added the plugin via the command line (see the Cordova docs on how to do this), and used it to save the canvas image data as files. But I couldn't see them from the Photo gallery! That's not useful for this particular application - so I uninstalled that plugin, and decided to add that bit of native functionality myself.

The problem is that Fire OS (and other Android systems) keeps a separate database of images that needs to be updated in order for the images to appear in the photo browser. So in addition to saving the file, I needed to update the system to recognize the fact that the file is an image and to update the database. To do this, I needed to add a few lines of Java code to add in a custom JavaScript Interface in Cordova's native app wrapper code, found deep within the platforms folder here:

/simpledraw/platforms/amazon-fireos/src/com/example/html5/simpledraw/SimpleDraw.java

If you've done any sort of Java development, you'll recognize what that long path is - it's the standard Java directory system based on the name of the package. I had to do some digging on the web to find the right APIs to wire together, but in the end it was only a few lines of Java. I would have preferred to stay within JavaScript, but having the option to dive into native code if I needed to was very useful.

Here's the resulting SimpleDraw.java CordovaActivity class:

package com.example.html5.simpledraw;

import org.apache.cordova.*;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.provider.MediaStore;
import android.util.Base64;
import android.view.Gravity;
import android.webkit.JavascriptInterface;
import android.widget.Toast;

public class SimpleDraw extends CordovaActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        super.init();

        //super.getFactory().enableDeveloperToolsUnix(this.getPackageName() + ".devtools");

        this.appView.addJavascriptInterface(new JSObject(), "SimpleDraw");

        super.loadUrl(Config.getStartUrl());

    }

    public class JSObject {

        @JavascriptInterface
        public void savePNG(final String dataUrl) {

            runOnUiThread(new Runnable() {
                @Override
                public void run() {

                    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
                    String fileName = "drawing-" + df.format(new Date()) + ".png";

                    byte bytes[] = Base64.decode(dataUrl.substring(dataUrl.indexOf(",") + 1), Base64.DEFAULT);

                    Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

                    MediaStore.Images.Media.insertImage(getContentResolver(), bmp, fileName, fileName);

                    Toast toast = Toast.makeText(getApplicationContext(), "Image saved!", Toast.LENGTH_SHORT);
                    toast.setGravity(Gravity.CENTER, 0, 0);
                    toast.show();


                }
            });

        }
    }

}

Notes about the native code:

  • The native functionality is exposed to the web engine using the addJavascriptInterface() method, which takes a parameter defining the name of the JavaScript object you want to use - in this case I named it the same as the project, but it could be anything.
  • The commented out line in the onCreate() method can be used to enable access to Chromium's remote Developer Tools. You can then connect to your device remotely using this command, then browsing to localhost:9222 in your browser: 
adb forward tcp:9222 localabstract:com.example.html5.simpledraw.devtools
  • From JavaScript, the canvas is converted into an image/png data URL using the toDataURL() method. This is what’s passed to the Java class via the SimpleDraw.savePNG() method. It's then converted into an array of bytes to be saved as an image file.
  • Rather than using the basic File API, I used the built-in MediaStore class to save the converted PNG image to the file system, which automatically updates the device's internal data table so it appears instantly in the Photo gallery.

AndroidManifest.xml

In order for the app to be able to save files to the device's local storage, it needs to have the right permissions. This is done by editing the AndroidManifest.xml file found in the /platforms/amazon-fireos directory.

In the example below, you can see that the WRITE_EXTERNAL_STORAGE permission has been added to the list of user-permissions, allowing the app to save images to the file system. Additionally, the android:screenOrientation flag has been set in the application element to lock the screen to landscape mode only.

The resulting file looks like this: 

<?xml version='1.0' encoding='utf-8'?>
<manifest android:hardwareAccelerated="true" android:versionCode="1" android:versionName="0.0.1" android:windowSoftInputMode="adjustPan" package="com.lab126.html5.simpledraw" xmlns:android="http://schemas.android.com/apk/res/android">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <application android:hardwareAccelerated="true" android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/app_name" android:name="SimpleDraw" android:screenOrientation="landscape" android:theme="@android:style/Theme.Black.NoTitleBar">

... more xml configuration here ...

</manifest>

Now when you test out the app and click the download arrow, the image will be saved and you'll get a native Toast message pop up confirming the process. 

If you check in the Photos app, you'll be able to see your image. Hooray! So the app now works, but we're still a few steps away from publishing it to the Amazon Appstore.

Config.xml

The Cordova config.xml settings file can be found in your main project folder. Enter in your contact details, the name and description of your app, and then add in Fullscreen and BackgroundColor preference tags (see below) which will make your app fill the entire screen, and prevent any sort of color flash as it loads.

Here's what the config.xml file looks like: 

<?xml version='1.0' encoding='utf-8'?>
<widget id="com.example.html5.simpledraw" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>SimpleDraw</name>
    <description>
        A sample drawing app using Cordova for Amazon Fire OS
    </description>
    <author email="beattier@amazon.com" href="http://www.amazon.com">
        Russell Beattie, HTML5 Platform Technical Evangelist
    </author>
    <content src="index.html" />
    <access origin="*" />
    <preference name="Fullscreen" value="true" />
    <preference name="BackgroundColor" value="0xff000000"/>
</widget>

Icons

Next we'll need to replace the different sized app icons that our project used before we are ready to publish. These images are found in the /platforms/amazon-fireos/res directory, and will need to be created and copied over manually. I used a drawing program to whip up a quick app icon and then exported it as a giant 512px square image (you'll use this later when submitting your app to the Appstore). I then shrunk the images as needed and saved them as icon.png files in the following folders:

/res/drawable = 96px

/res/drawable-hdpi = 72px

/res/drawable-ldpi = 36px

/res/drawable-mdpi = 48px

/res/drawable-xhdpi = 96px

Now when you build and run your project, the generic Cordova icon will be replaced by your app's custom icon.

Signing

We're almost there! One last step before we're ready to publish the app is to create a signed -release version of the app's .apk file. Up until now, the Cordova build process has created -debug versions of the app for testing, but now we'll set up the files needed to sign the apk when the --release flag is passed to the build command.

First, you'll need to either create or locate your Java keystore. If you haven't created one yet, definitely check out the Android documentation which summarizes the keystore process quite well. However the following command will get you started:

 

$ keytool -genkey -v -keystore my-release-key.keystore -alias myapps -keyalg RSA -keysize 2048 -validity 10000

This will prompt you for all the information you need to enter and create a new keystore file for you to use to self-sign your app. Put the keystore somewhere safe and accessible from your Cordova project, and don't forget the password!

Next, you need to create a new text file called ant.properties in the base of the amazon-fireos directory: /platforms/amazon-fireos/ant.properties. In it, add two lines pointing at the appropriate directory: 

key.store=/Users/example/keystore/my-release-key.keystore
key.alias=myapps

Now you can create a release version of your app to submit to the Amazon Appstore by just using the Cordova command line tool. Call the build command and add a --release flag like this:

$ cordova build --release

This time it will go through the normal build cycle, and then stop to ask you for your password to your keystore and alias. Once it is complete, there will be a brand new signed apk ending in -release.apk saved in the platform's ant-build directory. For example /platforms/amazon-fireos/ant-build/SimpleDraw-release.apk .

Submitting your app

Now that you have your apk ready to go, you can submit it to the Amazon Appstore! You'll need to gather some screenshots, that big icon image from earlier, and fill out some details describing your app on the site, but within a short time the submission process will be complete.

To start the process, head to the Amazon Developer Portal and click on the "Submit Android App" button (you'll need an Amazon developer account of course – that’s free).  That will guide you through the process - for more information about the type of information you need to answer, definitely check out our helpful submission documentation as well.

Good luck!

Hopefully this will save you some time when developing your own Cordova app for Amazon Fire OS. Though it may seem like a lot of steps the first time through, it's really only a few extra files that need to be created or changed, and the end result is a fun new app created using familiar, yet powerful, web technologies. Good luck with your app - and definitely contact us if you have any questions or concerns!

 

January 30, 2014

Russell Beattie

Creating an application using Amazon's Mobile App Distribution Program is a great way for developers who have hosted web apps to create downloadable apps available on the Amazon Appstore. Web app developers benefit from the increased discoverability that the store provides when users are searching for new apps, as well as being reminded to use the web app by the icon on their device's home screen afterwards. In addition, all of a user’s linked devices that use the Amazon Appstore will have the icon waiting in their Cloud list of apps as well.

And because web apps are hosted online, developers have increased flexibility to re-use existing assets and make changes or fixes to the app quickly and easily, without having to re-create a new app that has to be re-installed by the end user. But what happens if the user wants to use the app offline? Obviously, if the app relies on live server-side content - such as streaming videos or airline reservations - then obviously it's not going to work. But if the web app is self-contained and doesn't need to talk to a server for its core functionality - like most games - then being able to take the app offline is something that should be an option for users.

Happily, enabling your web app to be used offline can be done using HTML5's built-in Application Cache with only a few small changes to your code. Below I'll outline the steps you need to take to create a basic offline web app. It's surprisingly easy to set up, but beware! Application Cache has a well deserved reputation for being difficult for developers to get a handle on.

Offline Web App Walkthrough

1. Create Manifest File

The first thing you need to do is create an HTML5 manifest text file with a list of every asset file your web app requires - HTML, JavaScript, CSS, images, icons, fonts, etc. The manifest file can have any name you choose, but we'll use app.appcache to be clear and to avoid overlap with other types of manifest files.

Here's the content of a basic manifest file:

CACHE MANIFEST 
# Version 1.0 
CACHE: 
main.js 
main.css 
logo.png 
NETWORK: 
*

- The first line needs to be CACHE MANIFEST.

- The second line in this example is just a comment, but is useful to make changes to your web app by simply incrementing the version number. Note: Only changes to the manifest file will invalidate the cache.

- The third line begins the CACHE: section where you list out the asset files used by your web app, either relative to the location of the manifest file, an absolute path or complete URL. Note: DO NOT list app.appcache in your manifest.

- The NETWORK: section has a wildcard which permits the browser to download files that are not listed in the CACHE: section. Note: Without the NETWORK: section, the browser will ONLY re-request files listed in the CACHE: section after the initial page load.

2. Confirm Server Settings

You need to also make sure your web server serves the correct MIME type for the manifest file. For Apache, it looks like this:

AddType text/cache-manifest .appcache

You also need to makes sure the manifest file is not being cached on the server. If the HTTP Cache-Control header for the manifest file doesn't update, or a 304 Not Modified is return, then the web engine won't be able to see if the manifest file has been changed or not, which is the only way to invalidate the offline cache.

3. Add Manifest Attribute

You then need to add an attribute to the tag of every HTML page you serve pointing at the manifest file, like this:

<html manifest="app.appcache">

4. Add Update Script

Finally, you need to make sure your app updates itself if the manifest changes - the easiest way to do this is to add this bit of JavaScript to your main HTML:

<script>
window.applicationCache.addEventListener('updateready', function(e){
    window.location.reload();
});
</script>

5. Test

Your web app should now be offline enabled! If you have Python installed, you can test this by setting up a local server to see what's happening both on the server and in the browser.

beattier@amazon.com:~/html5demos/offline$ python -m SimpleHTTPServer

Serving HTTP on 0.0.0.0 port 8000 ...

1.0.0.127 - - [21/Jan/2014 13:42:52] "GET / HTTP/1.1" 200 -
1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /app.appcache HTTP/1.1" 200 -
1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.css HTTP/1.1" 200 -
1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /main.js HTTP/1.1" 200 -
1.0.0.127 - - [21/Jan/2014 13:42:52] "GET /logo.png HTTP/1.1" 200 -

... 

If you request the page again, you'll see that *only* the manifest is requested.

1.0.0.127 - - [21/Jan/2014 13:43:12] "GET /app.appcache HTTP/1.1" 304 -

By modifying the manifest file and reloading, you'll see that all the files listed will be re-downloaded again.

You can also connect the Amazon Web App Tester to see the client side of the process as well by using Remote Debugging. (See our previous overview of setting up the Tester here.) In the screenshot above, I've connected to a Kindle Fire HDX and loaded a demo offline web app stored on Github. By looking at the Resources tab and drilling down into the Application Cache folder, I can see the assets that are cached locally, and a log of the Application Cache events.

Application Cache Gotchas

This is just a basic way to setup an offline web app. There are more options that you can add to your manifest file, more events you can track in JavaScript and more functionality you can use to make your web app's offline experience much more seamless to the end user. Check out the links below for more information.

Conceptually, it's important to understand that once you've enabled a manifest, your web app is now offline first and forever. Let's repeat that for clarity: OFFLINE FIRST AND FOREVER.

OFFLINE FIRST means:

- Your web app's files will then always be loaded from the offline cache first, and then a request will be made to the server for the manifest file to see if there have been any updates.

- The browser will not automatically refresh if the manifest has changed. It will in fact download the files from the server, but it will wait until the next time the page is requested to use them. This is why the script in step 4 above to detect a manifest change and immediately refresh the page is important.

FOREVER means:

- The only thing that can invalidate the offline cache and trigger a re-download of files is a change in the contents of the manifest file - not just the timestamp.

- There is no programmatic way to invalidate the offline cache from the browser. Even changing or deleting the manifest attribute in the tag will not invalidate the cache.

- Until the browser requests a manifest and receives a 404 or 410 from the server, it will continue to consider the web app as being offline and use the last downloaded version, rather than updating from the server.

Summary and External Resources

The info above should be able to get you started with offline web apps. Once you've added in the manifest, your web app will be available offline the next time your users load up your app. Fair warning: This can be a tricky feature to implement - especially if you misconfigure or forget some of the steps above. Getting the browser to let go of the manifest and refresh your code can be incredibly frustrating. I think the upside is worth it though, as enabling your web app to be used anywhere a native app can be used is incredibly satisfying.

To get more information about Application Cache, I encourage you to check out these great articles which dive into the topic in even more detail.

·         http://diveintohtml5.info/offline.html

·         http://www.html5rocks.com/en/tutorials/appcache/beginner/

·         http://www.html5rocks.com/en/mobile/workingoffthegrid/

·         http://alistapart.com/article/application-cache-is-a-douchebag

In future posts, I'll expand on offline apps by looking at topics such as offline data storage and efficient caching strategies.

-Russ (@RussB)

 

January 21, 2014

Russell Beattie

The Amazon Mobile App Distribution Program enables developers to create Kindle Fire apps using existing HTML5 mobile web apps. It’s also a good way for web developers to start creating mobile apps using the skills and knowledge they already have.

Back in December we covered how to get your existing web apps onto actual devices with a Webinar on Submitting HTML5 Web Apps to the Amazon Appstore, and companion blog post focusing on setting up the Amazon Web App Tester to debug and test your apps.

We wanted to follow up with some more details to help you get the most out of the tester, which is a key part of the HTML5 web app creation process on Kindle Fire tablets. The Web App Tester has a variety of powerful features which can be used to make development faster and easier. Below are a few ways to best take advantage of all that great functionality. 

Amazon's Web App Tester

What exactly is Amazon's Web App Tester? It's a downloadable app which lets you test your web app in a production-like environment on your Kindle Fire or Android device, without first submitting it to our store. The tester contains the same web engine and libraries that will run your web app when it is wrapped into a downloadable app. The tester however, has an interface that lets you enter in your own custom URLs, and most importantly, enables remote debugging for development and testing using your desktop computer.

Because the Web App Tester is based on the same technologies as the final wrapped app, you should be able to better assess your app's performance and functionality - and more quickly work through any problems you might encounter - on the device itself, rather than via an emulator or simulator, which may not be as accurate.

Additionally, libraries that are pre-loaded into the final downloadable app, such as the In App Purchasing API for JavaScript, are also built into the Tester so you can debug your IAPs before you launch. (Look for a more detailed post about enabling IAP for your web apps coming soon).   

Managing Multiple URLs

The first thing you'll encounter when using the Web App Tester is a place to enter a URL for testing. This is great for easy ad-hoc testing, but if you have complicated URLs to enter, or have multiple URLs that you need to manage, you can create a JSON file of URLs and put it into the root folder of your Kindle Fire. The file has to be named amazon.testerurls.json and placed in the /mnt/sdcard/ folder on your device. Here's how the list of URLs should be formatted:

{ 
   "urls":[
      "http://m.imdb.com",         
      "http://m.amazon.com",         
      "https://read.amazon.com"]     
}
 

The easiest way to get it to the correct spot on your device is via the command line using the Android Debug Bridge (ADB), which comes as part of the Android SDK. Assuming you've set up the SDK correctly, you only need to connect your device using USB, open a command line, change to the directory where your JSON file is located and run this command:

$ adb push amazon.testerurls.json /mnt/sdcard/ 

Remote Debugging Options

If your device and your computer share the same network, you can enable Web Developer Tools for debugging over WiFi. If you're on a corporate network, or want to test aspects of your app including offline functionality or WLAN speeds, you can connect via USB using ADB.

Helpfully, the Web App Tester gives you all the details you need to enable Remote Debugging once you start the app. Simply click the full screen handle at the bottom or side of the screen, and swipe down. There you'll see options to enable remote debugging using ADB or via your network.

 

Once you choose, a dialog box pops up with instructions and the exact URL to enter into your Chromium based browser (for debugging). For example, here are the instructions for remote debugging via WiFi:

 

Close that dialog, enter the URLof your web app to test and you'll be all set to debug via a desktop computer.

Dev Tools Tips

Once the Dev Tools page is open in your desktop browser, remote testing and debugging of your mobile web app should become as familiar as doing development for desktop browsers. Here are some things to watch for.

On Device Tools. First, note that all the normal functionality you'd find in Dev Tools is live, but running on the device itself. In fact the entire UI is a static HTML app served from the device, which then communicates back via Web Sockets. Viewing the network speed, recording the timeline activity or profiling is all happening with the device's hardware.

Reload Shortcut. When doing development for a desktop browser, you may be in the habit of clicking the reload icon in the browser to refresh the page. Rather than exiting out of your app on the device, and then re-starting, you can simply type Command-R/Control-R inside the remote Dev Tools window to refresh the contents of the page on the device itself.

Live Inspection. Just like on a desktop browser, you can use the inspect icon to help pinpoint elements on the screen within the HTML5 markup of your app - rather than clicking the screen, just activate the inspect icon, and then touch the screen to find that element within the Dev Tools. It goes in reverse as well, notice that as you use your mouse to hover over the markup in Dev Tools, the corresponding elements light up on the device.

FPS Meter. In the Dev Tools, you can use the settings icon to turn on the FPS meter, which displays in the top corner of your device. This will let you get a live view of how fast your app is refreshing, without having to add in additional libraries.

On Device Debugging and Console. You can step through JavaScript code just as you would normally. Additionally, the console is also live, with the JavaScript engine running on the device. This allows you to use console tricks such as $0 to refer to the selected element, navigate using document.location.href, or even pop up an alert() window if needed.

Remote Debugging API

Because the Web App Tester's Dev Tools use the same remote debugging protocol as desktop Chromium browsers, they can be accessed not only from Dev Tools, but from text editors, IDEs or via scripting languages such as Python. Here's an example using the chrome-remote-interface Node.js module.

First, install the library using NPM:

npm install chrome-remote-interface 

Then create a test.js file with this boilerplate example (modifying the options variable as needed):

var Chrome = require('chrome-remote-interface');

var options = {

host:  'localhost',

port:  '9222'

};

Chrome(options, function (chrome) {

    with (chrome) {

        on('Network.responseReceived', function (message) {

            console.log(message);

        });

        on('Page.loadEventFired', function (message) {

            console.log("----------------------- page loaded ");

            console.log(message);

        });

        Network.enable();

        Page.enable();

        Page.reload();

    }

}).on('error', function (err) {

    console.log(err);

    console.error('Cannot connect to Chrome');

)};

Then run the script

node test.js 

As you use your app on the device, you'll see events logged in your console as they fire. The protocol and module will also let you send commands to the remote browser, letting you automate testing on the device and recording the results. For more info, check out the Remote Debugging Protocol pages here.

Hopefully some of these tips will come in handy as you're doing development for your web app, if you have any tips of your own or questions about using Dev Tools, definitely get in touch!

-Russ (@RussB)

 

Want the latest?

appstore topics

Recent Posts

Archive