UI Testing in Phimpme Android

Espresso is an Android Testing tool which helps developers to write UI based tests. After writing tests, developers can make use of Android studio to run the tests or can implement a method in various Continuous integration sites like Travis CI to run the tests on a new push or a pull request. I implemented Espresso tests in the Phimpme Android project of FOSSASIA to test the basic UI elements of the home screen, camera view and the settings activity.

Steps to Add the UI tests :

    1. The first step is to import the packages related to the instrumentation tests and configure the build.gradle file of the application to add certain dependencies. This can be done using the line of code below:
      dependencies {
      androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
      }
      
    2. To ensure best test results make sure that you turn off the device animations. For this go to developer options and disable the:
      • Window animation scale
      • Transition animation scale
      • Animator duration scale

      After doing the above, your developer options screen should look like the screenshot below with all the animation scale disabled.

    3. Create the java class for the android test with the name of the activity you want to test followed by the word Test. Suppose you want to create a test for your MainActivity. Make a test class with the name MainActivityTest.
    4. Define your tests with annotations @LargeTest @RunWith(AndroidJUnit4.class) and define the MainActivity Test.
    5. Define the Test activity rule with the annotation @Rule and mention the java class which you want to test. I’ll be explaining the codes exactly as I used to add espresso test in Phimpme Android application below. In this for testing the PhimpMe.class, I used the following codes
      
      @Rule
      public ActivityTestRule<PhimpMe> mActivityTestRule = new ActivityTestRule<>(PhimpMe.class);

      The codes from defining the tests to defining the rules are given below:

      @LargeTest
      @RunWith(AndroidJUnit4.class)
          public class MainActivityTest {
             @Rule
             public ActivityTestRule<PhimpMe> mActivityTestRule = new ActivityTestRule<>(PhimpMe.class);
      
    6. After this, start writing the tests with the @Test annotations, the code as I used to test the Load more photos button in the MainActivity of the Phimpme Application is shown below:
      
      @Test
      ViewInteraction imageView = onView(
                      allOf(withId(R.id.btnLoadMoreLocalPhotos),
                              childAtPosition(
                                      allOf(withId(R.id.titlebarLocalPhotos),
                                              childAtPosition(
                                                      IsInstanceOf.instanceOf(android.widget.LinearLayout.class),
                                                      0)),
                                      1),
                              isDisplayed()));
      imageView.check(matches(isDisplayed()));
      

 

The above code checks whether the plus button in the top right corner is visible in the UI.

For the complete code, please refer to the Phimpme Android Repository of FOSSASIA or refer to this pull request in which I added tests for all the Activities and Fragments of the Phimpme Android Application. Since we are rebuilding the application by modifying the whole view of the application, the tests are currently removed. As soon as the application becomes fairly stable, I will be adding the Tests in this in the same way.

For complete tutorial on setting up the test using the Android Studio Inbuilt functionality. Refer to :

https://developer.android.com/studio/test/espresso-test-recorder.html#run-an-espresso-test-with-firebase-test-lab

That’s it for now. Thanks!

Resources :

https://developer.android.com/training/testing/ui-testing/espresso-testing.html

https://github.com/fossasia/phimpme-android/pull/85/files

https://developer.android.com/studio/test/espresso-test-recorder.html#run-an-espresso-test-with-firebase-test-lab

Detecting and Fixing Memory leaks in Susi Android App

In the fast development of the Susi App, somehow developers missed out some memory leaks in the app. It is a very common mistake that developers do. Most new android developers don’t know much about Memory leaks and how to fix them. Memory leaks makes the app slower and causes crashes due to OutOfMemoryException. To make the susi app more efficient, it is advised to look out for these memory leaks and fix them. This post will focus on teaching developers who’ll be contributing in Susi android App or any other android app about the memory leaks, detecting them and fixing them.

What is a memory leak?

Android system manages memory allocation to run the apps efficiently. When memory runs short, it triggers Garbage Collector (GC) which cleans up the objects which are no longer useful, clearing up memory for other useful objects. But, suppose a case when a non-useful object is referenced from a useful object. In that case Garbage Collector would mark the non-useful object as useful and thus won’t be able to remove it, causing a Memory Leak.

Now, when a memory leak occurs, the app demands for memory from the android system but the android system can only give a certain amount of memory and after that point it will refuse to give more memory and thus causing a OutOfMemoryException and crashing the app. Even if sometime due to memory leaks, the app doesn’t crash but it surely will slow down and skip frames.

Now, few other questions arises, like “How to detect these leaks?” , “What causes these leaks ?” and “How can we fix these?” Let’s cover these one by one.

Detecting Memory Leaks

You can detect memory leaks in android app in two ways :

  1. Using Android Studio
  2. Using Leak Canary

In this post I’ll be describing the way to use Leak Canary to detect Memory Leaks. If you want to know about the way to use android studio to detect leaks, check out this link .

Using Leak Canary for Memory Leak detection :

Leak Canary is a very handy tool when it comes to detecting memory leaks. It shows the memory leak in an another app in the mobile itself. Just add these lines under the dependencies in build.gradle file.

debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.5.1’
releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5.1’
testCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5.1’

And this code here in the MainApplication.java file.

if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.

// You should not init your app in this process.

return
;
}
LeakCanary.install(this);
// Normal app init code…

