Android Studio Gradle Build

Ongoing work with Android Studio. I had a library project + 1 pro project + 1 free project and it was very cumbersome in Eclipse. With gradle it becomes 1 project with two flavors. The main concepts are appended. This is a developing post and this is my fourth edit. I will refine it further as I get more insights.

Project Structure

D:\
repository/                       //Custom Maven repository
MyExtLib/                         //External Lib Project
MyProject/                        //Project Root

   MyModule/
      libs/
         myJar.jar               //Lib as a jar in libs folder
      src/
         main/
            java/                //Common Classes
            res/                 //Common res
            AndroidManifest.xml
         proflavor/
            java/
            res/
            AndroidManifest.xml
         freeflavor/
            java/
            res/
            AndroidManifest.xml
      proguard-project.txt
      xxyy.keystore
      build.gradle               //Module level gradle build file

   MyLib/
      src/
         main/
            java/
            res/
            AndroidManifest.xml
      build.gradle             //Lib Module gradle build file

   settings.gradle
   build.gradle                //Top level gradle build file

Top level settings.gradle file

include ':MyModule'

Common module level build.gradle file

apply plugin: 'com.android.application'
android {
    compileSdkVersion 22
    buildToolsVersion '22.0.1'

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1625
        versionName '9.2.5'
    }

    signingConfigs {
        myConfig {
            storeFile file('../xxyy.keystore')
            storePassword "*********"
            keyAlias "*******"
            keyPassword "********"
        }
    }

    buildTypes {
        debug {
        }
        release {
            zipAlignEnabled true
            signingConfig signingConfigs.myConfig
            minifyEnabled true
            proguardFile getDefaultProguardFile('proguard-android.txt')
        }
    }

    productFlavors {
        freeflavor {
            applicationId 'com.cls.xxx'
            proguardFile('freeflavorproguard.txt')
        }
        proflavor {
            applicationId 'com.cls.xxxpro'
            proguardFile('proflavorproguard.txt')
        }
    }
}

dependencies {
    compile 'com.android.support:support-v13:21.0.0'
    compile 'com.android.support:appcompat-v7:22.1.1'
}

You can have four approaches to libraries:-
1. Local Library Module
2. Library jar in /libs folder
3. External Library Project
4. Library jar in custom local maven repository

Approach 1 – Local Library Module

#Insert this line into the top level settings file

include ':MyLib'

#Insert this line into the module level gradle file

dependencies {
   compile project(':MyLib')
}

Approach 2 – Library jar in /libs folder

#Insert this line into the module level gradle file
dependencies {
   compile files('libs/myJar.jar')
}

Approach 3 – External library project

#Insert this line into the top level settings file

include ':MyExtLib'
project(':MyExtLib').projectDir = new File('D:/MyExtLib')

#Insert this line into the module level gradle file

dependencies {
   compile project(':ExtMyLib')
}

Approach 4 – Custom Local Maven Repository

Step 1 upload jars into maven

Install maven from maven.org
Issue maven command for uploading your jars which are not available in mavenCental to a custom local maven repository d:\repository

D:\store\Applications\apache-maven-3.1.0\bin>mvn install:install-file
-Dfile=d:\My\ Project/My\ Module/libs/myJar.jar
-DgroupId=com.cls.mycustomjar
-DartifactId=mycustomjar
-Dversion=1.0.0
-Dpackaging=jar
-DgeneratePom=true
-DlocalRepositoryPath=d:\repository

Step 2 upload library projects as aar into maven

//build.gradle for Mylib
//uploadArchives task
apply plugin: 'com.android.library'
...
apply plugin: 'maven'
group = 'com.cls.mylibrary'
version = '1.0.0'
uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: uri("d:/repository"))
        }
    }
}

Execute gradle tasks to upload library projects as aar into the custom rep

//In mylibrary project execute task
gradlew uploadArchives

Step 3

OK Now your jars and libraries are ready to be pulled from your local custom repository.

//build.gradle for module
//Insert repository code into the module level build.gradle file

repositories {
    maven { url uri("d:/repository") }
}

dependencies {
    compile 'com.cls.mycustomjar:mycustomjar:1.0.0'
    compile 'com.cls.mylibrary:mylibrary:1.0.0'
}

GPS Programming Insights

Here is a quick insight into GPS programming for Android. It is by no means a complete reference but it may be able to give you the missing perception you are looking for.

GPS Primer

