How to Build a Continuous Integration Environment for Java with Jenkins, Maven, SVN, and IntelliJ IDEA

 
Continuous Integration is a practice that does not necessarily require specialized CI server software. You know for sure some magicians, who are able to write amazing shell scripts in less than a few hours, that calls the build, makes the tests, deploys status emails with links to nicely formatted reports and run it periodically at fixed times as Cron job. But not everybody is such a shell script wizard. For the rest of us, there is CI server software like Jenkins.

Regardless what way you take, you need a build process, that can be initiated by command line. The same applies to your tests. There are several build tools; the usual suspects in the Java world being Ant, Maven and meanwhile Gradle. Sometimes there are heated debates, which is the better tool. This is certainly not the case here, although I personally have a slight tendency towards Maven for several reasons.

The next important component is a version control system, in this example Subversion is used.

The screencast is divided into the following sections:
– Some basic configuration of Jenkins
– Quick setup of SVN
– Configure quickly a very simple Maven project on the command line
– Import the Maven project into IntelliJ IDEA and link it to the SVN repository
– Create a Jenkins Job, that polls every 5 minutes the SVN repository for changes
– Let a JUnit test fail and show what happens
– Add a dependency on log4j to Maven’s pom.xml and show the interaction between Maven, IntellliJ and Subversion

The actual project is nothing fancy, just a dummy Java Swing application.

But the same principles apply to more complicated scenarios, e.g. when working with a JEE stack. IMHO these are scenarios in which Maven can play its strengths. That’s perhaps a topic for a further blog post, who knows? 😉

Creating a Beacon App for Android in Less Than 10 Minutes from Scratch

 
Before starting to create a Beacon app for Android a few words about Bluetooth.

Bluetooth Low Energy (BLE)

Compared to classic Bluetooth, Bluetooth Low Energy consumes much less power (e.g. 0.01 W instead of 1W), the BLE devices are considerable cheaper to build, but the data transfer rate is much lower (e.g. 0.27 Mbit/s).

Great applications for BLE are scenarios where the power consumption must be low and the data rate is not important, e.g. the communication with small wearables, watches and fitness equipment and so on.

So it was only logical when Apple decided to use BLE as base technology for their small passive transmitters called iBeacons®.

Beacons

Beacons are permanently sending out their unique identifier to notify nearby smartphones of their existence, hence the name beacons. A compatible app running on the smartphone then knows that it is in a close proximity. Depending on the application, this can trigger an action on the smartphone, e.g. displaying some information about nearby items.

Beacons are available in different sizes and shapes and from different vendors, e.g. one being www.beaconinside.com.

Different Beacons from different Vendors

Different Beacons from different Vendors

Bluetooth Smart

Bluetooth Low Energy is also known as Bluetooth Smart. Since Android 4.3 Jelly Bean, Android also supports Bluetooth Smart.

Android Smartphone Requirements:

  • Android > 4.3
  • Bluetooth Smart radio unit

The Android SDK has no built-in support for beacons. But there is a library from Radius Networks that can be used.

Beacon Advertisements

Beacons broadcast in regular intervals (e.g. something between 100ms and 1 second). They send out their ID.

The ID consists of three parts:

  • UUID (organization or company)
  • major (arbitrarily, e.g. specific chain store)
  • minor (e.g. location in store)

Flow Control

First you have to define a Region. A region does not mean a geographically region, it’s rather a certain types of beacons you are interested in.

After defining a monitoring and ranging callback you can start monitoring.

Pseudo Code:

region = new Region("<someUUID>")
addMonitorCallback
    ->(didEnterRegion)
        startRangingBeaconsInRegion
    ->(didExitRegion)
        stopRangingBeaconsInRegion
addRangingCallback
    ->(didRangeBeaconsInRegion)
        evalute beacon info

startMonitoring(region)     

Coarse-grained Distance Estimations

The received signal strength indicator (RSSI) together with the Transmit Power can be used to measure the distance between transmitter and smartphone. The distance is not exactly measured; it’s more a coarse-grained estimation. Therefore often the proximity is divided intoImmediate (centimeters), Near (couple of meters) and Far (> 10 meters).

The library averages the distance measurements over 20 seconds in time, therefore you will note a lag, when the distance between beacons and smartphone changes. This might exactly be the behavior you want, since the distance estimations are coarse-grained and you are most likely interested in the information, which one of the beacons is the closest one.

Screencast

The following screencast shows in detail how to create a Beacon app for Android in less than 10 minutes from scratch.

Source Code

Finally here is the source code for this simple example.

package com.software7.beacon.app;

import android.os.RemoteException;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import org.altbeacon.beacon.*;

import java.util.Collection;


public class BeaconActivity extends ActionBarActivity implements BeaconConsumer {

    public static final String TAG = "BeaconsEverywhere";
    private BeaconManager beaconManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_beacon);

        beaconManager = BeaconManager.getInstanceForApplication(this);

        beaconManager.getBeaconParsers().add(new BeaconParser()
                .setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));

        beaconManager.bind(this);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        beaconManager.unbind(this);
    }

    @Override
    public void onBeaconServiceConnect() {
        final Region region = new Region("myBeaons", Identifier.parse("<replaceBySomeUIID>"), null, null);

        beaconManager.setMonitorNotifier(new MonitorNotifier() {
            @Override
            public void didEnterRegion(Region region) {
                try {
                    Log.d(TAG, "didEnterRegion");
                    beaconManager.startRangingBeaconsInRegion(region);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void didExitRegion(Region region) {
                try {
                    Log.d(TAG, "didExitRegion");
                    beaconManager.stopRangingBeaconsInRegion(region);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void didDetermineStateForRegion(int i, Region region) {

            }
        });

        beaconManager.setRangeNotifier(new RangeNotifier() {
            @Override
            public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
                for(Beacon oneBeacon : beacons) {
                    Log.d(TAG, "distance: " + oneBeacon.getDistance() + " id:" + oneBeacon.getId1() + "/" + oneBeacon.getId2() + "/" + oneBeacon.getId3());
                }
            }
        });

        try {
            beaconManager.startMonitoringBeaconsInRegion(region);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_beacon, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}


Last Famous Words

I hope that gets you started with your beacon project.

If you found this helpful, share it!

Using Auto Layout to add a Comments Section to your iOS 8 App

 
Below there is a complete series of 4 screencasts (38 minutes total) to show how to add a comments section to your app.
It uses Auto Layout, the cell height is calculated automatically, it runs on iPhone and iPad, and it moves the transparent comments section automatically when the onscreen keyboard is shown.

Comments Section

The final result of our comments section…

 

Please note that iOS 8 is required for this tutorial.

The tutorial is divided into 4 screencasts:

1) Basic Setup (18 minutes)
2) Automatic Cell Height Calculation (7 minutes)
3) Handle the onscreen Keyboard (8 minutes)
4) A little bit of Polishing (5 minutes)

If there are any questions, simply leave a comment in the comments section of the blog.