Android: Gradle First Look


Introduction

Gradle favors convention over configuration. It means, Gradle provides default values for settings and properties. This makes Gradle very easy to get started with. However, if you would like to change/override Gradle default settings and properties, you can do that easily.

Gradle uses Groovy DSL (Domain Specific Language) as it’s configuration lanugage. Groovy is a dynamic language for the Java Virtual Machine (JVM).

Gradle supports three different kinds of repositories.

  1. Maven
  2. Ivy
  3. Static files (mostly *.jar or .aar)

Dependencies are fetched from repositories during the execution phase. Gradle keeps a local cache, so a particular dependency will be downloaded for once only.

Gradle also allows you to build other than Java projects. If you want to manage your JS/C++ projects you can do that using Gradle.

We only need to say what we need, not how to achieve it and Gradle will make it happen. For example, we can add a single line to our build file and Gradle will download the dependency from a remote repository and also sub dependencies and it will make sure all the classes are available to our project.

Installation

Download Gradle from, https://gradle.org/gradle-download/
Make sure to add the Gradle/bin path to your Environment variable.

In MacOS, if you have homebrew installed, then you can run the following command,

~$ brew update
~$ brew install gradle

First task

Let’s create our first task in gradle.

task firstTask {
	println "Hello Gradle"
}

Now we will run our first task.

~$ gradle firstTask
Starting a Gradle Daemon (subsequent builds will be faster)
Hello Gradle
:firstTask UP-TO-DATE

BUILD SUCCESSFUL

Total time: 3.838 secs

All available tasks

~$ gradle tasks
Hello Gradle
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'Temp'.
components - Displays the components produced by root project 'Temp'. [incubating]
dependencies - Displays all dependencies declared in root project 'Temp'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Temp'.
help - Displays a help message.
model - Displays the configuration model of root project 'Temp'. [incubating]
projects - Displays the sub-projects of root project 'Temp'.
properties - Displays the properties of root project 'Temp'.
tasks - Displays the tasks runnable from root project 'Temp'.

Other tasks
-----------
firstTask

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.008 secs

Build a simple Java project

  1. Create this project structure, /SimpleJava/src/main/java/com/genericslab/simplejava
  2. Create a Java file inside simplejava folder
  3. Create a build.gradle file inside SimpleJava fodler, parallel with src folder.
package com.genericslab.simplejava;

public class Main {
	public static void main (String... args) {
		System.out.println("Hello Java!!!");
	}
}
apply plugin: 'java' 

After this, run the ~ $ gradle tasks command again and see the avialable tasks.

~$ gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'SimpleJava'.
components - Displays the components produced by root project 'SimpleJava'. [incubating]
dependencies - Displays all dependencies declared in root project 'SimpleJava'.
dependencyInsight - Displays the insight into a specific dependency in root project 'SimpleJava'.
help - Displays a help message.
model - Displays the configuration model of root project 'SimpleJava'. [incubating]
projects - Displays the sub-projects of root project 'SimpleJava'.
properties - Displays the properties of root project 'SimpleJava'.
tasks - Displays the tasks runnable from root project 'SimpleJava'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 1.657 secs

As you can see, we have got a number of additional Build Tasks because we had added java plugins.
Let’s run ~ $ gradle build

~$ gradle build
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 1.448 secs

A build directory has been generated. You can find a Main.class file in the following directory,
~SimpleJava/build/classes/main/com/genericslab/simplejava

Let’s run it.

~$ java -cp build/classes/main/ com.genericslab.simplejava.Main
Hello Java!!!

Run build with a specific version of Gradle (Gradle Wrapper)

You need to have Gradle already installed.

apply plugin: 'java'

task wrapper(type: Wrapper) {
	gradleVersion = '2.6'
}
~$ gradle wrapper

After running the wrapper task, you will see a folder named gradle and a script file named gradlew and a .bat file named gradlew.bat