You are good to go. Now just run the app and if there is a memory leak you will get something like this. It dumps the memory in .hprof file and displays it in another app.

      

Causes of Memory Leaks

There are many causes of memory leaks. I will list a few top of my head but there can be more.

  1. Static Activities and Views : Defining a static variable inside the class definition of the Activity and then setting it to the running instance of that Activity. If this reference is not cleared before the Activity’s lifecycle completes, the Activity will be leaked.
  2. Listeners : When you register a listener, it is advised to unregister it in onDestroy() method to prevent memory leaks. It is not that prominent but may cause memory leaks.
  3. Inner Classes : If you create an instance of Inner Class and maintain a static reference to it, there is a chance of memory leak.
  4. Anonymous Classes : A leak can occur if you declare and instantiate an AsyncTask anonymously inside your Activity. If it continues to perform background work after the Activity has been destroyed, the reference to the Activity will persist and it won’t be garbage collected until after the background task completes.
  5. Handlers and Threads : The very same principle applies to background tasks declared anonymously by a Runnable object and queued up for execution by a Handler object.

Preventing and Fixing Memory Leaks

So, now you know what are the causes of these memory leaks. You just have to be a little more careful while implementing these. Here are some more tips to prevent or fix memory leaks :

  1. Be extra careful when dealing with Inner classes and Anonymous classes. Make them static wherever possible. Use a static inner class with a WeakReference to the outer class if that helps.
  2. Be very careful with a static variable in your activity class because it can reference your activity and cause leak. Be sure to remove the reference in onDestroy().
  3. Unregister all listeners in onDestroy() method.
  4. Always terminate worker threads you initiated on Activity onDestroy().
  5. Make sure that your allocated resources are all collected as expected. Do not always rely on Garbage Collector.
  6. Try using the context-application instead of a context-activity.

Conclusion

So, now if you want to contribute in Susi Android App and implement a feature in it, you can just check if there is a memory leak due to your implementation and fix it for better performance of the app. Also, if you find any other memory leak in the app, do report it on the issue tracker, fix it and make the Susi Android App more efficient.

Happy Coding!

Adding swap space to your DigitalOcean droplet, if you run out of RAM

The Open Event Android App generator runs on a DigitalOcean. The deployment runs on a USD 10 box, that has 1 GB of RAM, but for testing I often use a USD 5 box, that has only 512mb of RAM.

When trying to build an android app using gradle and Java 8, there could be an issue where you run out of RAM (especially if it’s 512 only).

What we can do to remedy this problem is creating a swapfile. On an SSD based system, Swap spaces work almost as fast as RAM, because SSDs have very high R/W speeds.

Check hard disk space availability using

df -h

There should be an output like this

Filesystem      Size  Used Avail Use% Mounted on
udev            238M     0  238M   0% /dev
tmpfs            49M  624K   49M   2% /run
/dev/vda1        20G  1.1G   18G   6% /
tmpfs           245M     0  245M   0% /dev/shm
tmpfs           5.0M     0  5.0M   0% /run/lock
tmpfs           245M     0  245M   0% /sys/fs/cgroup
tmpfs            49M     0   49M   0% /run/user/1001

The steps to create a swap file and allocating it as swap are

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

We can verify using

sudo swapon --show
NAME      TYPE  SIZE USED PRIO
/swapfile file 1024M   0B   -1

And now if we see RAM usage using free -h , we’ll see

              total        used        free      shared  buff/cache   available
Mem:           488M         37M         96M        652K        354M        425M
Swap:          1.0G          0B        1.0G

Do not use this as a permanent measure for any SSD based filesystem. It can corrupt your SSD if used as swap for long. We use this only for short periods of time to help us build android apks on low ram systems.

