June 9, 2014 · alarmclock api calendar coding cron cron4j debian google guava jasypt java jcalendar joda-time linux raspberrypi update windows

Google Alarm Clock goes JAVA

logo_java
During the past few months I've been doing a lot of Java programming. A great opportunity occurred for me to actually grasp a new programming language. It was even more fascinating, as it didn't involve the ubiquitous in the Internet sphere 'learning to code' approach, i.e., take a course, learn to add up numbers and believe you can code. Instead of learning from scratch without a clue why would I be doing this or that, I have actually grasped Java in practice. I wrote two fully working Java desktop database applications that are now used by a company. Moreover, my home Linux server got another task - database server.

The Java experience made me want to rewrite all the pieces of Python code to a more cross-platform language. Java is cross platform in the sense that a compiled Java program runs on all platforms for which there exists a JVM (This holds for all major operating systems, including Windows, Mac OS and Linux.). First application to get Java implementation was the Google Alarm Clock. Since I am using the alarm on a daily basis, I thought to give it a try (after that I intend to test the Pi4j Java library to use Raspberry Pi GPIO with Java). The application works fine, but lacks some features, but I'm constantly improving it and making it better. Here's what I come up with so far.

How it works

The basic principle of the application is the same as the Python script I've introduced in September last year. We add an event to the Google calendar (it can be done within the application), and once the alarm clock is started it queries Google Calendar for events matching our criteria:

    CalendarService myService =
        new CalendarService("SimpleGoogleAlarmClock");
    myService.setUserCredentials(mail, decryptedPass);
    URL feedUrl = new URL("https://www.google.com/calendar/feeds/default/private/full");

    CalendarQuery myQuery = new CalendarQuery(feedUrl);
    myQuery.setMinimumStartTime(s);
    myQuery.setMaximumStartTime(e);
    myQuery.setFullTextQuery(query);
    myQuery.setStringCustomParameter("singleevents", "true");
    myQuery.setStringCustomParameter("sortorder", "a");

    CalendarEventFeed myResultsFeed =
        myService.query(myQuery, CalendarEventFeed.class);

    for (int i = 0; i < myResultsFeed.getEntries().size(); i++) {
        CalendarEventEntry MatchEntry = (CalendarEventEntry) myResultsFeed.getEntries().get(i);
        String time = MatchEntry.getTimes().get(0).getStartTime().toUiString();
        String wakeup = MatchEntry.getTitle().getPlainText();
        queryArea.append("\t" + time + " " + wakeup + "\n");
    }

Once it reached start time of an event it starts music player. The Google password is encrypted within the application using Jasypt library.Once properties file is created at first run, the library saves your password in an encrypted form. When the password is needed, the same library decrypts it inside the application and passes to objects requesting your password.

The application would work both on Windows and Linux machines. I suppose Mac users would be able to use it fully as well. As long as you have Java installed on your machine, you are good to go.There is no need to download and install any additional libraries, programs, files, as all the libraries needed are included with the project. You can even run the application on Raspberry Pi (just type sudo apt-get update && sudo apt-get install oracle-java7-jdk from your console to install it). The application is set to recognize the operating system it is running on and adjust paths and external commands according to the OS. Moreover, there is no need for working directory. The application would work from anywhere, just make sure the lib subdirectory is placed in the same directory as the jar file.

To start the application, just double click on the GoogleAlarmClock.jar file from Desktop mode, or if you want to start it from command line (Desktop is needed though), type:

 java -jar /path/to/GoogleAlarmClock.jar

Here's the main application window:

Google Alarm Clock