From now on, you need to use gradlew instead of gradle to use the particular gradle version 2.6. Any further command started with gradlew will first install the Gradle version 2.6 (if not installed already).

If you have built an application that need to use a particular Gradle version, or you might want to run the application without any hassel in other machine then it is a preferable way to use a wrapper.

Tasks

Task is the unit that Gradle executes. Each task has a lifecycle and may contain properties. Task can also contain dependencies of other tasks.

Let’s create a task

// project level task 
project.task "TaskA"


// in theory, it is a local level task 
// however, practically it is same as before 
// because we are now in project context 
task "TaskB"

// We can add description of a task 
TaskB.description = "This is a description for TaskB"

// Action of a task 
TaskA {
	doLast {
		println "Action#1 for TaskA"
	}
}

// We can define the task and define action together
task "TaskC" {
	doLast {
		println "Action#1 for TaskC"
	}
}

// If we define multiple actions inside a task, 
// actions will be appended
TaskA {
	doLast {
		println "Action#2 for TaskA"
	}
}

// define, description and actions all in one 
task "TaskD" {
	description "This is a description for TaskD"
	doLast {
		println "Action#1 for TaskD"
	}
}

Run ~$ gradle tasks in command line.

~$ gradle tasks
:tasks

------------------------------------------------------------
All tasks runnable from root project
------------------------------------------------------------

Build Setup tasks
-----------------
init - Initializes a new Gradle build. [incubating]
wrapper - Generates Gradle wrapper files. [incubating]

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'Temp'.
components - Displays the components produced by root project 'Temp'. [incubating]
dependencies - Displays all dependencies declared in root project 'Temp'.
dependencyInsight - Displays the insight into a specific dependency in root project 'Temp'.
help - Displays a help message.
model - Displays the configuration model of root project 'Temp'. [incubating]
projects - Displays the sub-projects of root project 'Temp'.
properties - Displays the properties of root project 'Temp'.
tasks - Displays the tasks runnable from root project 'Temp'.

Other tasks
-----------
TaskA
TaskB - This is a description for TaskB
TaskC
TaskD - This is a description for TaskD

To see all tasks and more detail, run gradle tasks --all

To see more detail about a task, run gradle help --task <task>

BUILD SUCCESSFUL

Total time: 0.852 secs

Task Dependencies

// define, description and actions all in one 
task "TaskD" {
	description "This is a description for TaskD"
	doLast {
		println "Action#1 for TaskD"
	}
}

// TaskA is dependent on TaskC
// means, TaskC will auto executed when we run TaskA 
TaskA.dependsOn TaskC 

// one task must execute after another task 
TaskB .mustRunAfter TaskA 

// one task should execute after another task 
// avoids circular dependencies 
TaskB.shouldRunAfter TaskA, TaskB 

/*
Remember, mustRunAfter and shouldRunAfer will not kick in 
automatically. If we run both tasks together, or, the tasks
have dependencies on one another, only then those two 
keywords will be meaningful. 

What if we would like to run a task after another task, 
even we don't call it separately, or don't have dependencies.
Similar to final block, we can use finalizedBy 
*/

// might be very usefule for db migration 
// after the migration you might want to run a task for cleanup 
TaskB.finalizedBy TaskC 

Run ~$ gradle TaskA

~$ gradle TaskA
:TaskC
Action#1 for TaskC
:TaskA
Action#1 for TaskA
Action#2 for TaskA

BUILD SUCCESSFUL

Total time: 0.908 secs

Define properties

 
def buildName = "Awesome-20160902a"
println "Build name: $buildName"

// global scope
project.ext.versionName = "Awesome=20160902b"
println "Version name: $project.ext.versionName"


<h3>Use built-in task: Copy</h3>


task copyFiles (type: Copy) {
	exclude 'file1.txt', 'file2.txt' // do not copy these files
	from 'src'  // define the source folder 
	from 'dest' // define the destiantion folder 
}

// this can be re-written to 

def spec = copySpec {
	exclude 'file1.txt', 'file2.txt' // do not copy these files
	from 'src'
}

