Detecting beacons using Nearby Messages API

My colleagues from iOS are working on a project that is using BLE  beacons to detect when users that have the app installed enter a BLE beacon region. Apple has native support for beacon detection and it seemed pretty straight forward to use on iOS. I was curious how this can be done on Android, and unfortunately it was frustrating, as any other BLE development at the moment of this writing.

On Android one option would be the AltBeacon library which :

Allows Android devices to use beacons much like iOS devices do.

What this library does, is pretty much offers the same API on Android,  as Apple does on iOS. The library is maintained, it has a large user group, but of course there still are problems, and I expect more to come now with the introduction of Doze, and the next release of Android-N, which also brings changes to Doze mode, and power savings.

So I wanted as much as possible a “native” support for this. One option would be to directly use the BLE API like BluetoothLeScanner  and continuously scan in the background for beacons. You would have to probably start the scan at intervals, to not drain the battery, schedule alarms to start the scans, a lot of overhead, I was looking for something more simple. The other option was to use the Nearby Messages API from the Play Services library, which seemed to allow a “background scan mode”.

Integration was not straight forward as I hopped to, some additional steps have to be done and the documentation is not very clear / all over the place. So, in order to integrate “Nearby Messages API” to detect beacons on Android, you will have to do the following :

  1. First create a Google Developer Console project
  2. Enable the “Nearby Messages API” for the project you just created
  3. Create an API Key for this project, as described here and add it to the Manifest
    • <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.google.sample.app" >
          <application ...>
              <meta-data
                  android:name="com.google.android.nearby.messages.API_KEY"
                  android:value="API_KEY" />
              <activity>
              ...
              </activity>
          </application>
      </manifest>
  4. Configure and register your beacons as described here; what I had to do was install the Android app, open it, select the project I just created, discover the beacon and register it with my app
  5. You will have to attach a message to your beacon, to get it detected. I did this using the Beacon Tools app, click the registered beacon, and tap on “Attachments”
  6. Follow the code sample here and it should detect your beacons

For subscribing from an Activity or Fragment you should do the following :

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    googleApiClient = new GoogleApiClient.Builder(this)
            .addApi(Nearby.MESSAGES_API)
            .enableAutoManage(this, this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .build();
}

@Override
protected void onStart() {
    super.onStart();
    googleApiClient.connect();
}

@Override
protected void onStop() {
    super.onStop();
    googleApiClient.disconnect();
}

@Override
public void onConnected(@Nullable Bundle bundle) {
    Nearby.Messages.subscribe(googleApiClient, new MessageListener() {
        @Override
        public void onFound(Message message) {
            Log.i(TAG, "Found message : " + message);
        }
    });
}

If you want to subscribe in the background, you should pass a PendingIntent instead of a MessageListener :

private void backgroundSubscribe() {
    Nearby.Messages.subscribe(googleApiClient, getPendingIntent(),
            new SubscribeOptions.Builder()
                    .setStrategy(Strategy.BLE_ONLY)
                    .build());
}

private PendingIntent getPendingIntent() {
    return PendingIntent.getService(
             this, 0,
             new Intent(this, BackgroundSubscribeIntentService.class),
             PendingIntent.FLAG_UPDATE_CURRENT);
}

Note: the last method, used to subscribe in the background has a lower reliability, as mentioned in the docs :

Subscribes for published messages from nearby devices in a persistent and low-power manner. This uses less battery, but will have higher latency and lower reliability (notably, updates may only be delivered when the device’s screen is turned on). The subscription will be retained after the client disconnects.

The full source for my sample project where I also handle  the user consent flow, granting permissions, and all the necessary stuff to scan for beacons is available on GitHub .

Posted in Android, BLE, Google, PlayServices | Tagged , , , , , , , | 5 Comments

Android Unit testing with Junit4 and Mockito

