Tutorial #101: Getting started with Flurry Analytics in Android

Hello everyone!

Now-a-days, developers keep track of user activity and generate useful statistics about an application. Hence, analytics plays an important role in mobile applications. There are plenty of analytics solutions that exists in the market today. For example, ACRA is a library enabling Android Application to automatically post their crash reports to a GoogleDoc form. It is targetted to Android application developers to help them get data from their applications when they crash or behave erroneously.

Through this post, we will learn how to integrate the get started with the Flurry Analytics SDK in Android. The Flurry Analytics SDK provides you with the tools and resources you need to gain a deep level of understanding about your userโ€™s behavior in your apps. Set up advanced analysis of complex events, with metrics, segments and funnels to better track your userโ€™s habits and performance.

Pre-requisites: Eclipse IDE, Android SDK, Flurry Developer Account

Step 1: Sign up for Flurry developer account

First, you need to create a developer account by signing up over here. Once the account is created, download the required SDK. Now, login into your account and create a new application that will display all the analytical information.

Step 2: Create Android application project

Launch Eclipse IDE and a create a new Android application project called AndroidFlurryAnalyticsDemo with package name com.app.flurry.android. Copy the FlurryAnalytics.jar file inside the libs folder of your Android project.

Step 3: Initialize Flurry and simulate a crash

Create a new class called FlurryApplication that extends the Application class in Android. This class will initialize Flurry on application launch.

FlurryApplication.java

package com.app.flurry.application;

import android.app.Application;
import com.app.flurry.utils.AppConstants;
import com.flurry.android.FlurryAgent;

public class FlurryApplication extends Application {
	@Override
	public void onCreate() {
	super.onCreate();

        //set log enabled
        FlurryAgent.setLogEnabled(true);

        //set log events
        FlurryAgent.setLogEvents(true);
        
        // initialize Flurry
        FlurryAgent.init(this, AppConstants.FLURRY_API_KEY);
    }
}

Next, let us simulate a crash event in our Activity class as follows!

MainActivity.java

package com.app.flurry.main;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.app.flurry.android.R;
import com.app.flurry.utils.AppConstants;
import com.flurry.android.FlurryAgent;

public class MainActivity extends Activity {

	private Button m_objBtnPrint;
	private String m_strName = null;

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

		m_objBtnPrint = (Button)findViewById(R.id.btnPrint);
		m_objBtnPrint.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				FlurryAgent.onEvent("click button");	
				simulateCrashEvent();
			}
		});
	}

	public void simulateCrashEvent(){
		try{
			m_strName.concat("This will result in null pointer exception");
		}catch(Exception ex){
			ex.printStackTrace();
			FlurryAgent.onError("error", "Error occurred on click of button", ex); 		
		}
		FlurryAgent.logEvent("print button clicked");	
	}

	@Override
	protected void onStart(){
		super.onStart();
		FlurryAgent.onStartSession(this, AppConstants.FLURRY_API_KEY);
	}

	@Override
	protected void onStop(){
		super.onStop();		
		FlurryAgent.onEndSession(this);
	}
}

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <TextView
        android:id="@+id/txt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <Button
        android:id="@+id/btnPrint"
        android:layout_below="@+id/txt1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/print" />

</RelativeLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.app.flurry.android"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="19" />

    <!-- required permission -->
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- optional permission - highly recommended -->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <!-- optional permission -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

    <application
        android:name="com.app.flurry.application.FlurryApplication"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name="com.app.flurry.main.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
    </application>

</manifest>

Save all changes and run the application on an Android device. If no errors occur, then you should see the following output from the dashboard page of the Flurry application.

crashes_page

events_page

users_page

Source code for this tutorial can be found over here

Reference: Flurry SDK Developer’s page

Display contacts from address book in Android

In my last post, I had explained how to add new contacts using the Contacts Provider component in Android. Moving on, we will now learn how to display existing phone book contacts using the concept of Cursors. The Cursor interface provides random read-write access to the result set returned by a database query.

As mentioned on the developer’s page, cursor implementations are not required to be synchronized so code using a Cursor from multiple threads should perform its own synchronization when using the Cursor.

Through this post, we will learn how to display existing phone book and Skype contacts using Cursors.

Pre-requisites: Eclipse IDE, Android SDK (Target API level: 19)

Open the Eclipse IDE and create a new Activity class called DisplayContactListActivity in any of your existing Android projects and add the following code!

DisplayContactListActivity.java

package com.example;