task copyFilesAgain (type: Copy) {
	with spec 
	into 'dest' 
}

// another spec variable 
def spec = copySpec {
	exclude {
		// iterator: it 
		// exclude all pngs 
		it.file.name.endsWith("png")
	}
	from 'src'
}

Sourceset

Gradle Android projects follows this folder structure:

src/main/java
src/main/res
src/main/jniLibs
src/androidTest/java
src/test/java

if you do not like to change your current folder structure then you can tell Gradle where to look for specific files

android {
    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }

        androidTest.setRoot('tests')
    }
}

Gradle daemon

Everytime we run ~$ gradle build, Gradle launches JVM and starts from the beginning. Instead if Gradle could run as a service and could reuse already runned JVM then it would have been great!

Voila! Gradle can do that and it's called daemon.

Run ~$ gradle --daemon build and you might be prompted to give a permission and gradle daemon will be started.

If you would like to run --daemon all the time, instead of per command basis then write the following code,

or.gradle.daemon=true

in your project's gradle.properties file. That will ensure daemon will run on your project.

If you would like run daemon irrespective of projects, that means for all projects, then add the line in your ~/.gradle/gradle.properties file.

Android: Instrumentation Testing using Espresso


Introduction

Android provides a number of extra components beyond Java, for example, Activity, Service etc. Testing these extra components you will be required Instrumentation testing and it needs to be run on device/emulators. Espresso is an official instrumentation testing framework by Google.

Installation

Add this line in app level build.gradle in dependencies

androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'

add this line inside defaultConfig

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

For more information,

  1. Android dev
  2. Google github

Project Structure

An androidTest folder should automatically be generated when you created the Android application. For example,

In Android view,

Screenshot at Sep 15 11-44-37.png

Or, in Project view,

Screenshot at Sep 15 11-46-31.png

If not,

  1. Create a folder named androidTest inside src
  2. Create a folder named java inside androidTest

Now we will create our first test class.

  1. Go to the MainActivity.java class and click on MainActivity class name
  2. Select Navigate (from menu)
  3. Select Test (Shift + Cmd + T)
  4. Select, Create New Test…

Select JUnit4 and the dialog should look like this,

Screenshot at Sep 15 11-56-52.png

Click OK.

Check the correct directory is selected. We need to select androidTest directory,

Screenshot at Sep 15 11-57-34.png

Click OK.

Now Sync Gradle.

If you get the following error,

Error:Conflict with dependency 'com.android.support:support-annotations'. Resolved versions for app (24.2.1) and test app (23.0.1) differ. See http://g.co/androidstudio/app-test-app-conflict for details.

The add the following line in dependencies

androidTestCompile 'com.android.support:support-annotations:24.2.1'

Note that the above line version number need to match with the expected version number indicated with the error.

Now let’s go to the newly created MainActivityTest.java


package com.genericslab.espressodemo;

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    // This will launch the MainActivity before testing each method/case.
    @Rule
    public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);

}

Now we will write our first test case

@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

    // This will launch the MainActivity before testing each method/case.
    @Rule
    public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void testCase1() {
        // Find a view with id = R.id.text
        // Check the text of that view matches with string = R.string.app_name
        onView(withId(R.id.text)).check(matches(withText(R.string.app_name)));
    }
}

Now Right click on the file MainActivityTest.java from project explorer and select “Create MainActivityTest…” -> OK.
Click on Run.

Now we will add our second test case

@Test
public void testCase2() {

    // Open options menu, whether as overflow or using a physical button
    // This will open the option menu as a pop up menu. Next we will click on an item.
    // If you need context then pass, InstrumentationRegistry.getTargetContext()
    openActionBarOverflowOrOptionsMenu(InstrumentationRegistry.getTargetContext());

    // From the options menu, we are going to click on an item having text = R.string.label_help
    onView(withText(R.string.label_help)).perform(click());

    // alternatively, you can leverage id property.
    onView(withId(R.id.menu_help)).perform(click());

    // verifying
    onView(withId(R.id.text)).check(matches(withText(R.string.label_updated)));
}