Doing a table join in Android without using rawQuery

The Open Event Android App, downloads data from the API (about events, sessions speakers etc), and saves them locally in an SQLite database, so that the app can work even without internet connection.

Since there are multiple entities like Sessions, Speakers, Events etc, and each Session has ids of speakers, and id of it’s venue etc, we often need to use JOIN queries to join data from two tables.

 

Android has some really nice SQLite helper classes and methods. And the ones I like the most are the SQLiteDatabase.query, SQLiteDatabase.update, SQLiteDatabase.insert ones, because they take away quite a bit of pain for typing out SQL commands by hand.

But unfortunately, if you have to use a JOIN, then usually you have to go and use the SQLiteDatabase.rawQuery method and end up having to type your commands by hand.

But but but, if the two tables you are joining do not have any common column names (actually it is good design to have them so – by having all column names prefixed by tablename_ maybe), then you can hack the usual SQLiteDatabase.query() method to get a JOINed query.

Now ideally, to get the Session where speaker_id was 1, a nice looking SQL query should be like this –

SELECT * FROM speaker INNER JOIN session
ON speaker_id = session_speaker_id
WHERE speaker_id = 1

Which, in android, can be done like this –

String rawQuery = "SELECT * FROM " + SpeakerTable.TABLE_NAME + " INNER JOIN " + SessionTable.TABLE_NAME
        + " ON " + SessionTable.EXP_ID + " = " + SpeakerTable.ID
        + " WHERE " + SessionTable.ID + " = " +  id;
Cursor c = db.rawQuery(
        rawQuery,
        null
);

But of course, because of SQLite’s backward compatible support of the primitive way of querying, we turn that command into

SELECT *
FROM session, speaker
WHERE speaker_id = session_speaker_id AND speaker_id = 1

Now this we can write by hacking the terminology used by the #query() method –

Cursor c = db.query(
        SessionTable.TABLE_NAME + " , " + SpeakerTable.TABLE_NAME,
        Utils.concat(SessionTable.PROJECTION, SpeakerTable.PROJECTION),
        SessionTable.EXP_ID + " = " + SpeakerTable.ID + " AND " + SpeakerTable.ID + " = " +  id,
        null,
        null,
        null,
        null
);

To explain a bit, the first argument String tableName can take table1, table2 as well safely, The second argument takes a String array of column names, I concatenated the two projections of the two classes. and finally, put by WHERE clause into the String selection argument.

You can see the code for all database operations in the android app here  https://github.com/fossasia/open-event-android/blob/master/android/app/src/main/java/org/fossasia/openevent/dbutils/DatabaseOperations.java

Building Android preference screen


Some days ago, I started building a Setting Screen for my Android app. Everything was fine, until I opened it on an older Android version. The overview screen had no material design, a thing I would have accepted, if there weren’t those completely destroyed dialogs: Android’s internal preferences are using Android’s internal app.AlertDialogs. Those dialogs in combination with the AppCompat Dialog Theme, which I had applied to them, resulted in a dialog with two frames on older devices (One system default dialog and a material frame around it).
So I decided to switch to the android.support.v7.preference library, only to face a lot more issues.


Including the Library

In order to use the new preferences, we need to import a library. To do so, we add this line to our gradle dependencies (You should change the version number to the latest).

compile 'com.android.support:preference-v7:23.4.0'

Building The Preference Screen

Creating the Preferences

At first, we need to create our preference structure: We create a new XML Android resource file as xml/app_preferences.xml. Now we can add our preference structure to this file. Make sure to add a unique android:keyattribute for each preference. More information: How to build the XML

<android.support.v7.preference.PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android"> 
    <android.support.v7.preference.PreferenceCategory
        android:title="Category 1">          
    <android.support.v7.preference.SwitchPreferenceCompat
            android:key="key1"
            android:title="Switch Preference"
            android:summary="Switch Summary"
            android:defaultValue="true" />
    <android.support.v7.preference.EditTextPreference
            android:key="key2"
            android:title="EditText Preference"
            android:summary="EditText Summary"
            android:dialogMessage="Dialog Message"
            android:defaultValue="Default value" /> 
    <android.support.v7.preference.CheckBoxPreference
            android:key="key3"
            android:title="CheckBox Preference"
            android:summary="CheckBox Summary"
            android:defaultValue="true"/></android.support.v7.preference.PreferenceCategory></android.support.v7.preference.PreferenceScreen>