import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Data;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class DisplayContactListActivity extends Activity {

	private Button m_objBtnSkypeContact;
	private Button m_objBtnPhoneContact;
	private TextView m_objDisplayContact;

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

		m_objBtnSkypeContact = (Button)findViewById(R.id.btnSkypeContact);
		m_objBtnPhoneContact = (Button)findViewById(R.id.btnPhoneContact);
		m_objDisplayContact = (TextView)findViewById(R.id.txtDisplayContact);

		//skype phone contact button event
		m_objBtnSkypeContact.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				try{
					m_objDisplayContact.setText("");
					getSkypeContactsList();
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		});


		//phone contact button event
		m_objBtnPhoneContact.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				try{
					m_objDisplayContact.setText("");
					getPhoneContactsList();
				}catch(Exception ex){
					ex.printStackTrace();
				}
			}
		});
	}

	/**
	 * 
	 * This function is used to display the list of Skype contacts
	 */
	private void getSkypeContactsList() {
		StringBuffer strSkypeContactBuffer = new StringBuffer();
		Cursor objCursor = getContentResolver().query(
				Data.CONTENT_URI,
				new String[] { Data.CONTACT_ID, Data.DATA1 },
				Data.MIMETYPE + "= ?", 
				new String[] { "vnd.android.cursor.item/com.skype.android.skypecall.action" },
				null);

		while (objCursor != null && objCursor.moveToNext()) {
			long contact = objCursor.getLong(0);
			String skype = objCursor.getString(1);

			Log.i("ContactsApp" , " " + "contact " + contact + " has skype username: " + skype);
			strSkypeContactBuffer.append("Contact " + contact + " has skype username: " + skype + "\n");

		}

		//display contact details
		m_objDisplayContact.setText(strSkypeContactBuffer.toString());
	}

	/**
	 * 
	 * 
	 * This function is used to display the existing phone contacts
	 */
	private void getPhoneContactsList() {
		StringBuffer strPhoneContactBuffer = new StringBuffer();
		ContentResolver objContentResolver = getContentResolver();
		Cursor objCursor = objContentResolver.query(ContactsContract.Contacts.CONTENT_URI,
				null, null, null, null);
		if (objCursor.getCount() > 0) {
			while (objCursor.moveToNext()) {
				String id = objCursor.getString(objCursor.getColumnIndex(ContactsContract.Contacts._ID));
				String name = objCursor.getString(objCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
				if (Integer.parseInt(objCursor.getString(
						objCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
					Cursor objContactCursor = objContentResolver.query(
							ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
							null,
							ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
							new String[]{id}, null);
					while (objContactCursor.moveToNext()) {
						String phoneNo = objContactCursor.getString(objContactCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
						Log.i("ContactsApp", "Name: " + name + ", Phone No: " + phoneNo);
						strPhoneContactBuffer.append("Name: " + name + ", Phone No: " + phoneNo + "\n");
						
					}
					objContactCursor.close();
				}
			}
		}

		//display contact details
		m_objDisplayContact.setText(strPhoneContactBuffer.toString());
	}
}

activity_display_contact.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btnPhoneContact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:text="@string/lbl_phone_contact" />

    <Button
        android:id="@+id/btnSkypeContact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnPhoneContact"
        android:layout_centerInParent="true"
        android:text="@string/lbl_skype_contact" />

    <TextView
        android:id="@+id/txtDisplayContact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnSkypeContact"
        android:layout_centerInParent="true" />

</RelativeLayout>

strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Display contact demo -->
    <string name="lbl_phone_contact">Show Phone contacts</string>
    <string name="lbl_skype_contact">Show Skype contacts</string>
</resources>

No changed are to be made to the AndroidManifest.xml file. Run the application on an Android device and you should be able to see the following output!

Screenshot_2015-11-09-09-04-47

Now, click on any of the buttons to retrieve the contact list.

That’s it then for this Android tip. Stay tuned for more! ๐Ÿ™‚

Adding contacts in Android

Hi everyone!

The Android SDK allows developers to add new contacts using the Contacts Provider component. As mentioned on the developer’s page, the Contacts Provider is the source of data you see in the device’s contacts application, and you can also access its data in your own application and transfer data between the device and online services. The provider accommodates a wide range of data sources and tries to manage as much data as possible for each person, with the result that its organization is complex.

Developers can access the Contacts Provider information directly by calling ContentResolver methods or by sending intents to a pre-installed contacts application. Through this post, we will learn how to add new contacts to our existing phone address book.

Pre-requisites: Eclipse IDE, Android SDK (Target API level: 19)

In any of your existing Android projects, create a new Activity class called AddContactActivity and add the following code!

AddContactActivity.java

package com.example;

import java.util.ArrayList;
import android.app.Activity;
import android.content.ContentProviderOperation;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.PhoneLookup;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AddContactActivity extends Activity {

	private Button m_objBtnAddContact;
	private EditText m_objInputContactName, m_objInputContactEmail, m_objInputContactMobNo;
	public static final String LOG_TAG = "AddContactDemo";
	public static final String ACCOUNT_TYPE = "REPLACE_YOUR_GMAIL_ACCOUNT_ADDRESS";

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_add_contact);

		m_objBtnAddContact = (Button)findViewById(R.id.btnAddContact);
		m_objInputContactName = (EditText)findViewById(R.id.inputContactName);
		m_objInputContactEmail = (EditText)findViewById(R.id.inputContactEmail);
		m_objInputContactMobNo = (EditText)findViewById(R.id.inputContactMobNo);

		//add new contact button event
		m_objBtnAddContact.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View arg0) {
				// TODO Auto-generated method stub
				try{
					
					String strContactName = m_objInputContactName.getEditableText().toString();
					String strContactEmail = m_objInputContactEmail.getEditableText().toString();
					String strContactMobNumber = m_objInputContactMobNo.getEditableText().toString();

					if(!isNullOrBlank(strContactName) && !isNullOrBlank(strContactEmail) && !isNullOrBlank(strContactMobNumber)){
						if(!contactExists(AddContactActivity.this, strContactMobNumber)){
							Contact objContact = new Contact(strContactName, strContactMobNumber, strContactEmail);
							addContactsToAddressBook(objContact);
						}else{
							Toast.makeText(getApplicationContext(), "Contacts already exists", Toast.LENGTH_LONG).show();
						}
					}
				}catch(Exception e){
					e.printStackTrace();
				}
			}
		});
	}


	/**
	 * This method is used to determine if a particular contact already exists
	 * @param Activity instance
	 * @param mobile number
	 * @return
	 */
	public boolean contactExists(Activity p_objActivity, String p_strMobileNumber) {
		if (p_strMobileNumber != null) {
			Uri objLookupUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(p_strMobileNumber));
			String[] objPhoneNumberProjection = { PhoneLookup._ID, PhoneLookup.NUMBER, PhoneLookup.DISPLAY_NAME };
			Cursor objCursor = p_objActivity.getContentResolver().query(objLookupUri, objPhoneNumberProjection, null, null, null);
			try {
				if (objCursor.moveToFirst()) {
					return true;
				}
			} finally {
				if (objCursor != null)
					objCursor.close();
			}
			return false;
		} else {
			return false;
		}
	}

	/**
	 * This function is used to check if a string is null or empty
	 * @param p_strInput
	 * @return true if string is null or empty
	 */
	public static boolean isNullOrBlank(String p_strInput){
		return (null== p_strInput || p_strInput.trim().equals(""));
	}

	/**
	 * This function is used to add a new contact to address book
	 * 
	 * @param contact
	 */
	public void addContactsToAddressBook(Contact contact){
		ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
		ops.add(ContentProviderOperation.newInsert(
				ContactsContract.RawContacts.CONTENT_URI)
				.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, ACCOUNT_TYPE)
				.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "com.google")
				.build()
				);

		if(contact != null){
			//add contact name
			ops.add(ContentProviderOperation.newInsert(
					ContactsContract.Data.CONTENT_URI)              
					.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
					.withValue(ContactsContract.Data.MIMETYPE,
							ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
							.withValue(
									ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,     
									contact.getName()).build()
					);

			//add  contact email
			ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
					.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
					.withValue(ContactsContract.Data.MIMETYPE,
							ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
							.withValue(ContactsContract.CommonDataKinds.Email.DATA, contact.getEmailId())
							.withValue(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK)
							.build());

			//add contact mobile no
			ops.add(ContentProviderOperation.
					newInsert(ContactsContract.Data.CONTENT_URI)
					.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
					.withValue(ContactsContract.Data.MIMETYPE,
							ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
							.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, contact.getMobileNo())
							.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, 
									ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE)
									.build()
					);

			// Asking the Contact provider to create a new contact                  
			try {
				getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
			} 
			catch (Exception e) {               
				e.printStackTrace();
				Toast.makeText(getApplicationContext(), "Unable to add contact!", Toast.LENGTH_LONG).show();
			}
		}

		Toast.makeText(getApplicationContext(), "Contact added!", Toast.LENGTH_LONG).show();
	}


}