Android: Speed up Gradle build process in Android Studio


These techniques will help you to make the Gradle build process faster.

Technique #1

  1. Open up gradle.properties file
  2. add the following line

org.gradle.daemon=true

Side effects

  1. Gradle daemon is a background process. If you use this, it might take (300-400) MB memory or more from your system.
  2. After adding the above line, try to Rebuild the project. If you get error, something like, Unable to locate a Java Runtime to invoke, very obvious solution, remove the line.

Technique #2

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Compile independent modules in parallel

prefs_as

Alternatively,

  1. Open up gradle.properties file
  2. add the following line

org.gradle.parallel=true

Technique #3

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Configure on demand

prefs_as2

Alternatively,

  1. Open up gradle.properties file
  2. add the following line

org.gradle.configureondemand=true

Technique #4

  1. Open up gradle.properties file
  2. add the following line

org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

Side effects

The above line means, you are allowing Java compilers to have available memory up to 2 GB (2048 MB). If you do not have available memory to give out 2 GB to Java then you should not add this line.

Another way to say this, If you have enough memory then you can try to tweak the settings.

Technique #5

All the above changes to gradle.properties file can be configures globally instead of changing the settings for every individual project.

Modify or create a file named gradle.properties in the following directory.

For Mac,
/Users/USER_NAME/.gradle/gradle.properties

For Linux,
/home/USER_NAME/.gradle/gradle.properties

For Windows,
C:\Users\USER_NAME\.gradle\gradle.properties

and apply the above changes. Finally it should look like,


org.gradle.daemon=true

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true

org.gradle.configureondemand=true

# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

Technique #6

  1. Open up Android Studio Settings (PC/Linux) or Preference (Mac)
  2. check Compile independent modules in parallel

prefs_as3

Side effects

Apply this option after all of your libraries are downloaded and configured correctly. Gradle try to resolve the dependencies from internet, every time we build our project. This option will force the gradle to resolve the dependencies from cache. You may need to uncheck the option if you add/modify a library.

Technique #7

If you are using google play services, then try to use only the library you need. For example, if you need maps, then use

compile 'com.google.android.gms:play-services-maps:7.5.0'

instead of:

compile 'com.google.android.gms:play-services:7.5.0'

Technique #8

Consider using separate flavors for development and production.

android {
    productFlavors {
        // Define separate dev and prod product flavors.
        dev {
            // dev utilizes minSDKVersion = 21 to allow the Android gradle plugin
            // to pre-dex each module and produce an APK that can be tested on
            // Android Lollipop without time consuming dex merging processes.
            minSdkVersion 21
        }
        prod {
            // The actual minSdkVersion for the application.
            minSdkVersion 14
        }
    }
          ...
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
                                                 'proguard-rules.pro'
        }
    }
}
dependencies {
  compile 'com.android.support:multidex:1.0.0'
}

for further details,

https://developer.android.com/tools/building/multidex.html#dev-build

Technique #9

Try to use Gradle 2.4, it has a huge performance boost over previous versions.

  1. Open up Android Studio Project Structure option. File -> Project Structure
  2. Write value 2.4 as Gradle Version

prefs_as4

Technique #10

  1. Open up app level build.gradle file. (Inside app directory)
  2. add the following line

dexOptions {
incremental true
}

