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!

Working with ConstraintLayout in Android

Few months ago, during the Google I/O conference, Google introduced a new set of tools for Android developers. Among them is a new Layout editor and a new layout called the ConstraintLayout.

I’ll be highlighting the key points in this brand new layout.

ConstraintLayout is available in a new Support Library that’s compatible with Android 2.3 (Gingerbread) and higher, but the new layout editor is available only in Android Studio 2.2 Preview.

Layout Editor & Constraints Overview.

The new layout editor in Android Studio 2.2 Preview is specially built for the ConstraintLayout. You can specify the constraints manually, or automatically reference within the layout editor.

Overview of Constraints?

A constraint is the description of how a view should be positioned relative to other items, in a layout. A constraint is typically defined for one or more sides by connecting the view to:

  • An anchor point, or another view,
  • An edge of the layout,
  • Or An invisible guide line.

Since each view within the layout is defined by associations to other views within the layout, it’s easier to achieve flat hierarchy for complex layouts.

In principle, the ConstraintLayout works very similar to the RelativeLayout, but uses various handles (or say anchors) for the constraints.

  • Resize handle. The resize handle is the alt text seen in the corners of the figure above, and it’s used to resize the view.
  • Side handle. The side handle is the alt text in the figure above, and it’s used to specify the location of a widget. E.g using the left side handle to always be aligned to the right of another view, or the left of the ConstraintLayout itself.
  • Baseline handle. The baseline handle is the alt text in the figure above. It is used to align the text of a view by the baseline of the text on another view.

Getting started with ConstraintLayout

Setup

Ensure that you’re running the AS 2.2 preview, and Android Support Repository version 32 or higher, it’s required before you can use the ConstraintLayout. Let’s get started.

  • First, you need to add the constraint layout library to your app’s dependencies within your build.gradle file:
dependencies {
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha1'
}
  • Sync your project.

Add Constraints

There are typically two ways to create ConstraintLayout in AS. You can create a new XML layout and select the root element to be a ConstraintLayout or convert an existing layout into a ConstraintLayout as shown in the image below:

Once you have the ConstraintLayout setup, what is next is to add the constraints to the views within that layout.

As an example, drag an ImageView to the layout. The new layout builder will immediately ask to add a drawable or resource, select one from the options and press ok. Also drag a TextView unto the layout.

To create a constraint, drag the top side handle on the ImageView to the top of the ConstraintLayout. You can also drag from the top side handle of the TextView to the bottom handle of the ImageView

Using the Inspector Pane

Now that we’re able to add constraints, we will need to use the inspector. It’s on the right hand side of the layout builder and it lists various properties of the selected widget. Typically, it looks as shown below:

You can use the sliders to move the view by percentage along the x and y axes. You can also control the dimensions of the view from the inspector pane, by altering the values corresponding to the layout_width and layout_height fields.

Taking a closer look at the square widget on the inspector pane. It contains some more control over the dimensions of the views.

There are other modes of controlling the size of the view. Clicking on the inner lines in the image above help you cycle through the other modes.

  • Fixed mode: alt text This allows you specify the width and height of the view.
  • Any size: alt text This mode allows the image to fill up all the space required to fulfill that constraint. You can look at this like “match constraint”
  • Wrap content: alt text This just expands to fill the content of the view. E.g text or image

Using Auto-connect to add constraints.

Autoconnect as the name suggests, automatically creates connections between views/widgets. It tries to create connections to neighboring views/widgets.
To enable autoconnect, look out for the alt text icon on the top bar of the layout editor.

Thats’s almost it for the constraint layout.
If you want, you can head over to the official documentation on it at http://tools.android.com/tech-docs/layout-editor

Cheers.

Handling data in android

So this week I was working with getting some data from the sqlite database in android and someone who was a beginner in android also asked me to help him with the same. I asked him what he knew and he said that even after reading up a lot he wasn’t able to figure out what exactly to do with the data he wants to save and use in his app. I have seen that this is a problem with a lot of people starting to develop android apps. So, I decided to write a blog on how can you handle your data in your android apps.