activity_add_contact.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/txtContactName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lbl_contact_name" >
    </TextView>

    <EditText
        android:id="@+id/inputContactName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/lbl_contact_name"
        android:inputType="text" >
    </EditText>

    <TextView
        android:id="@+id/txtContactEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lbl_contact_emailid" >
    </TextView>

    <EditText
        android:id="@+id/inputContactEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/lbl_contact_emailid"
        android:inputType="textEmailAddress" >
    </EditText>

    <TextView
        android:id="@+id/txtContactMobNo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lbl_contact_mobnumber" >
    </TextView>

    <EditText
        android:id="@+id/inputContactMobNo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:hint="@string/lbl_contact_mobnumber"
        android:inputType="number" >
    </EditText>

    <Button
        android:id="@+id/btnAddContact"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:hint="@string/lbl_add_contact" >
    </Button>

</LinearLayout>

strings.xml

 <string name="lbl_contact_name">Enter contact name</string>
    <string name="lbl_contact_emailid">Enter contact email ID</string>
    <string name="lbl_contact_mobnumber">Enter contact mobile number</string>
    <string name="lbl_add_contact">Add contact
</string>

Before running the application on a physical device, one needs to add the below permissions in the AndroidManifest.xml file

AndroidManifest.xml

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />

Finally, if no errors occur you should be able to see the below output on running the application.