android {
    compileSdkVersion 22
    buildToolsVersion 1.2.3

    defaultConfig {
        applicationId "com.wordpress.tausiq.sampleapp"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

    dexOptions {
        incremental true
    }
}

Side effects

Enabling this could cause your builds to fail (especially on consecutive runs). If error occurs you can try to build again. Or, if it doesn’t work for you then skip it.

Technique #11

Try not to use Build -> Rebuild Project every time to check for compile errors or after a small change. Rather use, Build -> Make Project, it is much faster than previous option.

References:

Using Android Studio to increase productivity


Android Studio has been released officially. A couple of bug fix releases have already been rolled out since the first stable release. You can download the latest stable version from here.

Android Studio

There are 3 more release channels along with Stable channel. Namely,

  1. Canary channel: rough edges build
  2. Dev channel: slightly more stable than Canary
  3. Beta channel: Beta version
  4. Stable channel: Stable release

If you would like to test out Canary builds or Dev builds, then you can look at this page.

After running Android Studio (AS) you can set the default channel from which it will look for the next updates.

Set default update channel

Fire up AS.

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Updates from left pane
  3. Select channel from the drop down list on right pane
  4. Hit Apply -> OK

Dracula theme

Currently there are two themes in AS. I would suggest to use Dracula theme. It’s background is black and the syntax highlighting color combination is more soothing to look at. If you are a full-time developer then you might work on AS for long hours and Dracula theme would be a preferable choice.

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Appearance from left pane
  3. Select themes from the drop down list on right pane
  4. Hit Apply -> OK

Auto import

AS can auto import classes for you. On ambiguous occassion it would provide a dialog box for you to choose, otherwise it will done it automatically. To turn on this feature,

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for Editor from left pane then Auto Import
  3. Check: Optimize imports on the fly
  4. Check: Add unambiguous imports on the fly
  5. Hit Apply -> OK

Useful shortcuts

Duplicates:
Place cursor on a code line. Hit Cmd + d. (The line will be duplicated on next line)

Go to a class:
Hit Cmd + n. Type out the name of a class and hit enter.

Go to a file:
Hit Cmd + Shift + n. Type out the name of a file and hit enter.

Rename:
place cursor on the “thing” you would like to rename. Hit Shift + F6

Quick fix:
Alt + Enter.
This shortcut also heavily used to extract hardcoded string resources from java to xml

Plugins

Following plugins can be handy for Android developers.

  • Genymotion
  • .gitignore Support

Genymotion

Genymotion is a third-party emulator made by Genymobile. It a faster and more responsive than official Android emulator. Get Genymotion from here.

.gitignore Support

Nice plugins to edit .gitingore file. Details can be found here.

Invalidated Caches / Restart

Often times when you add a jar file or library and also added the necessary code on build.gradle file and then sync the gradle file but still the jar is not added to the project then you might need this option. Go to File -> Invalidated Caches / Restart and Hit on “Invalidate and Restart” button.

Code completion

  1. On Mac, go to Preference (cmd + ,) and on Windows go to Settings
  2. look for “Editor” from left pane and then “Code Completion”
  3. Check: Parameter Info -> Autopopup in (ms): 1000 (This will display the parameter info of a method in 1 sec.
  4. Hit Apply -> OK

Android: Generate version name / version code / release apk file name automatically


Version code

versionCode is an incremental integer variable. If you are using Git, then you can write a simple function in your build.gradle file


def gitVersion() {
    def process = "git rev-list master --first-parent --count".execute()
    return process.text.toInteger()
}

this will return the commit number and ensures the incremental integer.

Version Name

For versionName you can maintain 3 global variable

versionMajor = 1
versionMinor = 1
versionPatch = 0

and combine them in a function, then return

def buildNumber() {
    return "${versionMajor}.${versionMinor}.${versionPatch}";
}

You can also use the timeStamp in your versionName for a unique naming convention

def buildTime() {
    def df = new SimpleDateFormat("yyMMddhhmm")
    return df.format(new Date())
}

you can merge these two functions

def genVersionName() {
    return buildNumber() + "." + buildTime();
}

and finally,

defaultConfig {
    minSdkVersion 16
    targetSdkVersion 19
    versionCode genVersionCode()
    versionName genVersionName()
    applicationId "com.wordpress.tausiq.sampleapp"
}

Version code again

Major, minor and patch number can also be used to generate versionCode

def genVersionCode() {
    return versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100;
}

Apk naming convention

First write a signingConfigs

    signingConfigs {
        release {
            storeFile file("keys/KEYSTORE_FILE.jks")
            storePassword "*******************"
            keyAlias "*******************"
            keyPassword "*******************"
        }
    }

then write a buildTypes

    buildTypes {
        release {
            signingConfig signingConfigs.release
            applicationVariants.all { variant ->
                def file = variant.outputFile
                def fileName = "App_" + buildTime() + ".apk";
                variant.outputFile = new File(file.parent, fileName);
            }
        }
    }

Release apk file name will be like, App_1410010507.apk
You can modify the naming convention and write/change the functions based on your need.

some more sample functions you may consider

def gitSha() {
      return 'git rev-parse --short HEAD'.execute().text.trim()
    }
def buildTime() {
      def df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
      df.setTimeZone(TimeZone.getTimeZone("UTC"))
      return df.format(new Date())
    }

As a sample example, here is the whole build.gradle file from one of my applications.

build.gradle

import java.text.SimpleDateFormat

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.2'
    }
}