Finally , Android Studio 1.1 and the Android gradle plugin bring support for unit testing . We no longer need to run our tests on an actual device, we will be able to execute the tests against the JVM, making them much faster. This was previously possible using robolectric, but I always found it a hussle to setup on different projects.

So why should we test our code ?

enter image description here

First of all tests (unit tests, integration tests, whatever) are not like cake frosting, You don’t add them at the end. Tests are written during the development process, not at the end of it. So you will need to plan ahead, and write code while constantly having testability in mind. Which is a good “problem” to have.

But, why should you even bother writing unit tests ?

  • unit testing will “force” you to write loosely coupled code
  • gives you confidence in your code / system
  • fast, automated regression
  • easily improve and refactor your code without breaking it
  • provide documentation, because they show how the code should be used and how it should behave

What is basically a unit test :

  • written by the programmer
  • runs fast (no Network, DB, File access)
  • small scope, focused on a smart part of the software

If you are still not convinced, here is why I write tests :

  • too lazy to manually test
  • I hate regression testing
  • don’t trust QA to properly test all the cases
  • don’t trust other developers changes
  • I wan’t to be able to do a change on Friday at 6:00 PM, publish it, and have a relaxing weekend

enter image description here

To get started with unit testing on Android make sure you follow these steps. Basically you need to :

  • make sure you use build tools 1.1.0 or newer
classpath 'com.android.tools.build:gradle:1.1.0'
  • add those dependencies to your build.gradle
 testCompile 'junit:junit:4.12'
 testCompile 'org.mockito:mockito-core:1.9.5'
  • as this is an “experimental feature” for now, go to Settings>Gradle>Experimental and check Enable Unit Testing support
  • create a folder src/test/java/ and place your unit tests there
  • to Run your tests in Android Studio, select Test Artifact : Unit Tests and then right-click your test class or package and select Run

Here is an example from a sample project I made to highlight the benefits of tests in your Android app.

public class DefaultUserManager implements UserManager {
    private static final String KEY_USER = "key_user";
    private final AccountManager accountManager;
    private final SharedPreferences preferences;

    @Inject
    public DefaultUserManager(AccountManager accountManager, SharedPreferences preferences) {
        this.accountManager = accountManager;
        this.preferences = preferences;
    }
   @Override
    public void setUser(@NonNull User user) {
        if (user.getUserName() == null || user.getUserName().isEmpty()) {
            throw new IllegalArgumentException("Username can not be null or empty");
        }
        if (user.getAuthToken() == null || user.getAuthToken().isEmpty()) {
            throw new IllegalArgumentException("Auth token can not be null or empty");
        }
        preferences.edit().putString(KEY_USER, new Gson().toJsonTree(user).toString()).commit();
        Account account = new Account(user.getUserName(), CleanAuthenticator.TYPE);
        accountManager.addAccountExplicitly(account, "no_pass", null);
        accountManager.setAuthToken(account, CleanAuthenticator.TOKEN_WRITE, user.getAuthToken());
    }
}

And now a part of the unit test for it :

@RunWith(MockitoJUnitRunner.class)
public class DefaultUserManagerTest {
    DefaultUserManager testedUserManager;

    @Mock
    SharedPreferences preferences;
    @Mock
    AccountManager accountManager;
    @Mock
    SharedPreferences.Editor editor;

    ///some setup code omitted

    @Test
    public void testSetUserWillPersistToSharedPreferences() {
        //when - adding a new user
        testedUserManager.setUser(theUser());
        //verify - preferences are updated
        verify(preferences, atLeastOnce()).edit();
        //verify - changed to preferences are committed
        verify(editor, atLeastOnce()).commit();
    }