Once the local date and time on your machine matches the event start date-time of an event, the application starts (depending on the operating system: Windows: Windows Media Player; Linux: mpg321) music player that plays random mp3 from pre-set directory:

    ...

    if (time.matches(sDate.toString(fmtt))) {
        new Thread(alarm).start();
    }

    ...

    Thread alarm = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                in = new FileInputStream(props);
                propsy.load(in);

                String path = propsy.getProperty("mp3_path");

                File dir = new File(path);
                if (dir.isDirectory()) {
                    File[] files = dir.listFiles();
                    File selected = files[new Random().nextInt(files.length)]; // selects random file
                    String song = selected.toString(); // passes songpath to string
                    String decodedPath = URLDecoder.decode(empe, "UTF-8");
                    try {
                        final String os = System.getProperty("os.name");
                        if (os.contains("Windows")) {
                            // if OS is Windows, start WMP
                            String playCommand = "cmd /c start \"%programfiles%\\Windows Media Player\\wmplayer.exe\" " + "\"" + decodedPath + "\"";
                            Runtime.getRuntime().exec(playCommand);
                        } else {
                            // if Linux/Mac start mpg321
                            String[] playCommand = {"mpg321", "-g", "100", decodedPath};
                            Runtime.getRuntime().exec(playCommand);
                        }
                    } 
             ...
        }
    });

alarm_running

In the future, I plan to implement mp3 player within the application itself. For the time being, make sure you have installed mpg321 on your Linux machine or have WMP active on your Windows to make the "alarm" work. I am also working on a headless version of the application, that can be run purely from the console. I have a simplified implementation that only starts the scheduler, but I want it to become a server-client application invoked from the desktop application described here so there's no need to log into your Raspberry Pi or server to start the application. It would then send a request to your server and start the scheduler there.

Implemented (so far) keyboard shortcuts

From the main window:

  • Alt+E "” Show events,
  • Alt+S "” Start alarm clock,
  • Alt+A "” Add event,
  • Alt+P "” Edit properties.

From the Event window:

  • Alt+R "” Set event recurrence.

Adding Alarm/event

You don't have to open web browser to create wake event. The application lets you set the alarm from itself. Once you click Add entry button (keyboard shortcut: Alt+A), a new window will open letting you set an event in a simplified Google Calendar manner. You can set the event title (default is extracted from the application's properties file), start and end date-time and simplified recurrence.

Add Alarm Windows

The recurrence is, for now, limited to choosing days of week and end date for the recurring. In the future I intend to create a fully-fledged event creation. Once you set the alarm recurrence, the Repeat check-box will within Add Alarm window will show you a simplified summary of the event repetition pattern.

Add Event Window

After adding an event, a summary message will appear:

summary

Now we can check the Google Calendar to see if the event was added. Success!

The piece of code responsible for adding an event to Google Calendar is:

// for single event
    DateTime startTime = DateTime.parseDateTime(dateFrom + "T" + timeFrom);
    DateTime endTime = DateTime.parseDateTime(dateTo + "T" + timeTo);
    When eventTimes = new When();
    eventTimes.setStartTime(startTime);
    eventTimes.setEndTime(endTime);
    myEntry.addTime(eventTimes);

    CalendarEventEntry insertedEntry = myService.insert(postUrl, myEntry);
    insertedEntry.update();

// for recurring event
    String recurData = "DTSTART;TZID=" + timeZone + ":" + stTime + "\r\nDTEND;TZID=" + timeZone + ":" + enTime + "\r\nRRULE:FREQ=WEEKLY;BYDAY=" + days + ";UNTIL=" + unt;
    Recurrence r = new Recurrence();
    r.setValue(recurData);
    myEntry.setRecurrence(r);

    CalendarEventEntry insertedEntry = myService.insert(postUrl, myEntry);
    insertedEntry.update();

The recurData string contains syntax for recurring events based upon the iCalendar standard (RFC 2445).

Properties file

Properties ScreenThe properties passed from the application are saved in a separate file: <mark>propsy.properties</mark>. This is a regular JAVA properties file created from the application at first run. As The Java Tutorials states,

Properties are configuration values managed as key/value pairs. In each pair, the key and value are both String values. The key identifies, and is used to retrieve, the value, much as a variable name is used to retrieve the variable's value.

The Google Alarm Clock properties consists of six keys:

    email            // stores your Google email
    password         // stores encryted password for your Google Account
    query            // the phrase used for querying Google Calendar
    mp3_path         // directory containing your music files
    time_zone        // your time zone
    scheduler        // used by cron4j, can be edited from here

The application writes out the properties with the following method:

// creating Properties object
    Properties propsy = new Properties();
// creating File object
    File props = new File("propsy.properties");
// creating output stream to write the file
    OutputStream out = out = new FileOutputStream(props);