Most of the android apps need to save data even if only to save some user preferences. So primarily there are 3 ways to save your data :

  1. In form of key values (SharedPreferences)
  2. Reading/Writing to files
  3. Writing to a database

So let’s go step by step. When we need to store just some preferences of the users like if they want notifications or what kind of theme they want : light or dark etc. So basically if we want to store a key value in the persitant storage of the app we can do that using SharedPreferences. To use sharedpreferences, we initialise the sharedpreference object like

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);

in oncreate and cache it. Then we just need to add or retrieve what we want using this cached SharedPreferences object. To Add a key value pair :

sharedPreferences.edit().putString("someKey", "someValue").apply();

Also you can put all kinds of stuff here. For example right now we added a string with key “someKey” and Value “someValue”. We can also add Booleans, Floats, Ints, Longs, StringSets etc.

To retrieve the same value we do something like this

sharedPreferences.getString("someKey", "DefaultValue");

Now if you want some logs or some values that can be exported and sent to your server, you should write them to files and maybe read some json inputs etc. as well.

Basically android has a File system similar to other platforms. All android devices have two file storage areas : “Internal” and “external” storage. The difference between them is as follows :

Internal :

  • Always available
  • Files saved here are accesible by only your app
  • When user uninstalls the app, system removes all your app’s files from internal storage

Best to use this when you want to be sure that neither the user nor the other app’s can access your files

External :

  • It’s not always available because user can mount external storage as USB storage and remove it as well
  • It’s readable by anything(Other apps, users etc.)
  • When you uninstall, system removes your app’s files from here only if you save them in the directory from getExternalFilesDir()

Now to read and write files, you need extra permissions

  • android.permission.WRITE_EXTERNAL_STORAGE
  • android.permission.READ_EXTERNAL_STORAGE

So now let’s get down to it. How do I save and read files in my app?

You first initialise the File object

File file = new File(context.getFilesDir(), filename);

This will create a file with filename in the internal storage. For external storage

first check if the storage is available, then just create a file using getExternalStoragePublicDirectory

File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;

This is for writing public files.

Now we move onto the most used part in an android app which is a database.Android has a built in SQLite database package which helps us in writing databases in files with syntax similar to SQL.

You need to add 2 classes which are mandatory and another class which basically helps you get organised. So the first is a Contract. This is where you actually write statements that will be executed later on to initialise or create the tables we want. For this make an a static abstract inner class that implements BaseColums like

public static abstract class Microlocation implements BaseColumns {
    public static final String TABLE_NAME = "microlocation";

    public static final String ID = "id";

    public static final String NAME = "name";

    public static final String LATITUDE = "latitude";

    public static final String LONGITUDE = "longitude";

    public static final String FLOOR = "floor";

    public static final String[] FULL_PROJECTION = {
            ID,
            NAME,
            LATITUDE,
            LONGITUDE,
            FLOOR

    };

    public static final String CREATE_TABLE =
            "CREATE TABLE " + TABLE_NAME
                    + " ("
                    + ID + INT_TYPE + PRIMARY_KEY + COMMA_SEP
                    + NAME + TEXT_TYPE + COMMA_SEP
                    + LATITUDE + REAL_TYPE + COMMA_SEP
                    + LONGITUDE + REAL_TYPE + COMMA_SEP
                    + FLOOR + INT_TYPE
                    + " );";

    public static final String DELETE_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME;


}

Here we are making static final Strings for column names and then creating a static final String CREATE_TABLE which is basically a statement that creates the table Microlocation with the specified key, columns, data types etc.