    @Test
    public void testSetUserWillAddAndroidAccount() {
        //when - adding a new user
        testedUserManager.setUser(theUser());
        //verify -that a user was also added to the Android account manager
        verify(accountManager, times(1))
                .addAccountExplicitly(any(Account.class), anyString(), any(Bundle.class));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testInvalidUserThrowsException() {
        //when - adding an invalid user
        testedUserManager.setUser(aInvalidUser());
    }
    //some more omitted, boilerplate code...
}

So with 3 small, easy to read and maintain methods, I have tested my setUser() method. This is how the setUser() method is supposed to work, and this is how all the classes interacting with my UserManager.setUser() method expect it to work.

Obviously in time I will need to improve this code, re-factor it, change it, and this can and will break some things. But, I will have the confidence of my unit tests, I’ll just run them in a few seconds, see if anythings is broken, and if not I can publish my changes, and get back to … YouTube.

Of course, there are multiple cases that will need integration tests, and even manual test, but unit tests will find errors early, will save you a ton of time and also drive good design in your code.

Posted in Android | 1 Comment

Recycler View – pre cache views

Prerequisites :

  1. RecyclerView

Recently I had to “optimize” a RecyclerView to make it load images before they are displayed (render views, before they become visible). The problem was as seen below, that for a few milliseconds the preview or a blank image is displayed before the Bitmap is placed. I had to get rid of this effect, and make the images load before they become visible to the user. Obviously for loading images I am using Picasso .

And here is the code for the initial implementation, which obviously loads a list of cat images.


CatsRecyclerAdapter adapter = new CatsRecyclerAdapter(getActivity(), cats);

//Setup layout manager
PreCachingLayoutManager layoutManager = new PreCachingLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
layoutManager.setExtraLayoutSpace(DeviceUtils.getScreenHeight(getActivity()));

recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);

First thing I tried was to tell Picasso to  pre-fetch some of the images, before they are displayed. Inside the bindViewHolder() method I was using :


Picasso.with(context).load(url).fetch();

to load the images for the next few items. This will download the images before they are needed, and improves things, but there is still a delay while the Bitmap is loaded from disk / memory and resized properly.

My second temptation was to “hack” the RecyclerView, and set the width to be higher than the screen height, forcing the rendering of a few items that are not yet visible to the user. This did not work with RecyclerView, although it might work with ListView.

Finally I digged more inside the RecyclerView and found out that the LinearLayoutManager has a protected method :

protected int getExtraLayoutSpace(RecyclerView.State&amp;amp;gt; state)

So I extended this class and override the method, to return an additional number of pixels to be drawn, and solve our problem in an elegant way.

public class PreCachingLayoutManager extends LinearLayoutManager {
    private static final int DEFAULT_EXTRA_LAYOUT_SPACE = 600;
    private int extraLayoutSpace = -1;
    private Context context;

    public PreCachingLayoutManager(Context context) {
        super(context);
        this.context = context;
    }

    public PreCachingLayoutManager(Context context, int extraLayoutSpace) {
        super(context);
        this.context = context;
        this.extraLayoutSpace = extraLayoutSpace;
    }

    public PreCachingLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
        this.context = context;
    }

    public void setExtraLayoutSpace(int extraLayoutSpace) {
        this.extraLayoutSpace = extraLayoutSpace;
    }

    @Override
    protected int getExtraLayoutSpace(RecyclerView.State state) {
        if (extraLayoutSpace > 0) {
            return extraLayoutSpace;
        }
        return DEFAULT_EXTRA_LAYOUT_SPACE;
    }
}

And just use this as LayoutManager for our RecyclerAdapter, and as it can be seen below, the problem is solved.

If anyone needs it, the code is available on GitHub.

Posted in Android, Google | Tagged , , , , , , , | 8 Comments

Google Play Services – on a much needed DIET