// Strings containing information from 
// jTextFields, jCombobox and jPasswordField
    String cron = "* * * * *";
    String mail = email_tf.getText();
    char[] passw = pass_tf.getPassword();
    String pass = new String(passw);
    String quer = query_tf.getText();
    String mp3path = path_tf.getText();
    String tz = Timezone.getSelectedItem().toString();

...

// preparing the information to be 
// passed into the Properties file
    propsy.setProperty("email", mail);
    propsy.setProperty("password", encryptedPassword);
    propsy.setProperty("query", quer);
    propsy.setProperty("mp3_path", mp3path);
    propsy.setProperty("time_zone", tz);
    propsy.setProperty("scheduler", cron);

// writing the Strings into the Properties file
    propsy.store(out, null);

// closing the output stream
    out.close();

NOTE: Mind the difference between password string and Properties set method - this is because the password is being encrypted before it is passed to the Properties with Jasypt library:

// Password-Based encryption is performed by means of
// generating an encryption key from a user-supplied
// password, and feeding an encryption algorithm with
// both the input and the generated key
    StandardPBEStringEncryptor passwordEncryptor = 
        new StandardPBEStringEncryptor();
    passwordEncryptor.setPassword("some_randomly_generated_phrase");
    String encryptedPassword = passwordEncryptor.encrypt(pass);

Once the application has set up its Properties object, it can query the object for information about the keys and values that it contains once they are needed.

// creating Properties object
    Properties propsy = new Properties();
// creating File object
    File props = new File("propsy.properties");
// creating input stream to read from the file
    FileInputStream in = new FileInputStream(props);

// opening the input stream
    propsy.load(in);

// passing key values to Strings
    String mail = propsy.getProperty("email");
    String pass = propsy.getProperty("password");
    String query = propsy.getProperty("query");
    String path = propsy.getProperty("mp3_path");
    String tz = propsy.getProperty("time_zone");

...

// passing string values to text fields
    email_textfield.setText(mail);
    pass_textfield.setText(decryptedPass);
    query_textfield.setText(query);
    path_textfield.setText(path);
    Timezone_combobox.setSelectedItem(tz);

// closing input stream
    in.close();

NOTE: Before your password is passed into the application it is first decrypted.

The TimeZone combo box has been achieved with the following method:

    private void LoadTZ() {
        String TIMEZONE_ID_PREFIXES = "^(Africa|America|Asia|Atlantic|Australia|Europe|Indian|Pacific)/.*";
        ArrayList&lt;String&gt; list = new ArrayList&lt;&gt;();
            String[] timezoneIDs = TimeZone.getAvailableIDs();
            for (String TZ : timezoneIDs) {
                if (TZ.matches(TIMEZONE_ID_PREFIXES)) {
                    list.add(TZ);
                }
            }
            Collections.sort(list);
    }
// the LoadTZ(); void is then invoked at application start
// and jComboBox Timezone is set a model:
    Timezone.setModel
        (new DefaultComboBoxModel&lt;&gt;
            (list.toArray(new String[list.size()])));

This method eliminates all the unnecessary time zones and double entries provided by Java default TimeZone object. Once the time zones are parsed through the regex TIMEZONE_ID_PREFIXES, they are sorted alphabetically and passed to Timezone jComboBox.

Setting up the time zones is needed for creating Calendar events from within the application. I've noticed that the Google Calendar API has some problems parsing daylight saving times. Instead, it creates events an hour earlier/later. Thus the additional time zones setting.

Automatic updates

A neat feature for a desktop application is to automatically notify the user for available updates or even provide automatic updating functionality. I've decided to implement a library and architecture that can provide it.

Back-end

First of all, when you run the application, it checks if there is a newer version available. It is doing this by checking an xml file located somewhere in a remote server. Once a newer version is available, the application notifies the user that a new version is available and prompts them to allow the application to perform automatic update:

update1

Next, the application parses another xml file containing links with all the files that are needed for the update. The files are downloaded and stored in a temporary directory and passed through instructions of what to do with them.

Those are:

update3

  • MOVE: Replaces old file with new one.
  • DELETE: Deletes an old file.
  • EXECUTE: Executes a new file (a jar actually).
  • **OPEN: **Opens a file (used for text file with a list of changes).