The v7.preference library provides some preferences we can use: CheckBoxPreference, SwitchPreferenceCompat, EditTextPreference and a ListPreference (and a basic Preference). If we need more than these predefined preferences, we have to build them on our own.

Creating the Preference Fragment

Now we need to create our Preference Fragment, where we can show the preferences from our XML file. We do this by creating a new class, called SettingsFragment, which extends PreferenceFragmentCompat. Since the onCreatePreferences is declared as abstract in the source code of the library, we are forced to include our own implementation to tell the fragment to load our just created app_preferences.xml.

import android.support.v7.preference.PreferenceFragmentCompat;
public class SettingsFragment extends PreferenceFragmentCompat {
@Override
    public void onCreatePreferences(Bundle bundle, String s) {
        // Load the Preferences from the XML file
        addPreferencesFromResource(R.xml.app_preferences);
    }
}

We can add this SettingsFragment (v4.app.Fragment), like any other Fragment (e.g. with a FragmentTransaction) to an Activity.

Applying the Preference Theme

Finally we need to specify a preferenceTheme in our Activity’s theme. If we don’t do so, the App will crash with an IllegalStateException.
The v7.preference library provides only one Theme: PreferenceThemeOverlay(You may have a look at its source code). We add this with the following line in our Activity’s theme:

<item name="preferenceTheme">@style/PreferenceThemeOverlay</item>

After we have done this, our result should now look like this.
(The Activity’s parent theme is Theme.AppCompat and the background is set with android:windowBackground)

Settings Screen with PreferenceThemeOverlay

As you can see, it has an oversized font and a horizontal line below the category title. This is definitely not material design.

It more looks like a mixture of material design for the CheckBoxand Switch widgets on the right and an old design for everything else.

This leads us to the next point: Applying the material theme to our settings.

Applying the Material Design Theme

Since there is no material theme in our current preference library, we need to import the v14.preference library. It is strange that Google splitted up these two libraries, because the v14 version is obviously only an addition to the v7.preference library. However, this means for us, that we have to add one more line to our gradle dependencies (You should change the version number to the latest).

compile 'com.android.support:preference-v14:23.4.0'

Now we have access to two more themes: PreferenceThemeOverlay.v14 and PreferenceThemeOverlay.v14.Material (You may have a look at their source code). To use the material theme, we simply change the preferenceTheme in our Activity’s theme.

<item name="preferenceTheme">
    @style/PreferenceThemeOverlay.v14.Material
</item>

A side effect of including the v14.preference library is that we can use a new preference called MultiSelectListPreference (it requires the v7.preference library to work).

Settings Screen with PreferenceThemeOverlay.v14.Material

Our result should look like this. And this time it is full material design.

The font is not oversized anymore and also the horizontal line below the category title has disappeared.

We can change the color of the CheckBox, the Switch and the PreferenceCategory title by changing the colorAccent in our Activity’s theme.


This was only the first step to create a material design Settings Screen. As soon as you open the Alert Dialog of the EditText preference, you will find more design issues.

Motion in android

So earlier this year I attended a talk where the speaker wanted to introduce us to meaningful motion in android apps and he convinced us to use this in our apps as well. Motion came in with Material design, actually not really came but became popular with Material design and since google has added the same kind of motions to their apps as well, developers have started using it.

I love motion, not only does it boost engagement but it’s instantly noticeable. Think of the apps you use that feature motion design and how pleasing, satisfying, fluent and natural they feel to experience. Eg. Zomato, Play music etc.

Now think of some apps that don’t use any kind of motions and you’ll realise they look a bit boring and you as users will always prefer apps with some kind of motion.

Touch

So firstly let’s discover the feedback on touch. It helps to communicate to the user in a visual form that some interaction has been made. But also keep in mind that this animation should be enough for them to gain clarity and encourage further explorations and not distract them.

For adding backgrounds you can use the following :

  • ?android:attr/selectableItemBackground — Show a ripple effect within the bounds of the view.
  • ?android:attr/selectableItemBackgroundBorderless — Show a ripple effect extending the bounds of the view.

View Property Animator

Introduced in API 12, this allows us to perform animated operations (in parallel) on a number of view properties using a single Animator instance

Some of the parameters that can be added to a view are as follows :

  • alpha() -Set the alpha value to be animated to
  • scaleX() & scaleY()— Scales the view on it’s X and / or Y axis
  • translationZ() — Translates the view on its Z axis
  • setDuration()— Sets the duration of the animation
  • setStartDelay() — Sets the delay on the animation
  • setInterpolator() — Sets the animation interpolator
  • setListener() — Set a listener for when the animation starts, ends, repeats or is cancelled.