Main screen

Main screen

Adding new contact

Adding new contact

Contact already exists

Contact already exists

In the next post, we will see how to fetch and display the list of contacts! ๐Ÿ™‚

Tutorial #100: The Switch from KitKat to Lollipop!

Hey everyone!

The new Android version release has brought few changes in the way developers used certain widgets to create layouts. For instance, starting from Lollipop, Android introduces a new Toolbar widget. As mentioned by Chris Banes in this post, the Toolbar widget is a generalization of the Action Bar pattern that gives developers much more control and flexibility. Similarly, Android has made a “switch” in the way the current Switch widget is implemented.

As mentioned on the developer’s page, a Switch is a two-state toggle switch widget that can select between two options. The user may drag the “thumb” back and forth to choose the selected option, or simply tap to toggle as if it were a checkbox. Through this tutorial, we will learn how to implement the Switch widget in Android.

Pre-requisites: Eclipse IDE, Android SDK

Step 1: Create a new Activity class called TestSwitchActivity in any of your existing Android projects and write the following code,

TestSwitchActivity.java

import android.app.Activity;
import android.os.Bundle;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.Switch;
import android.widget.TextView;

public class TestSwitchActivity extends Activity {

	private TextView m_tvSwitchStatus;
	private Switch m_objSwitch;

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

		m_tvSwitchStatus = (TextView) findViewById(R.id.switchStatus);
		m_objSwitch = (Switch) findViewById(R.id.sampleSwitch);

		//set the switch to ON 
		m_objSwitch.setChecked(true);
		//attach a listener to check for changes in state
		m_objSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {

			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {

				if(isChecked){
					m_tvSwitchStatus.setText("Switch is currently ON");
				}else{
					m_tvSwitchStatus.setText("Switch is currently OFF");
				}

			}
		});

		if(m_objSwitch.isChecked()){
			m_tvSwitchStatus.setText("Switch is currently ON");
		}
		else {
			m_tvSwitchStatus.setText("Switch is currently OFF");
		}
	}
}

Step 2: Create Switch widget

In previous versions of Android, we would implement a Switch widget in our layout as follows,

activity_switch.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp">

    <Switch
        android:id="@+id/sampleSwitch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="20dp"
        android:text="@string/action" />

    <TextView
        android:id="@+id/switchStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/sampleSwitch"
        android:layout_marginTop="22dp"
        android:text="@string/status"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

According to recent changes, the Switch widget can now be implemented using SwitchCompat. The difference is that it does not make any attempt to use the platform provided widget on those devices which it is available normally.

activity_switch.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="5dp" >

    <android.support.v7.widget.SwitchCompat
        android:id="@+id/sampleSwitch"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="20dp"
        android:text="@string/action"
        android:thumb="@drawable/thumb_pressed"
        android:track="@drawable/bg"/>

    <TextView
        android:id="@+id/switchStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_below="@+id/sampleSwitch"
        android:layout_marginTop="22dp"
        android:text="@string/status"
        android:textAppearance="?android:attr/textAppearanceMedium" />

</RelativeLayout>

In this case, our Activity file would be as follows,

TestSwitchActivity.java

package com.app.lollipop.test;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.SwitchCompat;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;

public class TestSwitchActivity extends Activity {

	private TextView m_tvSwitchStatus;
	private SwitchCompat m_objSwitch;

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

