Skip to main content

"Thou shalt not spam" or what are web notifications

Web notifications are one of the newer features added into modern browsers. They are intended as alerts for the user outside of the web page context. The idea is for them to be browsers and system independent, for example, when using a mobile browser notification could go into the home screen of the device. On the desktop usually they show up on the right-corner of the screen, at least on most desktop environments. They can be very annoying thing and you should think if thoroughly before you decide this is the right way to inform the users. There are some restrictions that avoid the creation of popup but I still have a bad feeling whenever I see one of these. One example of these notifications are the gmail/gtalk updates that show up on new messages (they are useful and annoying at the same time). This is done via the Notification API and we will do a simple example of its usage.

The implementation

First we can start with a simple wrapper for the Notification API that will have a fallback for the WebKit temporary implementation. We will define a simpleNotification object that will be the wrapper and it will contain a function called show that accepts data parameter. This data parameter is also a wrapper object that contains all the required info for a simple notification. For example :

var data = {
    icon: "http://upload.wikimedia.org/wikipedia/commons/7/7e/Jacob_de_Gheyn_-_Wapenhandelinge_4.jpg",
    title: "The Art of War - The Use of Spies ",
    body: "text",
    timeout : 7000,
    errorCallback: function(){
      $("#fallback").text(this.body);
    }
  };

Where the errorCallback is executed if non of our implementations are supported by the browser, as for the others, they should be self descriptive.

var simpleNotification = (function () {
   var my = {};
    my.show = function (data) {
     if (window.webkitNotifications) {
       //check if there is a support for webkitNotifications
       if (window.webkitNotifications.checkPermission() == 0) {
         var notification = webkitNotifications.createNotification(data.icon, data.title, data.body);
         notification.show();
         //set timeout to hide it
         setTimeout(function(){
             notification.cancel();
          }, data.timeout);
       } else {
         webkitNotifications.requestPermission(function () {
           //call the same function again
           my.show(data);
         });
       }
     }else if (window.Notification) {
   //Currently a fallback, but this should be the real implementation on all browsers
       if ("granted" === Notification.permissionLevel()) {
         var notification = new Notification(data.title, data);
         notification.show();
       } else if ("default" === Notification.permissionLevel() ) {
         Notification.requestPermission(function () {
           //call the same function again
           my.show(data);
         });
       }
     }else{
       //Notifications not supported,going with fallback
      data.errorCallback();
     }
   };
 return my;
}());

The standard based implementation is the one that uses window.Notification. An interface defined by W3C

  [Constructor(DOMString title, optional NotificationOptions options)]
  interface Notification : EventTarget {
    static readonly attribute NotificationPermission permission;
    static void requestPermission(optional NotificationPermissionCallback callback);

    attribute EventHandler onclick;
    attribute EventHandler onshow;
    attribute EventHandler onerror;
    attribute EventHandler onclose;

    readonly attribute NotificationDirection dir;
    readonly attribute DOMString lang;
    readonly attribute DOMString body;
    readonly attribute DOMString tag;
    readonly attribute DOMString icon;

    void close();
  };

Since our data object already contains similar values it can be passed directly in the instantiation of the Notification object. For example :

var notification = new Notification(data.title, data);

On the other hand window.webkitNotifications is the non-standard Chrome implementation. For a more detailed usage of the webkit version please see decadecity blogpost.

In order for us to be able to trigger the notification it is important in the html snippet to have some element that can be user triggered. We have some simple html containing a button

 <body>
   <button id="show">Show quote</button>
   <div id="fallback"> my fallback notification</div>
 </body>

This is needed in order to have request for the permission and to avoid “spam” notification. In our case this is the onClick function on the button. More specifically the jQuery click event. The call now is very simple, we just create the data object using some defaults and load some random data.

$(document).ready(function() {
    $("#show").click(function () {
      var data = {
        icon: "http://upload.wikimedia.org/wikipedia/commons/7/7e/Jacob_de_Gheyn_-_Wapenhandelinge_4.jpg",
        title: "The Art of War - The Use of Spies ",
        body: "text",
        timeout : 7000,
        errorCallback: function() {
          $("#fallback").text(this.body);
        }
      };
      var randomSampleId = Math.floor(Math.random()*sampleData.quotes.length)+1;
      var sample = sampleData.quotes[randomSampleId];
      for (var key in sample) {
        data.title += key;
        data.body = sample[key];
       }

      simpleNotification.show(data);
    });
});

The sample data used is a JSON containing quotes from “Sun Tzu -The Art of War” which is also available for for free on project gutenberg (win for public domain). The data is structured in the following format :

var sampleData = {
    quotes: [
     {
       "1": "Sun Tzu said: Raising a host of a hundred thousand men and marching them great distances entails heavy loss on the people and a drain on the resources of the State.The daily expenditure will amount to a thousand ounce of silver. There will be commotion at home and abroad, and men will drop down exhausted on the highways, As many as seven hundred thousand lies will be impededin their labor."
     },
     {
       "2": "Hostile armies may face each other for years, striving for the victory which is decided in a single day.This being so, to remain in ignorance of the enemy's condition simply because one grudges the outlay of a hundred ounces of silver in honors and emoluments, is the height of inhumanity."
     }
    ]
 };

One more thing to note is that Web Notifications were not supported by IE when this article was written.

You can play around with the jsfiddle to see it in action.

Related links

Popular posts from this blog

HTML 5 data-* attributes, how to use them and why

It is always tempting to add custom attributes in HTML so that you can use the data stored there to do X. But if you do that there is no way of knowing if your HTML attribute will not be overridden in the future and used for something else and additionally you will not be writing valid HTML markup that can pass HTML 5 validator and with that you can create some very bad side effects. That is why there is a spec in HTML 5 called custom data attributes that enable number of useful features.

You may go around and read the specs, but the basic idea is very simple, you can add any attribute that starts with "data-" and that attribute will be treated as non-visible data for that attribute. By non-visible I mean that it is not something that gets rendered to the client so it does not affect the layout or style of the page, but it is there in the HTML so in no way this is private.
So let's get right into it, the following snippet is a valid HTML5 markup

<div id="aweso…

Temporary files and directories in Java 7 and before

Sometimes we want to create a temporary file, whether to save some data that gets written by some other application or just to temporary store stuff. Well, usually applications have their own temporary folder where they do this and it gets somehow configured. But why not use the underlying OS specific file like "/tmp/" in Linux so there must be some system property that has this info and there is. The key is "java.io.tmpdir" resulting in "/tmp" in my case or by code:
String tempDir = System.getProperty("java.io.tmpdir"); We can use tempDir  folder as a temporary place to store files, but there are a lot nicer ways to work with files like this even in JDK6 not just in JDK7:
import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; public class TempFile { public static void main(String[] args) { try { // create a temp file File tempFile = File.createTempFile("old-file",…

Basic Authentication with RestTemplate

Spring Rest Templates are very good way of writing REST clients. By default they work with basic HTTP so if we need to use Basic Authorization we would need to init the rest template with custom HttpClient. This way the Rest Template will automatically use Basic Auth and append to the HTTP headers "Authorization: Basic BASE64ENCODED_USER_PASS".

HttpClient client = new HttpClient(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("USERNAME","PASS"); client.getState().setCredentials( new AuthScope("www.example.com", 9090, AuthScope.ANY_REALM), credentials); CommonsClientHttpRequestFactory commons = new CommonsClientHttpRequestFactory(client); RestTemplate template = new RestTemplate(commons); SomeObject result = template.getForObject( "http://www.example.com:9090/",SomeObject.class );

In EE application this would probably be managed by DI framework like Spring Core and only initialized once sin…