Now let’s write some code on how to do this on a button for example:

mButton.animate().alpha(1f)
.scaleX(1f)        
.scaleY(1f)        
.translationZ(10f)        
.setInterpolator(new FastOutSlowInInterpolator())        .setStartDelay(200)        
.setListener(new Animator.AnimatorListener() {            
@Override            
public void onAnimationStart(Animator animation) { }             
@Override            
public void onAnimationEnd(Animator animation) { }             
@Override            
public void onAnimationCancel(Animator animation) { }             
@Override            
public void onAnimationRepeat(Animator animation) { }        
})        
.start();

Note : Use ViewCompat class to implement the ViewPropertyAnimator from Android API version 4 and up

Object Animator

Similar to the ViewPropertyAnimator, the ObjectAnimator allows us to perform animations on various properties of the target view (both in code and XML resource files). However, there a couple of differences:

  • The ObjectAnimator only allows animations on a single property per instance e.g.Scale X followed by Scale Y.
  • However, it allows animations on a custom Property e.g. A view’s foreground colour.

Her we need to set the evaluator, set the delay and call start().

private void animateForegroundColor(@ColorInt final int targetColor) {   
    ObjectAnimator animator = ObjectAnimator.ofInt(YOUR_VIEW, FOREGROUND_COLOR, Color.TRANSPARENT, targetColor);                    animator.setEvaluator(new ArgbEvaluator()); animator.setStartDelay(DELAY_COLOR_CHANGE); animator.start();}

Interpolators

An Interpolator can be used to define the rate of change for an animation, meaning the speed, acceleration and behaviour during animating can be altered. Some of them are :

These are some of the basics. there are a lot of other things like

  • Window transitions(Explode, fade, slide etc.)
  • Shared element Transitions
  • Other custom transitions
  • Animated Vector drawables

you can play around with these for a better understanding but be sure to actually try everything on a device/emulator since you’ll get to actually see the changes in the UI and in turn understand better.

Flavors in Gradle

Ever wondered how do people maintain different versions of the same app on play store with some customisations in each version. For example, Lite version and a pro version which signify free and paid versions with extra features in paid one.

With the arrival of gradle as a build tool, we got gradle flavors which is an excellent way to have some variations in your app versions.

It can also be leveraged to do hermetic testing with prod/mock flavors. Some other examples could be free/paid flavors and stable/experimental flavors etc.

Now let’s talk about on how to proceed :

The process is very simple

  1. Create the flavors in you build.gradle file
android {  
      productFlavors {  
           mock {   
                applicationIdSuffix = ".mock"  
           }  
           prod  
      }  
 }

Here prod and mock are two flavors and the customisations we add are that mock has a applicationIdSuffix. We can also add altogether different applicationIds as well as define a different version for different flavors and a lot of other different things

2. Now we sync the project

If you now open Build Variants tool window either using the quick access menu located in the status bar in the bottom left hand corner of the Android Studio main window or using the Build Variant tool window bar. Once loaded, clicking in the Build Variant cell for the app module should now list the four build variants: mockDebug, mockRelease and prodDebug, prodRelease

After this we get different folders corresponding to the different flavors. We go ahead and add the different folders where we want the changes to occur. For example if I want to show two different maps for two different versions, where one version is for fdroid and another one is for googleplay and hence we replace google maps with OpenStreet Maps in the fdroid version. Hence, We add res/values and res/layout folders for different resources and we also add different java folders and add the classes that would be different for both the flavors. Since both of these will require different set of permissions we can also add AndroidManifest.xml for these were we define the permissions etc. we want.

Not that we only add the changes for each file. All the other essential things can be added from the Main flavor of the android project.

Any variable available through your code

Another thing to know is that the you can have add variables like this so that you can use different variables for different flavors. For examle if you have different api’s and different options for each flavor like if you want to report crashes in one of the flavors and not in other. Here HOST variable is not the only one you can expose in your code. You can do it with whatever you want:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

You can access them as follows:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES

So I think this is a really useful thing to know and is used in almost every app. We all need some configuration differences in our apps and gradle flavors is the best way to go about it. Go nuts with your imagination on the usage of flavors 😛

Adios!

Lambda expressions in Android

What are Lambda expressions