		try {

			m_tvSwitchStatus = (TextView) findViewById(R.id.switchStatus);
			m_objSwitch = (SwitchCompat)findViewById(R.id.sampleSwitch);
			m_objSwitch.setThumbResource(R.drawable.apptheme_switch_thumb_holo_light);
			m_objSwitch.setTrackResource(R.drawable.apptheme_switch_track_holo_light);
			m_objSwitch.setTextOn("ON");
			m_objSwitch.setTextOff("OFF");
			
			//set the switch to ON 
			m_objSwitch.setChecked(true);
			//attach a listener to check for changes in state
			m_objSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {

				@Override
				public void onCheckedChanged(CompoundButton buttonView,
						boolean isChecked) {

					if(isChecked){
						m_tvSwitchStatus.setText("Switch is currently ON");
					}else{
						m_tvSwitchStatus.setText("Switch is currently OFF");
					}

				}
			});

			if(m_objSwitch.isChecked()){
				m_tvSwitchStatus.setText("Switch is currently ON");
			}
			else {
				m_tvSwitchStatus.setText("Switch is currently OFF");
			}
		}catch(Exception e){
			e.printStackTrace();
			Log.i("App", "Error occurred due to" +e.getMessage());
		}
	}
}

Finally, save all changes and run the Android application project on an emulator or a real device. If no errors occur then you should see the following output!

Android Switch on Pre Lollipop versions

Android Switch on Pre Lollipop versions

Android Switch on Lollipop

Android Switch on Lollipop

From here on, we will learn more about the recent changes in Android 5.0. We will also try to implement the same in the upcoming tutorials.

Reference: Android Switch

Tutorial #96: Implement multi-language support in Android

Hey friends!

Today, Android has become one of the dominant mobile operating systems in the world. Android applications are now used by people living across the globe. It is important for developers to build applications that can support multiple languages. In order to reach maximum users, an application should handle text, audio files, numbers, currency, and graphics in ways appropriate to the locales where it will be used.

By default Android considers English as the primary language and loads the string resources from res/values/strings.xml file. In order to add support for another language, one needs to create a values folder by appending an hyphen and the ISO language code. For example if you want to add support for French, you should create a values folder named values-fr and keep a strings.xml file in it with all the strings translated into French language.

As mentioned on the developer’s page, it’s always a good practice to extract UI strings from your application code and keep them in an external file. Through this tutorial, we will learn how to implement multi-language support in Android.

Pre-requisites: Eclipse IDE, Android SDK

Step 1: Create Android project

Launch Eclipse IDE and create a new Android application project called AndroidMultiLanguageSupportDemo. Let’s keep the package name as com.app.multilanguage. Choose the target SDK as Android 4.4 (API level 19)

Step 2: Add multi-language support

As mentioned earlier, we need to create multiple values folder for the appropriate language depending on the language code as follows!

res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello_world">Hello world!</string>
</resources>

res/values-fr/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="hello_world">bonjour le monde!</string>
</resources>

MainActivity.java

package com.app.multilanguage;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

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

res/layout/activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}">

    <TextView
        android:id="@+id/txtView"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:textColor="#FE2E2E"
        android:text="@string/hello_world" />

</RelativeLayout>

Finally, save all changes. Make sure no errors are present. Run the application on an Android device and you should see the following output for the respective languages.

English

English

Japanese

Japanese

Hindi

Hindi

French

French

That’s it for this tutorial. Hope it helps! ๐Ÿ™‚

Reference: Supporting different languages

Tutorial #95: Resolving the Dalvik compiler limitation on 65K methods

Hello everyone!

In previous versions of the Android operating system, as applications grew in size one would encounter build errors that would indicate that the application has reached a limit of the Android application build architecture. The error would be as follows,

Conversion to Dalvik format failed:
Unable to execute dex: method ID not in [0, 0xffff]: 65536

If you observe the error closely, it specifies the number 65536. As mentioned on the developer’s page, this number is significant as it represents the total number of references that can be invoked by the code within a single Dalvik Executable (dex) bytecode file.

Developers hence need to resolve this error by adding multidex support to their application. From Android 5.0 (Lollipop) onwards, Android uses a runtime called ART which natively supports loading multiple dex files from application APK files. However, through this tutorial, we will learn one of the ways to resolve the Dalvik compiler limitation in previous (older) versions of Android.

Pre-requisites: Android Studio (version 1.0.0-rc4), Gradle plugin (version 2.2.1), latest Android SDK (with build tools installed for version 21)

Step 1: Configure application to add multidex support

Launch the Android Studio IDE and open any of your existing Android projects that contain more than 65K methods due to external libraries (.jar) files. Open the build.gradle file of your project and add the following lines of code,