The location in GPS is calculated by measuring the distance from orbitting satellites and doing a process called triliteration which is similar to triangulation. GPS calculates the distances by measuring the time it took for the satellite signal to reach the receiver. So distance = travel time t x 300000 km/sec. The satellites broadcast their own position through data sets called Almanac(Imprecise data) and Ephemeris(Precise data)
So in theory the receiver can solve for position using the three satellites and the distances from them.
As is evident the travel time can only be calculated with any degree of accuracy if the receiver clock is perfectly in sync with the satellites. The satellites transmit a Pseudo Random Code which is also known to and repeated by the receivers. If the clocks are in sync then this code will be perfectly in step. However due to inaccuracies in receiver clock the code will be quiet off. The secret to GPS is that using this inaccurate clock the receiver solves for position using the three satellites and then uses a fourth satellite to eliminate the sync error. Thus 4 satellites are required to get a position fix.

Android LocationManager

The code to start listening to location updates in your Activity is:-

// Comment

public class GpsActivity extends Activity implements GpsStatus.Listener, LocationListener{
   //...
   @Override
   protected void onResume() {
      LocationManager lm=(LocationManager) getSystemService(Context.LOCATION_SERVICE);
      lm.addGpsStatusListener(this);
      lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, mintime, mindistance,this);
   }

   @Override
   public void onPause(){
      lm.removeGpsStatusListener(this);
      lm.removeUpdates(this);
   }
   @Override
   public void onGpsStatusChanged(int event) {
      switch(event){
         case GpsStatus.GPS_EVENT_FIRST_FIX:
         break;
      case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
         break;
      case GpsStatus.GPS_EVENT_STARTED:
         break;
      case GpsStatus.GPS_EVENT_STOPPED:
         break;
      }
   }

   @Override
   public void onLocationChanged(Location loc) {
   }

   @Override
   public void onProviderDisabled(String provider) {
   }

   @Override
   public void onProviderEnabled(String provider) {
   }

   @Override
   public void onStatusChanged(String provider, int status, Bundle extras) {
   }
}

The GPS engine starts when the requestlocationupdates( ) is called. This engine is power hungry and guzzles power from EVENT START to EVENT STOP received in GPS status listener. The engine needs to download almanac and ephemeris and solve for multiple satellites to get location fixes so be very careful how long it runs. Commercial GPS receiver have dedicated multiple channels for simultaneous downloading and processing satellites almanac and ephemeris data. So solution emerges faster. How many channels do mobile GPS sets have ? If it is single channel it will be slow to converge location fix.
The validity of almanac data or orbital data of satellites is an imprecise long term data which typically remains valid for months. The ephemeris on the other hand is precise data and short term and the platform may invalid it in terms of a few hours. The ephemeris of each satellite is repeated in 30 sec intervals consisting of 3 packets with 6 sec blocks. So if 4 satellites are available and snr is > 28 dB and at least 30 sec for collecting ephemeris of one satellite. For four satellites 4*30=120 secs in single channel. For 4 channel rx it is only 30 sec. While collecting ephemeris if platform is moving then this critical process may need to be repeated until success.

So a lot of processing is to be done by GPS engine for giving location fix which has power implication.

There are some things to keep in mind while programming :-

If update rate is more than 3 secs for requestlocationupdates( ) then engine then will not work continuously. To conserve power at slower update rates a STOP_EVENT will start coming in between followed by a START_EVENT when the update interval comes.
When engine is ON wakelock is held. This also means that when you keep updaterate at <=3 secs no sleep for device.
The engine will call ongpsstatuschanged( ) listener when
1. FIRST FIX – First time location fix
2. EVENT STARTED – When Engine starts
3. EVENT STOPPED – When Engine stops
4. SAT STATUS – When the satellite data changes.
The SAT STATUS is a high throughput call and mutiple calls come so throttle your processing here if any. Here you can retreive an iterable satellite list through a gpsstat object and get loads of information like prn no , validity of ephemeris etc.
The engine will cal onlocationchanged( ) when solution is available at the rate passed to requestlocationupdates ( ). Here you can retreive location and other stuff like accuracy.
This is an ongoing article and I will update this as I get more insights.

Android Studio and AndEngine Setup

My efforts at setting up AndEngine with Android Studio. Hope it benefits somebody. Without much ado here it is:-

Steps

1. Clone AndEngine and AndEnginePhysicsBox2DExtension

C:\ProjectDir>git clone https://github.com/nicolasgramlich/AndEngine.git andEngine
C:\ProjectDir>git clone https://github.com/nicolasgramlich/AndEnginePhysicsBox2DExtension.git andEnginePhysicsBox2DExtension

2. Checkout AnchorCenter Branch