Lambda Expressions are one of the most important features added to Java 8. Prior to Lambda Expressions, implementing functional interfaces i.e interfaces with only one abstract method has been done using syntax that has a lot of boilerplate code in it.
In cases like this, what we are trying to do is pass a functionality as an argument to a method, such as what happens when a button is clicked.

Lambda expressions enables you to do just that, in a way that is much more compact and clear.

Syntax of Lambda Expressions

A lambda expression consist of the following:

  • A comma separated list of formal parameters enclosed in parentheses. The data types of the parameters in a lambda expression can be omitted. Also the parenthesis can be omitted if there is only one parameter. For example:
TextView tView = (TextView) findViewById(R.id.tView);
tView.setOnLongClickListener(v -> System.out.println("Testing Long Click"));
  • The arrow token ->
  • A body which contains a single expression or a statement block. If a single expression is specified, the java runtime evaluates the expression and then return its value. To specify a statement block, enclose statements in curly braces "{}"

Lambda Expressions in Android

To use Lambda Expressions and other Java 8 features in Android, you need to use the Jack tool-chain. Open your module level build.gradle file and add the following:

android {
  ...
  defaultConfig {
    ...
    jackOptions {
      enabled true
    }
  }
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
}

Sync your build.gradle file and if you are having any issue with build tools, you may need to update buildToolsVersion in your build.gradle file to "24rc4" or just download the latest Android SDK Build-tools from the SDK Manager, under the Tools (Preview channel).

Example

Adding a click listener to a button

without lambda expression

Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Toast.makeText(this, "Button clicked", Toast.LENGTH_LONG).show();
    }
}););

with lambda expressions It is as simple as:

Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(v -> Toast.makeText(this, "Button clicked", Toast.LENGTH_LONG).show(););

As we can see above, using lambda expressions makes implementing a functional interface clearer and compact. Standard functional interfaces can be found in the java.util.function package [included in Java 8]. These interfaces can be used as target types for lambda expressions and method references.

Another way to have Java 8 features in your Android app is using the RetroLambda plugin.

Using the new Awareness API

Google released the new Awareness API for everyone, a suite of signals concurring to gives the developer the context in which our user is, all as part of the Play Services 9.2.0, already available on Android devices around the globe.

This library combines 7 different feeds managing both the battery drain and the used memory, providing us with data such as user location, weather, time, headphones connection status, places, nearby beacons and currently performing activity.

INTRO

The Awareness API comes with two different versions, one is a callback based one (Fence API) and the other one is a polling based one (Snapshot API): while the first one will notify us when the conditions we specified are met, the second expects us to query the suite for the data from a unified interface.

There are 7 kinds of context that we can work with in Awareness API such as

Time — Local time at current user’s location

Location — Latitude/Longitude

Place — Places around user

Activity — Detected user activity (biking, walking, running, etc.)

Beacons — Check nearby beacon(s)

Headphones — Are headphones plugged in?

Weather — Current weather conditions

Now the Two set’s of API’s :

  • Snapshot API — Allows you to “request an information based on user’s context” as listed above.
  • Fence API — Allows you to “receive a signal when user’s context has changed and reaches the condition” through callback function, for example, if user moves closed to the specific coordinate with headphones plugged in, Fench API will call the registered BroadcastReceiver and let you do your job.

Getting started

  1. Create a project in https://console.developers.google.com (or in case you already have one, you can use it instead)
  2. And then browse to API Manager page and search for Awareness and click at Awareness API and Click Enable and wait until it finishes enabling

3. Go to Credentials tab and click at Create credentials -> API key -> Android key. Enter the name you project, for example, Android key and click Create (or if you have already created Android key previously, you could skip this step and use the existed one)

4. Add dependency in build.gradle

compile 'com.google.android.gms:play-services-contextmanager:9.2.0'

5. Open AndroidManifest.xml file and add meta-data to <application> tag like this:

        <meta-data
            android:name="com.google.android.awareness.API_KEY"
            android:value="YOUR_KEY" />
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="YOUR_KEY" />
        <meta-data
            android:name="com.google.android.nearby.messages.API_KEY"
            android:value="YOUR_KEY" />

We also need to add permissions for this:

6. Open AndroidManifest.xml

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

Now we come to the actual java code. open your activity and initialise the GoogleApiClient in onCreate()

private GoogleApiClient mGoogleApiClient;

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

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(Awareness.API)
                .build();
        mGoogleApiClient.connect();
    }

So this ends the setup part. Now we move on to the actual data retrieval from the API.

Accessing Snapshot API