apply plugin: 'com.android.application'
....
....

    compileSdkVersion 21
    buildToolsVersion '21.1.1'

    defaultConfig {
        applicationId "com.app.multidex"
        minSdkVersion 19
        targetSdkVersion 20
        versionCode 1
        versionName "1.0"

        // Enabling multidex support.
        multiDexEnabled true
    }
    buildTypes {
        release {
            runProguard true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support:multidex:1.0.0'
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile files('libs/my_large_file1.jar')
    compile files('libs/my_large_file2.jar')
}

As seen above, it is important to specify the multiDexEnabled setting in the defaultConfig, buildType, or productFlavor sections of your Gradle build file. In addition, one needs to add the multidex support library as a dependency in the dependencies section.

Step 2: Edit AndroidManifest.xml file

In the manifest file one needs to add the MultiDexApplication class from the multidex support library to the application element as follows,

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.app.multidex">
    <application
        ...
        android:name="android.support.multidex.MultiDexApplication">
        ...
    </application>
</manifest>

Finally, save all changes. Make sure no errors are present. Once the project has compiled successfully the Android build tools construct a primary dex (classes.dex) and supporting (classes2.dex, classes3.dex) as needed.

multidex_support_android

Alternatively, one can also use an opensource GitHub project that when downloaded provides a custom_rules.xml build script that can be easily integrated in any Android application.

Reference: Building apps with over 65K methods

Tutorial #94: Using Retrofit for Android

Hello friends!

In one of my previous tutorials, I had talked about using the Volley networking library in Android. However, Volley is less documented and is totally focused on handling individual, small HTTP requests.

Retrofit on the other hand makes it incredibly easy to download JSON or XML data from a web API and parse it into a Plain Old Java Object (POJO). It’s released by Square and offers very easy to use REST API’s. Through this tutorial, we will learn how to use Retrofit for Android.

Pre-requisites: Eclipse IDE, Android SDK

Step 1: Create Android project

Create a new Android application project called AndroidRetrofitExample with package name com.app.retrofit. Choose the target SDK as Android 4.4 (API level 19)

Step 2: Create REST service interface

Create a new interface called GithubApiService that will help fetch the list of contributors for a particular repository as follows,

GithubApiService.java

package com.app.retrofit;

import java.util.List;
import retrofit.http.GET;
import retrofit.http.Path;

public interface GithubApiService {
	@GET("/repos/{owner}/{repo}/contributors")
	List<Contributor> contributors(
			@Path("owner") String owner,
			@Path("repo") String repo
	);
}

Step 3: Create Activity class

Now, let’s implement our MainActivity class. We will create an AsyncTask in order to call the public Github API service.

MainActivity.java

package com.app.retrofit;

import java.util.List;
import retrofit.RestAdapter;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		new GetUserInfo().execute();
	}

	private class GetUserInfo extends AsyncTask<String, Void, String>{

		@Override
		protected String doInBackground(String... params) {
			// TODO Auto-generated method stub	
			try{ 
				RestAdapter restAdapter = new RestAdapter.Builder()
				.setEndpoint("https://api.github.com") 
				.build();

				GithubApiService github = restAdapter.create(GithubApiService.class);
				List<Contributor> contributors = github.contributors("pscholl", "glass_snippets");

				for (Contributor contributor : contributors) {
					Log.i("App", contributor.login + " - " + contributor.contributions);
				}

			}catch(Exception e){
				e.printStackTrace();
				return "failure";
			}	
           return "success";
	    }
      }
}

The RestAdapter is the class through which your API interfaces are turned into callable objects. By default, Retrofit will give you sane defaults for your platform but it allows for customization.

Contributor.java

package com.app.retrofit;

//POJO class
public class Contributor {
	
	// GitHub username
	public String login; 
	// Commit count
	public int contributions;
	
	public String getLogin() {
		return login;
	}
	public void setLogin(String login) {
		this.login = login;
	}
	public int getContributions() {
		return contributions;
	}
	public void setContributions(int contributions) {
		this.contributions = contributions;
	} 
}

Note: Make sure you have added the android.permission.INTERNET feature in the AndroidManifest.xml file. One also needs to add the following .jar files in the libs folder of the Android project.

  • retrofit-1.4.1.jar
  • okhttp-1.3.0-jar-with-dependencies.jar
  • gson-2.2.4.jar

Finally, save all changes. Make no errors are present. Run the application on an Android device and you should the following output in the Logcat window!

output

That’s it for this tutorial. Stay tuned for more! ๐Ÿ™‚

Reference: Retrofit for Android

Tutorial #92: Live Streaming using Vitamio in Android

Hello friends!