C:\ProjectDir\andEngine>git checkout -b GLES2-AnchorCenter origin/GLES2-AnchorCenter
C:\ProjectDir\andEnginePhysicsBox2DExtension>git checkout -b GLES2-AnchorCenter origin/GLES2-AnchorCenter

  • Now you have github link and anytime you need to update the AndEngine repositories just do git pull.
  • The ET is the project module. andEngine and andEnginePhysicsBox2DExtension are the two library modules.
  • The build.gradle in the two library modules is customized to cater for the default Eclipse sourcesets structure received from the github repository.

3. build.gradle[andEngine]

apply plugin: 'com.android.library'

android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "org.andengine"
        minSdkVersion 15
        targetSdkVersion 19

        ndk {
            moduleName "andengine_shared"
        }
    }
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jni.srcDirs = [] // This needs to be done so that gradle does not know about your jni. There are errors otherwise
            jniLibs.srcDirs = ['libs']
        }
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

4. build.gradle [andEnginePhysicsBox2DExtension]

apply plugin: 'com.android.library'
android {
    compileSdkVersion 19
    buildToolsVersion "20.0.0"

    defaultConfig {
        applicationId "org.andengine.extension.physics.box2d"
        minSdkVersion 15
        targetSdkVersion 19

        ndk {
            moduleName "andenginephysicsbox2dextension"
        }
    }
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jni.srcDirs = []
            jniLibs.srcDirs = ['libs']
        }
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}
dependencies {
    compile project(':andEngine')
}

5. build.gradle [ET Module]

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion '20.0.0'

    defaultConfig {
        applicationId "com.cls.etxxxxx"
        minSdkVersion 15
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile project(':andEngine')
    compile project(':andEnginePhysicsBox2DExtension')
}

6. settings.gradle [Project]

include ':ET', ':andEngine', ':andEnginePhysicsBox2DExtension'

7. ndk-build

Now to compile the jni part. Add this line to Application.mk located inside jni folders of both the andEngine library modules.

APP_PLATFORM:=android-19

Now give the command inside the jni folders:-

C:....../jni>ndk-build

This will compile the .so and put them in the libs folders of the modules

8. gradle sync and build

Thats all. Hopefully it should build for you :-).

Edits

Added github integration

External Drive Backup – Linux Mint

If you want to periodically back up your data to an external drive then you have to do a few things:-

1. Set up the /etc/fstab file to auto mount drive on boot up
2. Make a shell script to do the actual backup [rsync]
3. Copy this shell script into cron.daily, cron.weekly ….

Thats all. Easy.

Being a Linux noob I found it easier said than done. Based on a few days of trial and error I have written this guide for noobs.

Auto Mount

Setting Auto Mount using Menu->Settings->Disks [gnome-disks] did not work for me. By default it uses a weird mount point with UUID names and rsync stalled with permission errors mostly related to FAT file system. So I resorted to manually editing the /etc/fstab file.

Open /etc/fstab

gksudo leafpad /etc/fstab

The UUID is obtained by

sudo blkid

This is the line to be inserted at the end of fstab.

#My card reader
UUID=3365-3564 /mnt/CARDBK vfat user,umask=000,utf8 0 0

Shell Script

#!/bin/sh
SRC=/home/lakshman/Critical
DST=/mnt/CARDBK/
LOG=/home/lakshman/cronlog.txt
echo "Critical" >> $LOG
echo `date` >> /home/lakshman/cronlog.txt 2>>$LOG
if [ -d $DST ]
then rsync -rlt --delete $SRC $DST 2>>$LOG
else echo "$DST does not exist" >> $LOG
fi

The rsync flags are important. If the -a option is used which is shorthand for -rlptgoD then it will choke on FAT file systems since FAT does not support permissions and such.

Now copy this script into /etc/cron.daily

sudo cp ~/myscript.sh /etc/cron.daily

Cron

Cron can be set up by user using crontab -e command. But cron will miss cycles if the machine is Off at the cycle time. So I had to use system cron setup. The script in cron.daily is run by the cron daemon as per the /etc/crontab. This is the default crontab file

# m h dom mon dow user command
17 * * * * root run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

If the script is copied into /etc/cron.daily all the scripts in cron.daily will be run at 6.25 AM. But if the machine is not on at 6.25 AM then this cycle will be missed. Thats where anacron helps. anacron uses the /etc/anacrontab file. This is the default /etc/anacrontab file

# These replace cron's entries
1 5 cron.daily run-parts --report /etc/cron.daily
7 10 cron.weekly run-parts --report /etc/cron.weekly
@monthly 15 cron.monthly run-parts --report /etc/cron.monthly

It appears that anacron will take over the cron job and you dont have to do ANYTHING except copying the script into the cron.daily

This works for me. Now I have automated daily backups of my project files and other data to my card reader in addition to my git repositories.