Awareness.SnapshotApi.getDetectedActivity(mGoogleApiClient)
                .setResultCallback(new ResultCallback<DetectedActivityResult>() {
                    @Override
                    public void onResult(@NonNull DetectedActivityResult detectedActivityResult) {
                        if (!detectedActivityResult.getStatus().isSuccess()) {
                            Log.e(TAG, "Could not get the current activity.");
                            return;
                        }
                        ActivityRecognitionResult ar = detectedActivityResult.getActivityRecognitionResult();
                        DetectedActivity probableActivity = ar.getMostProbableActivity();
                        Log.i(TAG, probableActivity.toString());
                    }
                });

This will return the most probable activity done by the user. We can get a particular activity like walking, running, driving as well by integers defined below

    public static final int IN_VEHICLE = 0;
    public static final int ON_BICYCLE = 1;
    public static final int ON_FOOT = 2;
    public static final int STILL = 3;
    public static final int UNKNOWN = 4;
    public static final int TILTING = 5;
    public static final int WALKING = 7;
    public static final int RUNNING = 8;

Similarly we can also get Headphone state, location, weather, beacon etc.

For example let’s see headphone state:

Awareness.SnapshotApi.getHeadphoneState(mGoogleApiClient)
                .setResultCallback(new ResultCallback<HeadphoneStateResult>() {
                    @Override
                    public void onResult(@NonNull HeadphoneStateResult headphoneStateResult) {
                        if (!headphoneStateResult.getStatus().isSuccess()) {
                            Log.e(TAG, "Could not get headphone state.");
                            return;
                        }
                        HeadphoneState headphoneState = headphoneStateResult.getHeadphoneState();
                        if (headphoneState.getState() == HeadphoneState.PLUGGED_IN) {
                            Log.i(TAG, "Headphones are plugged in.\n");
                        } else {
                            Log.i(TAG, "Headphones are NOT plugged in.\n");
                        }
                    }
                });

This is same as acquiring activity and headphoneState.getState() will give you if it is plugged in or not

Now we take a look at the Fence API

Fence is similar to the geofence but in addition to geofence, the fence API also allows us to set awareness conditions and check if both conditions are true.

We use a BroadcastReceiver

private static final String FENCE_RECEIVER_ACTION = "FENCE_RECEIVE";

    private HeadphoneFenceBroadcastReceiver fenceReceiver;
    private PendingIntent mFencePendingIntent;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...

        fenceReceiver = new HeadphoneFenceBroadcastReceiver();
        Intent intent = new Intent(FENCE_RECEIVER_ACTION);
        mFencePendingIntent = PendingIntent.getBroadcast(MainActivity.this,
                10001,
                intent,
                0);
    }

    private void registerFences() {
        // Create a fence.
    }

    private void unregisterFence() {
    }

    @Override
    protected void onStart() {
        super.onStart();
        registerFences();
        registerReceiver(fenceReceiver, new IntentFilter(FENCE_RECEIVER_ACTION));
    }

    @Override
    protected void onStop() {
        super.onStop();
        unregisterFences();
        unregisterReceiver(fenceReceiver);
    }

    class HeadphoneFenceBroadcastReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

        }

    }

Here when we get message in onReceive() and we can detect if headphone is connected or not. Something like this

FenceState fenceState = FenceState.extract(intent);

            Log.d(TAG, "Fence Receiver Received");

            if (TextUtils.equals(fenceState.getFenceKey(), "headphoneFenceKey")) {
                switch (fenceState.getCurrentState()) {
                    case FenceState.TRUE:
                        Log.i(TAG, "Fence > Headphones are plugged in.");
                        break;
                    case FenceState.FALSE:
                        Log.i(TAG, "Fence > Headphones are NOT plugged in.");
                        break;
                    case FenceState.UNKNOWN:
                        Log.i(TAG, "Fence > The headphone fence is in an unknown state.");
                        break;
                }
            }

So as you can see this is pretty straight forward and very useful. You can build so many apps with multiple fences. You can think of a lot of usecases for this and make a lot of amazing apps. Happy Tinkering with the Awareness API!

Bottoms sheets in android

Hey Guys I recently used Bottom sheets, so that I should write a blog about it because I don’t see a lot of developers using this in their app UI’s.

It’s a very interesting UI element. A Bottom Sheet is a sheet of material that slides up from the bottom edge of the screen. Displayed only as a result of a user-initiated action, and can be swiped up to reveal additional content. It can be a temporary modal surface or a persistent structural element of an app.