Live video streaming is one of the most challenging functionalities to incorporate in an Android application. RTMP (Real Time Messaging Protocol) was developed by Adobe for Flash Player to transmit real-time media (audio, video) between a server and flash player. However, Android does not support RTMP out of the box. Hence, developers need to use third party libraries in order to stream a RTMP video.

Vitamio is an open multimedia framework or library for Android and iOS, with full and real hardware accelerated decoder and renderer. It supports streaming network protocols such as RTSP, RTMP, HLS and can play 720p/1080p HD mp4, mkv, m4v, mov, flv, avi, rmvb and many other video formats in Android and iOS. Through this tutorial, we will learn how to implement live streaming using Vitamio in Android.

Pre-requisites: Eclipse IDE, Android SDK

Step 1: Create Android project

Launch Eclipse IDE and create a new Android application project called AndroidVitamioDemo with package name com.app.vitamio.stream. Choose the target SDK as Android 4.4

Step 2: Add reference to Vitamio bundle (library)

Before creating the streaming Activity class, one needs to download the Vitamio Bundle and add the same as a reference to the above Android project. The Vitamio library will provide the required classes to setup, initialize and play the live stream.

Step 3: Create Activity class

Create a new Activity class called LiveStreamingActivity in order to implement the streaming process. Developers can also add an EditText to obtain the path or URL of the media file. For this tutorial, I have directly provided the URL to the media file.

LiveStreamingActivity.java

package com.app.vitamio.stream;

import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.widget.MediaController;
import io.vov.vitamio.widget.VideoView;
import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;

public class LiveStreamingActivity extends Activity {

	private String pathToFileOrUrl= "rtmp://204.107.26.252:8086/live/796.high.stream";
	private VideoView mVideoView;
	
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		
		if (!LibsChecker.checkVitamioLibs(this))
			return;
		
		setContentView(R.layout.activity_video_stream);
		mVideoView = (VideoView) findViewById(R.id.surface_view);

		if (pathToFileOrUrl == "") {
			Toast.makeText(this, "Please set the video path for your media file", Toast.LENGTH_LONG).show();
			return;
		} else {

			/*
			 * Alternatively,for streaming media you can use
			 * mVideoView.setVideoURI(Uri.parse(URLstring));
			 */
			mVideoView.setVideoPath(pathToFileOrUrl);
			mVideoView.setMediaController(new MediaController(this));
			mVideoView.requestFocus();

			mVideoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
				@Override
				public void onPrepared(MediaPlayer mediaPlayer) {
					// optional need Vitamio 4.0
					mediaPlayer.setPlaybackSpeed(1.0f);
				}
			});
		}

	}
	
	public void startPlay(View view) {
		if (!TextUtils.isEmpty(pathToFileOrUrl)) {
			mVideoView.setVideoPath(pathToFileOrUrl);
		}
	}

	public void openVideo(View View) {
		mVideoView.setVideoPath(pathToFileOrUrl);
	}

}

activity_video_stream.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <Button
            android:id="@+id/start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="startPlay"
            android:text="@string/play_video"/>
    </LinearLayout>

    <io.vov.vitamio.widget.VideoView
        android:id="@+id/surface_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:onClick="openVideo"
        android:text="@string/open_video"/>

</LinearLayout>

Step 4: Add permissions to Manifest file!

One needs to update the AndroidManifest.xml file in order to include the required permissions.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.app.vitamio.stream"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="19" />
 
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:name=".LiveStreamingActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       
        <activity
            android:name="io.vov.vitamio.activity.InitActivity"
            android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"
            android:launchMode="singleTop"
            android:theme="@android:style/Theme.NoTitleBar"
            android:windowSoftInputMode="stateAlwaysHidden" />
        
    </application>

</manifest>

Save all changes. Make sure no errors are present. Run the application on an Android device and you should see the following output!

output_1

output_2

That’s it for this tutorial. Stay tuned for more! ๐Ÿ™‚

Reference: Vitamio Bundle

Using multiple processes in Android

Hello everyone!

In Android, when an application’s first component starts, a new process for the application with a single thread of execution called main thread or UI Thread gets created. By default, all components of the same application usually run in the same process. A Service when invoked using the startService method from an Activity runs on the main application thread. However, if the Service performs an intensive task then it may cause your application to become unresponsive eventually resulting in an ANR (Application Not Responding) message.

Memory management plays an important role in applications involving multiple processes. As mentioned on the Android developer’s page, one can specify a separate process for each application component by declaring the android:process attribute for each component in the manifest file. For example, one can specify a service to run in a process separate from the application’s main process by declaring a new process named background as follows.

<service android:name= ".MyBackgroundService"
         android:process= ":background" />