Later EDIT 2015 : Google now has split up the play-services library on modules (http://android-developers.blogspot.ro/2014/12/google-play-services-and-dex-method.html) so the below method is not needed anymore.

As you should know, Android has a DEX method count limit of 65 K. Adding multiple libraries & jars to your project, will easily break this limit. One such library is Google Play Services which is  a very large library. See this post from Jake Wharton : “Play Services is a Monolith Aboinmation” 

Google Play Services 5.0 which just launched contributes over twenty thousand methods to your app. 20k+. One third of the limit! Now that is scary.

This was written when Play Services 5.0 were released. Now with the release of PlayServices 6.1 and a ton of other SDKs added (FIT SDK, new Drive APIS, etc) that count definitely increased. So, your DEX limit exceeds 65K , what should you do now ?

1.Use ProGuard

It’s good to have proguard configured for your project. I really recommend to strip down, obfuscate and shrink your application… BUT at RELEASE time, not every time you deploy to a device. This would really slow down the development process, the build time will increase a lot, obfuscated stacktraces, etc, etc. There are also cases where you can’t use ProGuard on your project ( using Dagger, although it might be supported now) . So this is not really a viable solution for me… as a lot of people tend to say.

2.Use the new MultiDex Support

Since a few weeks, we can use the new MultiDexApplication as mentioned in this post . Quote from the author of this post :

Note: you should really, really Proguard before going down the Multidex rabbit hole.

No, thanks. I’ve tried it, not easily supported by Gradle yet, as it can be seen here : DEX’s 64K limit is not a problem anymore, well almost… , but it works. But this also increases my build time from 15 seconds, to 2 minutes + a lot of Android Studio OutOfMemory exceptions… Not an option. There are a lot of people commenting on forums and issues, as it can also be seen on Jake Wharton’s post about the Play-Services big method count problem, mentioning this is not a big problem , and that we should use ProGuard, quote from one of the comments :

 I’d hardly call Google Play Services an abomination just because a handful of seconds are added to everyone’s debug build flow. I’d suggest that the lack of a 1.0 release on Android Studio is a bigger problem in everyone’s workflow.

Are you joking ? Even adding only 30 seconds delay to the build, when we deploy a lot when developing on Android, this means : ~50 deploys/day * 30 seconds = 25 minutes ~25 times of the 50, I say you will get distracted while the build is in progress… = + 30-60 mins lost 1 man = 60-90 mins lost / day due to slow build times… 1 team of 3 people = ~4 hours lost / day = ~25-30 hours lost / week THIS IS A BIG PROBLEM.  None of the above are acceptable solutions.

3.Google Play Services REPACKAGED

So, I re-packaged google play services to strip down it’s size. Here’s how to do it : 1.Go to ANDROID_HOME\extras\google\m2repository\com\google\android\gms\play-services\6.1.11 and get the play-services-6.1.11.aar file from there 2.Un-zip it ( I used WinRar ) and obtain the classes.jar file from it. This is where all the play services code is packaged. 3.Get JarJar – a utility that makes it easy to repackage Java libraries and embed them into your own distribution. 4.Create a services.rules file for jarjar. I use only Maps and Google Cloud Messaging in my application, so my services.rules file contained :

 keep com.google.android.gms.gcm.*
 keep com.google.android.gms.maps.*
 keep com.google.android.gms.location.*

5.Now, copy jarjar.jar , classes.jar , services.rules in the same folder and run :

java -jar jarjar.jar process services.rules classes.jar classes-stripped.jar

You’ll see classes-stripped.jar will be much smaller than the previous classes.jar. Rename classes-stripped.jar to classes.jar and add it back to the play-services.aar using an archive editor ( I used WinRar). 6.In Android Studio, use File->Import Module -> Import .JAR or .AAR package , and point to the .aar file that we just edited. That’s it. Now you have google-play-services added to your app as an android project and you’ll keep your quick build time. Did not had any troubles so far.. So this is the only viable solution for me right now. In the future, I really hope the folks at Google take the suggested approach by a lot of developers out there, and unbundle the play-services. Looking forward for the day when we will be able to add in build.gradle  just :

 compile 'com.google.android.play-services:common:6+'
 compile 'com.google.android.play-services:maps:6+'

Even more, looking forward for the day when DEX count will be removed.

Posted in Android, Google | Tagged , , , , , , , , | 3 Comments