This component was introduced in the Android Design Support library 23.2. Many apps like Google Maps use the bottom sheet, in which a sliding window pops up from the bottom of the screen. Also the Google play music app uses. When we drag up the sheet we see the song details as well as the current playlist.

Usage of expanded and collapsed Bottom sheets in Android

There are 3 states of Bottom sheets :-

  • Expanded — When the sheet is completely visible.
  • Collapsed — When the sheet is partially visible.
  • Hidden — When the sheet is completely hidden.

Step 1 is we need to import the latest design support library. Put this line in your app level build.gradle file.

compile ‘com.android.support:design:23.2.0’

Then one should create a new Blank Activity (not Empty Activity) in Android Studio. It sets up the CoordinatorLayout by default.

So now there ate two layouts created by default namely activity_main.xml and content_main.xml.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="yet.best.bottomsheets.MainActivity"
tools:showIn="@layout/activity_main">

<Button
android:id="@+id/open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:text="Open Bottom Sheet" />

<Button
android:id="@+id/collapse"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/open"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:text="Collapse Bottom Sheet" />

<Button
android:id="@+id/hide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/collapse"
android:layout_centerHorizontal="true"
android:layout_margin="5dp"
android:text="Hide Bottom Sheet" />

</RelativeLayout>

Notice that 3 Buttons have been created in this layout to perform different actions with the bottom sheets.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="yet.best.bottomsheets.MainActivity">

<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">

<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />

</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_main" />

<include
android:id="@+id/bottom_sheet"
layout="@layout/bottomsheet_main" />

<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email"
app:layout_anchor="@id/bottom_sheet"
app:layout_anchorGravity="top|right|end" />

</android.support.design.widget.CoordinatorLayout>

Those who aren’t familiar with the coordinator layout — basically there is a base level layout activity_main which contains the FloatingButton and within this layout including content_main.xml which will contain the rest of the layout. Notice that one also has to include bottomsheet_main.xml. This layout contains our bottom sheet layout which will be created next.

Create a new layout file called bottomsheet_main.xml

bottomsheet_main.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#d3d3d3"
app:behavior_hideable="true"
app:behavior_peekHeight="70dp"
app:layout_behavior="@string/bottom_sheet_behavior">

<TextView
android:id="@+id/heading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="7dp"
android:text="Collapsed"
android:textSize="18sp" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Main Content"
android:textSize="20sp" />

</RelativeLayout>

This layout is how our bottom sheet will actually look. You can design it as you want.

Now for the actual java code. This is the easiest part actually. Just set listeners to the 3 buttons created and perform the corresponding action with the bottom sheet.

public class MainActivity extends AppCompatActivity {

BottomSheetBehavior bottomSheetBehavior;
Button open, collapse, hide;
TextView heading;
@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
  setSupportActionBar(toolbar);

View bottomSheet = findViewById(R.id.bottom_sheet);
  bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);

setup();
 }

private void setup() {
  open = (Button) findViewById(R.id.open);
  collapse = (Button) findViewById(R.id.collapse);
  hide = (Button) findViewById(R.id.hide);
  heading = (TextView) findViewById(R.id.heading);

//Handling movement of bottom sheets from buttons
  open.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
    heading.setText("Welcome");
    heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorPrimary));
   }
  });

collapse.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
    heading.setText("Collapsed");
    heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorAccent));
   }
  });

hide.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view) {
    bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
   }
  });

//Handling movement of bottom sheets from sliding
  bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
   @Override
   public void onStateChanged(View bottomSheet, int newState) {
    if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
     heading.setText("Collapsed");
     heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorAccent));
    } else if (newState == BottomSheetBehavior.STATE_EXPANDED) {
     heading.setText("Welcome");
     heading.setTextColor(ContextCompat.getColor(MainActivity.this, R.color.colorPrimary));
    }
   }

@Override
   public void onSlide(View bottomSheet, float slideOffset) {}
  });
 }
}

We just use the bottomSheetBehavior.setState() method to set the relevant state on each button click.

The bottom sheets can also be dragged by touch gestures. One can simply touch the sheet and drag them up or down. For these touch gestures to be handled one has to implement the onStateChanged() listener. This listener is fired everytime the state of the sheet changes by gestures. Whenever this triggers it checks the state of the bottom sheet and again do the desired action which user would have done by the button clicks.

As you can see, this is a pretty neat UI solution and can be implemented so easily. Go try it out for yourself. Adios!