allprojects {
    repositories {
        mavenCentral()
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    }
}

apply plugin: 'com.android.application'

versionMajor = 1
versionMinor = 1
versionPatch = 0

def gitVersion() {
    def process = "git rev-list master --first-parent --count".execute()
    return process.text.toInteger()
}

def buildNumber() {
    return "${versionMajor}.${versionMinor}.${versionPatch}";
}

def buildTime() {
    def df = new SimpleDateFormat("yyMMddhhmm")
    return df.format(new Date())
}

def genVersionName() {
    return buildNumber() + "." + buildTime();
}

def genVersionCode() {
    return versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100;
}

android {
    packagingOptions {
        exclude 'LICENSE.txt'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/NOTICE'
    }

    lintOptions{
        checkReleaseBuilds false
        abortOnError false
    }

    compileSdkVersion 19
    buildToolsVersion '19.1.0'

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 19
        versionCode genVersionCode()
        versionName genVersionName()
        applicationId "com.wordpress.tausiq.sampleapp"
    }

    signingConfigs {
        release {
            storeFile file("keys/app_keystore.jks")
            storePassword "helloWorld"
            keyAlias "SampleApp"
            keyPassword "helloAndroid"
        }
    }

    buildTypes {
        debug {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            debuggable true
        }
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
            signingConfig signingConfigs.release
            applicationVariants.all { variant ->
                def file = variant.outputFile
                def fileName = "App_" + buildTime() + ".apk";
                variant.outputFile = new File(file.parent, fileName);
            }
        }
    }

    sourceSets {
        androidTest {
            setRoot('src/test')
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile project(':dependency:Formidable')
    compile 'uk.co.androidalliance:edgeeffectoverride:1.0.1'
    compile 'com.doomonafireball.betterpickers:library:1.5.2'
    compile 'de.greenrobot:eventbus:2.2.1'
}

apply plugin: 'idea'

idea {
    module {
        testOutputDir = file('build/test-classes/debug')
    }
}

For more information,
http://stackoverflow.com/questions/21329849/set-android-app-version-using-gradle

Jake Wharton

http://stackoverflow.com/questions/18328730/how-to-create-a-release-signed-apk-file-using-gradle

http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Signing-Configurations

http://stackoverflow.com/questions/18332474/how-to-set-versionname-in-apk-filename-using-gradle

Android: Custom Circle View with Stroke


layout-2014-03-20-113649 copy

layout-2014-03-20-114814 copy

res/values/attrs.xml

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

    <declare-styleable name="CircleView">
        <attr name="strokeWidth" format="dimension"/>
        <attr name="strokeColor" format="color|reference"/>
        <attr name="fillColor" format="color|reference"/>
        <attr name="circleRadius" format="dimension"/>
        <attr name="circleGap" format="dimension"/>
    </declare-styleable>

</resources>

CircleView.java


package ms.cloudtea.customcircleview.app.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet;

import ms.cloudtea.customcircleview.app.R;

/**
 * Created by Shahab on 3/20/14.
 */
public class CircleView
extends AbstractBaseView
{

    private int circleRadius = 20;
    private int strokeColor = 0xFFFF8C00;
    private int strokeWidth = 15;
    private int fillColor = 0XFFFFAB00;
    private int circleGap = 20;

    public CircleView(Context context) {
        super(context);

        init();
    }

    public CircleView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

        init();
    }

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray aTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleView, defStyleAttr, 0);

        strokeColor = aTypedArray.getColor(R.styleable.CircleView_strokeColor, strokeColor);
        strokeWidth = aTypedArray.getDimensionPixelSize(R.styleable.CircleView_strokeWidth, strokeWidth);
        fillColor = aTypedArray.getColor(R.styleable.CircleView_fillColor, fillColor);
        circleRadius = aTypedArray.getDimensionPixelSize(R.styleable.CircleView_circleRadius, circleRadius);
        circleGap = aTypedArray.getDimensionPixelSize(R.styleable.CircleView_circleGap, circleGap);

        aTypedArray.recycle();

        init();
    }

    public CircleView(Context context, int strokeColor, int strokeWidth, int fillColor, int circleRadius, int circleGap) {
        super(context);
        this.strokeColor = strokeColor;
        this.strokeWidth = strokeWidth;
        this.fillColor = fillColor;
        this.circleRadius = circleRadius;
        this.circleGap = circleGap;

        init();
    }

    private void init() {
        this.setMinimumHeight(circleRadius * 2 + strokeWidth);
        this.setMinimumWidth(circleRadius * 2 + strokeWidth);
        this.setSaveEnabled(true);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        int w = this.getWidth();
        int h = this.getHeight();

        int ox = w/2;
        int oy = h/2;

        canvas.drawCircle(ox, oy, circleRadius, getStroke());
        canvas.drawCircle(ox, oy, circleRadius - circleGap, getFill());
    }

    private Paint getStroke()
    {
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setStrokeWidth(strokeWidth);
        p.setColor(strokeColor);
        p.setStyle(Paint.Style.STROKE);
        return p;
    }

    private Paint getFill()
    {
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setColor(fillColor);
        p.setStyle(Paint.Style.FILL);
        return p;
    }

    @Override
    protected int hGetMaximumHeight() {
        return circleRadius * 2 + strokeWidth;
    }

    @Override
    protected int hGetMaximumWidth() {
        return circleRadius * 2 + strokeWidth;
    }

    public int getCircleRadius() {
        return circleRadius;
    }

    public void setCircleRadius(int circleRadius) {
        this.circleRadius = circleRadius;
    }

    public int getStrokeColor() {
        return strokeColor;
    }

    public void setStrokeColor(int strokeColor) {
        this.strokeColor = strokeColor;
    }

    public int getStrokeWidth() {
        return strokeWidth;
    }

    public void setStrokeWidth(int strokeWidth) {
        this.strokeWidth = strokeWidth;
    }

    public int getFillColor() {
        return fillColor;
    }

    public void setFillColor(int fillColor) {
        this.fillColor = fillColor;
    }

    public int getCircleGap() {
        return circleGap;
    }

    public void setCircleGap(int circleGap) {
        this.circleGap = circleGap;
    }
}

layout/activity_main.xml


<ms.cloudtea.customcircleview.app.ui.CircleView
        android:id="@+id/circle_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        cloudteams:circleRadius="@dimen/circle_view_radius"
        cloudteams:strokeWidth="@dimen/circle_view_stroke"
        cloudteams:circleGap="@dimen/circle_view_gap"
        cloudteams:fillColor="@android:color/holo_orange_light"
        cloudteams:strokeColor="@android:color/holo_purple"/>

You can download the Full project source code (Gradle build) from here:
http://www.4shared.com/zip/Hl45mi09ce/CustomCircleView.html

Acknowledgement: Expert Android (Satya Komatineni | Dave macLean)