Once the update is done, the application will prompt the user to restart it. In case of errors, an error message will be displayed. The updates are not mandatory, you can choose if you want to perform an update or not.

update2

Check for new version

The application version is set inside the code. It is composed of version and release number:

Release release = new Release(); 
release.setpkgver("1.0"); 
release.setPkgrel("1");

The remote xml file is set with the current release/version info:

<?xml version="1.0" encoding="UTF-8"?> 
<information> 
<pubDate>Sat, 07 Jun 2014 19:58:42 +0000</pubDate> 
<pkgver>1.0</pkgver> 
<pkgrel>2</pkgrel> 
</information>

If the version set within the application code is different (lower) than the one in the xml file, dialogue is displayed asking the user to update.

The Update Procedure

Once the update starts, instructions from another xml file are passed and the updater downloads, moves, deletes, runs or opens predefined files from a temporary directory within the application directory, e.g.:

<update> 
<instruction> 
<action>MOVE</action> 
<file>GoogleAlarmClock.jar</file> 
<destination>GoogleAlarmClock.jar</destination> 
</instruction> 
</update>

This instruction moves the GoogleAlarmClock.jar file from temporary directory to application directory.

Once the update is finished, the temporary files are deleted.

Libraries

For the application I have used several external libraries, hence the additional <mark>lib</mark> folder that has to be placed in the same folder as the application itself. The libraries are:

  • Google APIs Client Library for Java - Core client library .jar files (including Guava) and Google Calendar API library .jar file to make the communication between the application and Calendar service work,
  • Jasypt - basic encryption capabilities with minimum effort. I have used this library to encrypt Google application-specific password (or your normal Google Account password if you don't use 2-step verification) saved in the properties file. As the properties are saved in a plain-text file, I thought some precautions might be in place. Once you set your properties inside the application, they are passed to an external file. To prevent anyone seeing your password, Jasypt encrypts it inside the properties file and then decrypts inside the application once the password is needed.
  • Cron4j -a scheduler for the Java platform which is very similar to the UNIX cron daemon. What it does is to provide a better thread solution. The library executes tasks at fixed moments, during all the year. A scheduler can execute a task once a minute, once every five minutes, Friday at 10:00, on February the 16th at 12:30 but only if it is Saturday, and so on. It works similar to Linux cron. In my case, it is set to invoke Google Calendar query every minute and pass it to alarm instance:
// Creates a Scheduler instance.
    Scheduler sched = new Scheduler();
// Schedule a once-a-minute task.
    sched.schedule("* * * * *", new Runnable() {
        @Override
        public void run() {
            ...
        }
    });
// Starts the scheduler.
    sched.start();
  • Joda-Time - a quality replacement for the Java date and time classes. The library makes time manipulation easier. Instead of writing long classes to add days to given date (used for default event repeat settings), the library lets you do it in a one liner: org.joda.time.DateTime datePlus = new org.joda.time.DateTime(jDateChooser.getDate()).plusDays(7);
  • JCalendar - a Java date chooser bean for graphically picking a date. Specifically, I have used the JDayChooser bean that lets you choose a day by clicking on the day number:

addalarm_calendar

Download

The application can be downloaded from my server:

The link is a direct download for a zip file. Download it and extract anywhere you like. I will not update the zip here, all the updates will take place from the application's automatic update system.

Feel free to test it and let me know of any problems you've encountered. Any suggestions would be appreciated.

Feel free to leave a comment here or email me at:

contact@bartbania.com

ToDo

  • Clean and debug the code,
  • Finish "headless" console version of the application,
  • Add the ability to start/stop the alarm scheduler without the need to close the application,
  • Enhance recurring events creation,
  • Add ability to update/delete Calendar events from the application,
  • Implement internal mp3 player,
  • Localize the application,
  • Create client-server framework for starting alarm schedule on a remote machine, e.g. Raspberry Pi,
  • Making source code available to anyone interested,
  • Be open to any suggestions,
  • Test, test, test"¦
  • LinkedIn
  • Tumblr
  • Reddit
  • Google+
  • Pinterest
  • Pocket

Contact