Through this post, we will learn how to manage and use multiple processes in Android.

Pre-requisites: Eclipse IDE, Android SDK

Step 1: Create Service class

In any of your existing Android projects, create two new Service classes named FirstService and SecondService respectively.

FirstService.java

package com.example;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class FirstService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.i("FirstService", "onStartCommand method is called");
		return Service.START_NOT_STICKY;
	}
	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		Log.i("FirstService","onCreate Method is called");
		super.onCreate();
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		Log.i("FirstService","OnDestroy Method is called");
		super.onDestroy();
	}

	@Override
	public boolean stopService(Intent name) {
		// TODO Auto-generated method stub
		Log.i("FirstService","stopService Method is called");
		return super.stopService(name);
	}

}

SecondService.java

package com.example;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

public class SecondService extends Service {

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		// TODO Auto-generated method stub
		Log.i("SecondService", "onStartCommand method is called");
		return Service.START_NOT_STICKY;
	}
	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		Log.i("SecondService","onCreate Method is called");
		super.onCreate();
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		Log.i("SecondService","OnDestroy Method is called");
		super.onDestroy();
	}

	@Override
	public boolean stopService(Intent name) {
		// TODO Auto-generated method stub
		Log.i("SecondService","stopService Method is called");
		return super.stopService(name);
	}

}

Note: Make sure you have registered the above Services in the AndroidManifest.xml file as follows!

AndroidManifest.xml

....
<application
        android:allowBackup="true"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name="com.example.TestProcessActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
         <service
            android:name="com.example.FirstService"
            android:process=":firstservice">
        </service>
        <service
            android:name="com.example.SecondService"
            android:process=":secondservice">
        </service>
</application>
....

Step 2: Create Activity class

Create a new Activity class called TestProcessActivity that will include certain methods to call the respective Services.

TestProcessActivity.java

package com.example;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class TestProcessActivity extends Activity {

	private Intent m_objServiceIntent = null; 

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

	public void startFirstService(View view){
		m_objServiceIntent = new Intent(TestProcessActivity.this, FirstService.class);
		startService(m_objServiceIntent);
	}

	public void startSecondService(View view){
		m_objServiceIntent = new Intent(TestProcessActivity.this, SecondService.class);
		startService(m_objServiceIntent);
	}
}

activity_process.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startFirstService"
        android:text="@string/call_first_service"/>

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startSecondService"
        android:text="@string/call_second_service"/>

</LinearLayout>

Finally, make sure no errors are present. Save all changes and run the application on an Android device. If no errors occur then you should be able to see the following output!

output_1

Next, you need to call each Service by clicking the respective button.

first_service

second_service

Now, try switching to the DDMS perspective in order to view the multiple processes running on the Android device.

ddms_output

Reference: Multiple Processes in Android

Tutorial #91: Setting up Eclipse for Android Wear Development

The Android Wear SDK was officially launched at Google I/O back in June 2014. Android Wear is a version of Google’s Android operating system designed for smartwatches and other wearables. It helps display notifications and integrates the Google Now functionality by pairing with mobile phones. The Wear SDK includes code samples, documentation required to build applications that directly run on wearable, create custom user interfaces and sync data between mobile phones and wearables.

However, it is important for developers to first setup their environment in order to create wearable applications. This tutorial helps to setup up the Eclipse IDE for Android Wear development.

Pre-requisites: Eclipse IDE, Android Wear SDK

Step 1: Install Android Wear SDK and support repository

Before creating wearable applications, one needs to download and install the Android Wear SDK from the SDK manager as shown below. One also needs to install the Android support repository from the Extras section.

sdk_manager_install_wear_sdk

sdk_manager_support_repository

Step 2: Create support library project

Once the Android Wear SDK has been installed successfully, one will find the wearable folder present under,

{ANDROID_SDK}\extras\google\m2repository\com\google\android\support\

Next, rename the wearable-1.0.0.aar file as wearable-1.0.0.zip and extract the .zip file to obtain the wearable Android project. Now, launch Eclipse IDE and choose File –> Import –> Existing Android code into workspace. Navigate to the path where the project exists and import the same. Finally, create a libs folder and move the classes.jar in the libs folder. Right click the imported project, go to Android, select build target as Android 4.4W.2 and tick the checkbox Is Library. This makes sure that the project is setup as a library project targeting Android API 20.

Create library project

Create library project

Our final project structure would look something like this,

Project Structure

Project Structure

One can now create a new Android Wear project, or import one of the samples that come with the SDK, and add the above Wear support library as a dependency.

That’s it for this tutorial. Stay tuned for more! ๐Ÿ™‚