After adding this structure for all the tables we want to have in our database, we move on to adding a DbHelper class that extends SQLiteOpenHelper which basically has two Abstract methods called onCreate(SQLiteDatabase db) and onUpgrade(SQLiteDatabase db) which are called when the database is created and database version is changed respectively. We call all our CREATE_TABLE static Strings in onCreate which in turn creates all the tables. Something like this :

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(DbContract.Speakers.CREATE_TABLE);
    db.execSQL(DbContract.Sponsors.CREATE_TABLE);
    db.execSQL(DbContract.Sessions.CREATE_TABLE);
    db.execSQL(DbContract.Tracks.CREATE_TABLE);
    db.execSQL(DbContract.Sessionsspeakers.CREATE_TABLE);
    db.execSQL(DbContract.Event.CREATE_TABLE);
    db.execSQL(DbContract.Microlocation.CREATE_TABLE);
    db.execSQL(DbContract.Versions.CREATE_TABLE);
    db.execSQL(DbContract.Bookmarks.CREATE_TABLE);
    db.execSQL(DbContract.EventDates.CREATE_TABLE);
}

You can also call DELETE_TABLE Strings in onUpgrade and the call onCreate again if you like but it’s not compulsory.

Now that you’re database is initialised, let’s add some records into it. For example I have to add a new Micrlocation I’d create a method in my data model where I’ll add a basic structure for the query and then format it with the values for a particular object of the model. Something, like this

public String generateSql() {
    String insertQuery = "INSERT INTO %s VALUES ('%d', %s, '%f', '%f', '%d');";
    return String.format(Locale.ENGLISH,
            insertQuery,
            DbContract.Microlocation.TABLE_NAME,
            id,
            DatabaseUtils.sqlEscapeString(StringUtils.optionalString(name)),
            latitude,
            longitude,
            floor);
}

and then I’d execute the string returned by the call

String query = model.generateSql();

by this

public void insertQuery(String query, DbHelper mDbHelper) {
    SQLiteDatabase db = mDbHelper.getWritableDatabase();
    db.beginTransaction();
    db.execSQL(query);
  
    db.setTransactionSuccessful();
    db.endTransaction();
}

Where db is just a SQLiteDatabase instance.

Now that we have records we want to retrieve them according to usage and for that we create helper methods. This is an example of the retrieving all the microlocations added to the database in ASCENDING order of NAME

public ArrayList<org.fossasia.openevent.data.Microlocation> getMicrolocationsList(SQLiteDatabase mDb) {
    String sortOrder = DbContract.Microlocation.NAME + ASCENDING;
    Cursor cursor = mDb.query(
            DbContract.Microlocation.TABLE_NAME,
            DbContract.Microlocation.FULL_PROJECTION,
            null,
            null,
            null,
            null,
            sortOrder
    );

    ArrayList<org.fossasia.openevent.data.Microlocation> microlocations = new ArrayList<>();
    org.fossasia.openevent.data.Microlocation microlocation;

    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
        microlocation = new org.fossasia.openevent.data.Microlocation(
                cursor.getInt(cursor.getColumnIndex(DbContract.Microlocation.ID)),
                cursor.getString(cursor.getColumnIndex(DbContract.Microlocation.NAME)),
                cursor.getFloat(cursor.getColumnIndex(DbContract.Microlocation.LATITUDE)),
                cursor.getFloat(cursor.getColumnIndex(DbContract.Microlocation.LONGITUDE)),
                cursor.getInt(cursor.getColumnIndex(DbContract.Microlocation.FLOOR))
        );
        microlocations.add(microlocation);
        cursor.moveToNext();
    }
    cursor.close();
    return microlocations;
}

First we create a cursor and then just iterate of the cursor to get microlocation objects and add them to an Arralist and return the Arraylist to the calling method.

So This are most of the things that are there to handling data in Android. Should be sufficient to get you started.

Sorry for the long post but the content couldn’t be made any smaller but I hope you gain something from this post. You can checkout implementations I have followed for the Open event project in the github repo https://github.com/fossasia/open-event-android. You can also write to me anytime on FB, Twitter, Email etc. and I’ll be happy to answer any queries. Adios!

References : 1) developers.android.com

2) https://github.com/fossasia/open-event-android