Wednesday, November 23, 2011

How to send Email in Android ?

Mail m = new Mail("USER EMAIL", "PASSWORD"); 
String[] toArr = {"EMAIL-1", "EMAIL-2"}; 
m.setTo(toArr); 
m.setFrom("USER EMAIL"); 
m.setSubject("This is an email sent using my Mail JavaMail wrapper from an Android device."); 
m.setBody("Email body"); 
try { 
  m.addAttachment("/sdcard/bday.jpg"); 
  if(m.send()) { 
    Toast.makeText(this, "Email was sent successfully.", Toast.LENGTH_LONG).show(); 
  } else { 
    Toast.makeText(this, "Email was not sent.", Toast.LENGTH_LONG).show(); 
  } 
} catch(Exception e) { 
  Log.e("MailApp", "Could not send email", e); 
} 

Wednesday, November 16, 2011

Saturday, September 24, 2011

how to read line by line data from txt file in Android(JAVA) ?


URL url = new URL(
"http://dangalula.free.fr/psgtube/lesplusvues.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(
url.openStream()));

String theLine;
while ((theLine = in.readLine()) != null) {
Log.v("log_tag","line is :" +theLine);
}

Saturday, August 27, 2011

JSON Parsing in Android

1. Android and JSON

1.1. Android and JSON

JSON is a very condense data exchange format. Android includes the json.org libraries which allow to work easily with JSON files.

1.2. Twitter

Twitter is a great source for JSON. You can just call a URI and retrieve JSON. Here are some examples:

Table 1. Twitter URI's
URIDescription
http://twitter.com/statuses/user_timeline/vogella.json Get the timeline of user vogella .
http://search.twitter.com/search.json?q=android Search for the term "android" on Twitter.
http://twitter.com/statuses/user_timeline/vogella.json Returns the user data of user vogella .

Please note that some URI's return a JSONObject object while others return a JSONArray. The following coding uses an URI which returns an JSONArray.

2. Reading JSON

Create a new Android project "de.vogella.android.twitter.json" with the package "de.vogella.android.twitter.json" and the activity "ParseJSON".
Create the following coding for the activity. This will download the twitter feed for the user http://twitter.com/ and write the number of entries and the text messages to the Android log file.

package de.vogella.android.twitter.json;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

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

public class ParseJSON extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		String readTwitterFeed = readTwitterFeed();
	try {
		JSONArray jsonArray = new JSONArray(readTwitterFeed);
		Log.i(ParseJSON.class.getName(),
			"Number of entries " + jsonArray.length());
		for (int i = 0; i < jsonArray.length(); i++) {
		JSONObject jsonObject = jsonArray.getJSONObject(i);
		Log.i(ParseJSON.class.getName(), jsonObject.getString("text"));
		}
	} catch (Exception e) {
		e.printStackTrace();
	}
}

	public String readTwitterFeed() {
	StringBuilder builder = new StringBuilder();
	HttpClient client = new DefaultHttpClient();
	HttpGet httpGet = new HttpGet(
		"http://twitter.com/statuses/user_timeline/vogella.json");
	try {
		HttpResponse response = client.execute(httpGet);
		StatusLine statusLine = response.getStatusLine();
		int statusCode = statusLine.getStatusCode();
		if (statusCode == 200) {
			HttpEntity entity = response.getEntity();
			InputStream content = entity.getContent();
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(content));
			String line;
			while ((line = reader.readLine()) != null) {
				builder.append(line);
			}
		} else {
		Log.e(ParseJSON.class.toString(), "Failed to download file");
	}
	} catch (ClientProtocolException e) {
		e.printStackTrace();
	} catch (IOException e) {
		e.printStackTrace();
	}
	return builder.toString();
}
} 
 
 
To run this example assign the uses-permission to your "AndroidManifest.xml" for "android.permission.INTERNET".

3. Write JSON

Writing JSON is very simple. Just create the JSONObject or JSONArray and use the toString() method.
 

public void writeJSON() {
	JSONObject object = new JSONObject();
	try {
		object.put("name", "Jack Hack");
		object.put("score", new Integer(200));
		object.put("current", new Double(152.32));
		object.put("nickname", "Hacker");
	} catch (JSONException e) {
		e.printStackTrace();
	}
	System.out.println(object);
}
	

How to create LIve Wallpaper in Android ?

1.1. Live Wallpapers

Live Wallpapers are animated, interactive backgrounds for the Android homescreen. A live wallpaper is similar to other Android applications and can use most of the same functionality.
To create a live wallpaper, you need to create a XML file which describes your wallpaper. This file should contain a description of the application and can contain a preview and a link to a preference activity which allow to customize the live wallpaper.
You also maintain a service which must extend the abstract class "WallpaperService". "WallpaperService"is the base class for all live wallpapers in the system. You must implement the method onCreateEngine() and return an object of type "Engine". This engine will handle the life cycle events, animations and drawings of the wallpaper. Here you find life cycle hooks, as for example onCreate(), onSurfaceCreated(), onVisibilityChanged(), onOffsetsChanged(), onTouchEvent() and onCommand() .
This service requires the permission "android.permission.BIND_WALLPAPER" and must be registered via an intent-filter to the action "android.service.wallpaper.WallpaperService".
The service returns in its method onCreateEngine() an instance "android.service.wallpaper.WallpaperService.Engine". This class is responsible for handling the Live Wallpaper.
You should also maintain in your "AndroidManifest.xml" that your application uses the feature "android.software.live_wallpaper" in "AndroidManifest.xml". This will prevent that your wallpaper can be installed on devices which do not support live wallpapers.

2. Android Wallpaper Example

Create a new project "de.vogella.android.wallpaper". Do not create an Activity.
Create the folder "/res/xml" and maintain the following file "mywallpaper.xml. This file contains a description of your wallpaper and a preview graphic. You can also maintain a link to an activity which allow to configure your wallpaper. This resource file will be linked to from the "AndroidManifest.xml". You could also include the "android:thumbnail attribute" which would point to a drawable which gives a smaller image of the running wallpaper.


<?xml version="1.0" encoding="UTF-8"?>
<wallpaper 
xmlns:android="http://schemas.android.com/apk/res/android"
android:thumbnail="@drawable/icon"
android:description="@string/wallpaper_description"  
android:settingsActivity="de.vogella.android.wallpaper.MyPreferencesActivity"/>
 
	
Change your "AndroidManifest.xml" to the following to define your service ""MyWallpaperService" and to maintain the uses-feature. 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="de.vogella.android.wallpaper" android:versionCode="1"
	android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">


<service android:name="MyWallpaperService" android:enabled="true"
android:permission="android.permission.BIND_WALLPAPER"
android:label="Wallpaper Example ">
	<intent-filter>
<action android:name="android.service.wallpaper.WallpaperService"></action>
		</intent-filter>
	<meta-data android:name="android.service.wallpaper"
		android:resource="@xml/mywallpaper"></meta-data>
</service>
<activity android:label="@string/app_name" android:name=".MyPreferencesActivity"
	android:theme="@android:style/Theme.Light.WallpaperSettings"
	android:exported="true">
	</activity>
	</application>
	<uses-sdk android:minSdkVersion="10" />
	<uses-feature android:name="android.software.live_wallpaper"
		android:required="true"></uses-feature>

</manifest> 
 
We create the class "MyPoint" to save the elements we have drawn. 
 
				
package de.vogella.android.wallpaper;

public class MyPoint {
	String text;
	private int x;
	private int y;

	public MyPoint(String text, int x, int y) {
		this.text = text;
		this.x = x;
		this.y = y;
	}
}

			
		
Create a preference activity . Create the preference value files "prefs.xml" in the folder "res/xml".

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
	<CheckBoxPreference android:key="touch"
		android:title="Enable Touch"></CheckBoxPreference>
	<EditTextPreference android:key="numberOfCircles"
		android:title="Number of Circles"></EditTextPreference>
</PreferenceScreen>
 
 
Create the following coding for your preference activity. 
 
 
package de.vogella.android.wallpaper;

import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.PreferenceActivity;
import android.widget.Toast;

public class MyPreferencesActivity extends PreferenceActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		addPreferencesFromResource(R.xml.prefs);

	// We want to add a validator to the number of circles so that it only
	// accepts numbers
	Preference circlePreference = getPreferenceScreen().findPreference(
			"numberOfCircles");

		// Add the validator
	circlePreference.setOnPreferenceChangeListener(numberCheckListener);
	}

	/**
	 * Checks that a preference is a valid numerical value
	 */
	Preference.OnPreferenceChangeListener numberCheckListener = 
new OnPreferenceChangeListener() {

	@Override
	public boolean onPreferenceChange(Preference preference,
Object newValue) {
		// Check that the string is an integer
		if (newValue != null && newValue.toString().length() > 0
				&& newValue.toString().matches("\\d*")) {
			return true;
		}
		// If now create a message to the user
		Toast.makeText(MyPreferencesActivity.this, "Invalid Input",
				Toast.LENGTH_SHORT).show();
			return false;
	}
	};

} 
 
Create the following coding for the Wallpaper Service. 
 
package de.vogella.android.wallpaper;

import java.util.ArrayList;
import java.util.List;

import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.service.wallpaper.WallpaperService;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class MyWallpaperService extends WallpaperService {

	@Override
	public Engine onCreateEngine() {
		return new MyWallpaperEngine();
	}

	private class MyWallpaperEngine extends Engine {
		private final Handler handler = new Handler();
		private final Runnable drawRunner = new Runnable() {
			@Override
			public void run() {
				draw();
			}

		};
		private List<MyPoint> circles;
		private Paint paint = new Paint();
		private int width;
		int height;
		private boolean visible = true;
		private int maxNumber;
		private boolean touchEnabled;

		public MyWallpaperEngine() {
	SharedPreferences prefs = PreferenceManager
		.getDefaultSharedPreferences(MyWallpaperService.this);
		maxNumber = Integer
			.valueOf(prefs.getString("numberOfCircles", "4"));
			touchEnabled = prefs.getBoolean("touch", false);
			circles = new ArrayList<MyPoint>();
			paint.setAntiAlias(true);
			paint.setColor(Color.WHITE);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeJoin(Paint.Join.ROUND);
			paint.setStrokeWidth(10f);
			handler.post(drawRunner);
		}

		@Override
		public void onVisibilityChanged(boolean visible) {
			this.visible = visible;
			if (visible) {
				handler.post(drawRunner);
			} else {
				handler.removeCallbacks(drawRunner);
			}
		}

		

}
 
			@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			this.visible = false;
			handler.removeCallbacks(drawRunner);
		}

		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format,
				int width, int height) {
			this.width = width;
			this.height = height;
			super.onSurfaceChanged(holder, format, width, height);
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			if (touchEnabled) {

				float x = event.getX();
				float y = event.getY();
				SurfaceHolder holder = getSurfaceHolder();
				Canvas canvas = null;
				try {
					canvas = holder.lockCanvas();
			if (canvas != null) {
				canvas.drawColor(Color.BLACK);
				circles.clear();
				circles.add(new MyPoint(
				String.valueOf(circles.size() + 1), x, y));
				drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
				holder.unlockCanvasAndPost(canvas);
			}
			super.onTouchEvent(event);
		}
	}
		private void draw() {
	SurfaceHolder holder = getSurfaceHolder();
	Canvas canvas = null;
		try {
			canvas = holder.lockCanvas();
			if (canvas != null) {
				if (circles.size() >= maxNumber) {
					circles.clear();
				}
			int x = (int) (width * Math.random());
			int y = (int) (height * Math.random());
	circles.add(new MyPoint(String.valueOf(circles.size() + 1),
						x, y));
					drawCircles(canvas, circles);
				}
			} finally {
				if (canvas != null)
					holder.unlockCanvasAndPost(canvas);
			}
			handler.removeCallbacks(drawRunner);
			if (visible) {
				handler.postDelayed(drawRunner, 5000);
			}
		}

		// Surface view requires that all elements are drawn completely
		private void drawCircles(Canvas canvas, List<MyPoint> circles) {
			canvas.drawColor(Color.BLACK);
			for (MyPoint point : circles) {
			canvas.drawCircle(point.x, point.y, 20.0f, paint);
			}
		}
	}
 
 
If you start your application and set this wallpaper as
background your emulator should similar to the following. If you have
Touch enabled via the preferences you can click on the screen to
remove the existing circles, also via the settings you can define the
number of circles which should be displayed.
	 
 
 


Sunday, August 14, 2011

Android Cloud to Device Messaging (C2DM)

1. Cloud to device messaging

1.1. Cloud to device messaging

As of Android 2.2 the Android platform allows to push information from the Internet to an Android phone. This service is called "Cloud to Device messaging" or short C2DM.
The standard mechanism for an application which requires data from the internet is to poll periodically for the data. This uses unnecessary network bandwidth and consumes the battery. Therefore pushing data over the internet to the phone might be a preferable solution.
C2DM is intented to notify the mobile phone that new data is available. It is not intended to send lots of data, as the size of the message is limited to 1024 characters. The push service would notify the mobile device and the Android application would fetch the data from somewhere else.
C2DM uses existing connections the Google servers which is highly optimize to minimize bandwidth and battery consumption.

1.2. Requirements

C2MD only works as of Android 2.2 and requires that Android Market application is installed on the device.
To use C2DM on the Android simulator you also need to use a Google device with API 8 or higher and to register with a Google account on the emulator via the Settings.
To use C2DM on your Android device you have to write an Android application. This application will register at a Google server to be able to receive push messages from the Google server. Your server registers with the Google server and authenticates himself. The server can then push messages to the Google server which in return pushes the information to the device. For this authentication you require a Google user.

1.3. Information flow

In a C2DM you have three involved parties. The server which wants to push messages to the Android device (application server), the Google C2DM servers and the Android application.
To enable C2DM for an Android application, this application must registers with the Google C2DM servers. After registration it receives a registration ID from the Google C2DM servers.
The Android application must then send the registration ID to its application server. The application server stores the registration ID. This registration ID is required to send the information later to this specific device.
When the application server needs to push a message to the Android applicatoin, it sends the message via HTTP GET to Google’s C2DM servers.
The C2DM servers route the message to the device once the device is available, and an Intent broadcast is sent to the Android application. The app is started and processes the message in its defined Intent Receiver.

1.4. More details

To register your application for the C2DM service your application needs to fire an registration intent "com.google.android.c2dm.intent.REGISTER" to the C2DM server. This intent includes a Google mail address and the application ID.
Upon successful registration the C2DM server broadcast a REGISTRATION Intent to the application which includes a registration ID. Each registration ID represents a particular device, e.g. every Android phone will receive its own registration code.
The C2DM may refresh this registration ID periodically but until a refreshment your application should store this ID for later use.
Google provides a small set of classes which makes the registration as smooth as possible.
After the Android application received its unique registration ID it has to send this information to the application server.The application server can use this registration ID to send messages to the Android application on the specific device.

1.5. Send messages

With your registration ID the application server can send messages to your device. This requires in addition an authorization token which needs to be generated for each per Android application and registration ID.
The authorization token is generated by sending a post HTTPS request to the C2DM servers.
To send a message to the Android application on a specific device the application server sends a HTTP GET request to the Google C2DM servers. This HTTP GET request contain the registration ID (to address a specific device) and well as the authentication token (to tell Google that this server is allowed to send messages).
Once your server sends the message to the C2DM server this server will queue the message until the device is available. The message will be send to the device as a broadcast. Your application needs to register for this broadcast event to receive the message.

1.6. Receiving a Message

If a message is received on a device via a broadcast event. The system extracts the raw key/value pairs from the message. It then passes the key/value pairs to the Android application in a "com.google.android.c2dm.intent.RECEIVE" Intent. The data can be received from the Intent via getExtras(). The available keys are "payload", "from", "collapse_key". THe actual data is included in "payload">. The Android application extracts this data and can react to it.

1.7. Permissions

To use C2DM in your application to have to register for the permission "com.google.android.c2dm.permission.RECEIVE" and in most cases also "android.permission.INTERNET" permission. Your application should also declare the permission "applicationPackage + ".permission.C2D_MESSAGE" with the "android:protectionLevel" of "signature" so that other applications cannot register and receive message for the application. android:protectionLevel="signature". ensures that applications with request a permission must be signed with same certificate as the application that declared the permission.
Your application must also register as an intent receiver for the two intents "com.google.android.c2dm.intent.RECEIVE" and "com.google.android.c2dm.intent.REGISTRATION". "com.google.android.c2dm.SEND"

2. Device and Registration

Currently C2DM is under beta testing. You need to ask for access. Here is the link to the signup form .
If you test the following example on the emulator make sure that you use Google API 8 or later. You also have to register a Google user on the virtual device under "Settings"-> "Accounts Sync".
If you test the following example on a real device make sure that the Android Market is installed.

3. Example

3.1. Project

Create the Android project "de.vogella.android.c2dm" with the activity "RegisterActivity". Create the following layout "main.xml" which will allow to enter your registered user id for C2DM and send a registration request to the C2DM servers.

				
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<EditText android:id="@+id/editText1" android:layout_height="wrap_content"
android:layout_width="match_parent" android:hint="Enter C2DM registered User"
	></EditText>
	<LinearLayout android:layout_height="match_parent"
		android:layout_width="match_parent" android:id="@+id/linearLayout1"
		android:gravity="center">
	<Button android:id="@+id/button1" android:text="Register for Push
Notification"
	android:layout_height="wrap_content" android:onClick="register"
			android:layout_width="wrap_content"></Button>
	</LinearLayout>

</LinearLayout>

			

3.2. Create the Google utility classes

Create the following three classes from Google. Unfortunately I was not able to find a downloadable package of these classes at Google therefore I reproduce them.

				
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import java.io.IOException;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;

/**
 * Base class for C2D message receiver. Includes constants for the strings used
 * in the protocol.
 */
public abstract class C2DMBaseReceiver extends IntentService {
	private static final String C2DM_RETRY =
"com.google.android.c2dm.intent.RETRY";

	public static final String REGISTRATION_CALLBACK_INTENT =
"com.google.android.c2dm.intent.REGISTRATION";
	private static final String C2DM_INTENT =
"com.google.android.c2dm.intent.RECEIVE";

	// Logging tag
	private static final String TAG = "C2DM";

	// Extras in the registration callback intents.
	public static final String EXTRA_UNREGISTERED = "unregistered";

	public static final String EXTRA_ERROR = "error";

	public static final String EXTRA_REGISTRATION_ID = "registration_id";

public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";

	// wakelock
	private static final String WAKELOCK_KEY = "C2DM_LIB";

	private static PowerManager.WakeLock mWakeLock;
	private final String senderId;

	/**
	 * The C2DMReceiver class must create a no-arg constructor and pass the
	 * sender id to be used for registration.
	 */
	public C2DMBaseReceiver(String senderId) {
		// senderId is used as base name for threads, etc.
		super(senderId);
		this.senderId = senderId;
	}

	/**
	 * Called when a cloud message has been received.
	 */
	protected abstract void onMessage(Context context, Intent intent);

	/**
	 * Called on registration error. Override to provide better error messages.
	 * 
	 * This is called in the context of a Service - no dialog or UI.
	 */
	public abstract void onError(Context context, String errorId);

	/**
	 * Called when a registration token has been received.
	 */
	public void onRegistered(Context context, String registrationId)
			throws IOException {
		// registrationId will also be saved
	}

	/**
	 * Called when the device has been unregistered.
	 */
	public void onUnregistered(Context context) {
	}

	@Override
	public final void onHandleIntent(Intent intent) {
	try {
		Context context = getApplicationContext();
		if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
			handleRegistration(context, intent);
		} else if (intent.getAction().equals(C2DM_INTENT)) {
			onMessage(context, intent);
		} else if (intent.getAction().equals(C2DM_RETRY)) {
			C2DMessaging.register(context, senderId);
			}
		} finally {
			// Release the power lock, so phone can get back to sleep.
			// The lock is reference counted by default, so multiple
			// messages are ok.

			// it should use it's own lock.
			mWakeLock.release();
		}
	}

	/**
	 * Called from the broadcast receiver. Will process the received intent,
	 * call handleMessage(), registered(), etc. in background threads, with a
	 * wake lock, while keeping the service alive.
	 */
	static void runIntentInService(Context context, Intent intent) {
		if (mWakeLock == null) {
			// This is called from BroadcastReceiver, there is no init.
			PowerManager pm = (PowerManager) context
					.getSystemService(Context.POWER_SERVICE);
			mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
					WAKELOCK_KEY);
		}
		mWakeLock.acquire();

	// Use a naming convention, similar with how permissions and intents are
	// used. Alternatives are introspection or an ugly use of statics.
		String receiver = context.getPackageName() + ".C2DMReceiver";
		intent.setClassName(context, receiver);

		context.startService(intent);

	}

	private void handleRegistration(final Context context, Intent intent) {
		final String registrationId = intent
				.getStringExtra(EXTRA_REGISTRATION_ID);
		String error = intent.getStringExtra(EXTRA_ERROR);
		String removed = intent.getStringExtra(EXTRA_UNREGISTERED);

		if (Log.isLoggable(TAG, Log.DEBUG)) {
		Log.d(TAG, "dmControl: registrationId = " + registrationId
			+ ", error = " + error + ", removed = " + removed);
		}

		if (removed != null) {
			// Remember we are unregistered
			C2DMessaging.clearRegistrationId(context);
			onUnregistered(context);
			return;
		} else if (error != null) {
			// we are not registered, can try again
			C2DMessaging.clearRegistrationId(context);
			// Registration failed
			Log.e(TAG, "Registration error " + error);
			onError(context, error);
		if ("SERVICE_NOT_AVAILABLE".equals(error)) {
			long backoffTimeMs = C2DMessaging.getBackoff(context);

			Log.d(TAG, "Scheduling registration retry, backoff = "
					+ backoffTimeMs);
				Intent retryIntent = new Intent(C2DM_RETRY);
			PendingIntent retryPIntent = PendingIntent
			.getBroadcast(context, 0 /* requestCode */, retryIntent,
					0 /* flags */);

	AlarmManager am = (AlarmManager)
context.getSystemService(Context.ALARM_SERVICE);
			am.set(AlarmManager.ELAPSED_REALTIME, backoffTimeMs,
						retryPIntent);

				// Next retry should wait longer.
				backoffTimeMs *= 2;
				C2DMessaging.setBackoff(context, backoffTimeMs);
			}
		} else {
			try {
			onRegistered(context, registrationId);
			C2DMessaging.setRegistrationId(context, registrationId);
			} catch (IOException ex) {
			Log.e(TAG, "Registration error " + ex.getMessage());
			}
		}
	}
}

			

				
package com.google.android.c2dm;
/*
 */


import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * Helper class to handle BroadcastReciver behavior.
 * - can only run for a limited amount of time - it must start a real service 
 * for longer activity
 * - must get the power lock, must make sure it's released when all done.
 * 
 */
public class C2DMBroadcastReceiver extends BroadcastReceiver {
    
    @Override
    public final void onReceive(Context context, Intent intent) {
        // To keep things in one place.
        C2DMBaseReceiver.runIntentInService(context, intent);
        setResult(Activity.RESULT_OK, null /* data */, null /* extra */);        
    }
}

			

				
package com.google.android.c2dm;
/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * Utilities for device registration.
 *
 * Will keep track of the registration token in a private preference.
 */
public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = 
"com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT =
"com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE =
"last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";


    // package
    static final String PREFERENCE = "com.google.android.c2dm";
    
    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        // TODO: if intent not found, notification on need to have GSF
    }

    /**
     * Unregister the application. New messages will be blocked by server.
     */
    public static void unregister(Context context) {
        Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
        regIntent.setPackage(GSF_PACKAGE);
        regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
PendingIntent.getBroadcast(context,
                0, new Intent(), 0));
        context.startService(regIntent);
    }

    /**
     * Return the current registration id.
     *
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not complete.
     */
    public static String getRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        String registrationId = prefs.getString("dm_registration", "");
        return registrationId;
    }

    public static long getLastRegistrationChange(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
    }
    
    static long getBackoff(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
    }
    
    static void setBackoff(Context context, long backoff) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putLong(BACKOFF, backoff);
        editor.commit();

    }

    // package
    static void clearRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", "");
        editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
        editor.commit();

    }

    // package
    static void setRegistrationId(Context context, String registrationId) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", registrationId);
        editor.commit();

    }
}

			

3.3. Getting ready for registration

Create the following class "C2DMReceiver" which we will later register as a service.

				
package de.vogella.android.c2dm;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.c2dm.C2DMBaseReceiver;

public class C2DMReceiver extends C2DMBaseReceiver {
	public C2DMReceiver() {
		// Email address currently not used by the C2DM Messaging framework
		super("dummy@google.com");
	}

	@Override
	public void onRegistered(Context context, String registrationId)
			throws java.io.IOException {
		// The registrationId should be send to your applicatioin server.
		// We just log it to the LogCat view
		// We will copy it from there
		Log.e("C2DM", "Registration ID arrived: Fantastic!!!");
		Log.e("C2DM", registrationId);
	};

	@Override
	protected void onMessage(Context context, Intent intent) {
		Log.e("C2DM", "Message: Fantastic!!!");
		// Extract the payload from the message
		Bundle extras = intent.getExtras();
		if (extras != null) {
			System.out.println(extras.get("payload"));
			// Now do something smart based on the information
		}
	}

	@Override
	public void onError(Context context, String errorId) {
		Log.e("C2DM", "Error occured!!!");
	}

}

			
Create the following "AndroidManifest.xml". That will register the right receivers and requests the right permissions.

				
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="de.vogella.android.c2dm" android:versionCode="1"
	android:versionName="1.0">
<permission android:name="de.vogella.android.c2dm.permission.C2D_MESSAGE"
	android:protectionLevel="signature" />
<uses-permission android:name="de.vogella.android.c2dm.permission.C2D_MESSAGE" />
<!-- Permissions -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />


<application android:icon="@drawable/icon" android:label="@string/app_name">
	<activity android:name="RegisterActivity" 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=".C2DMReceiver" />
	<!-- Only C2DM servers can send messages for the app. If permission is 
		not set - any other app can generate it -->
	<receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
		android:permission="com.google.android.c2dm.permission.SEND">
		<!-- Receive the actual message -->
<intent-filter>
		<action android:name="com.google.android.c2dm.intent.RECEIVE" />
		<category android:name="de.vogella.android.c2dm" />
</intent-filter>
		<!-- Receive the registration id -->
<intent-filter>
	<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
	<category android:name="de.vogella.android.c2dm" />
</intent-filter>
	</receiver>
</application>
</manifest>
			
Change your Activity to the following. In this activity you can enter your registered Google C2DM user and ask for a registration key.

				
package de.vogella.android.c2dm;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import com.google.android.c2dm.C2DMessaging;

public class RegisterActivity extends Activity {
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	}

	public void register(View view) {
		Log.e("Super", "Starting registration");
		Toast.makeText(this, "Starting", Toast.LENGTH_LONG).show();
		EditText text = (EditText) findViewById(R.id.editText1);

		C2DMessaging.register(this, text.getText().toString());
	}
}
			

3.4. Register your application

Run your application, maintain your registered user and press the button. Check LogCat for the registration ID.
If you see " Unable to start service Intent { act=com.google.android.c2dm.intent.REGISTER pkg=com.google.android.gsf (has extras) }: not found" message make sure you are using a Google device and that you have a Google user registered on the device.

3.5. Using curl

If you run a Linux system you can easily test the service on teh command line. You can request your authentication key via curl on the command line. From the response get the part after "Auth=".

				
curl https://www.google.com/accounts/ClientLogin -d 
Email=your_user -d "Passwd=your_password" -d 
accountType=GOOGLE -d source=Google-cURL-Example -d service=ac2dm
			
This part and the registration code can be used to send a message to your device.

				
curl --header "Authorization: GoogleLogin auth=your_authenticationid" "
https://android.apis.google.com/c2dm/send"
-d registration_id=your_registration -d "data.payload=This data will be send
to your application as payload" -d collapse_key=0

			

3.6. Create your server ;-) application

As described earlier the application server needs to get an authentication key via HTTPS. Afterwards it can send messages to the device via HTTP by supplying the authentication key and the registration ID.
To avoid that you have to learn about server side Java programming we will "simulate" the server by another Android application. In general you could create a server side application for example with the Google App Engine .
Create a new Android application "de.vogella.android.c2dm.server" with the Activity "ServerSimulator". Assign the permission "android.permission.INTERNET" to this activity.
Change "main.xml" to the following.

				
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
<Button android:text="Get Authentication" android:id="@+id/button1"
	android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="getAuthentification"></Button>
<Button android:text="Show Authentication" android:id="@+id/button2"
android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="showAuthentification"></Button>
<EditText android:layout_width="match_parent" android:id="@+id/registrationId"
	android:hint="Registration Code" android:layout_height="wrap_content"></EditText>
<Button android:text="Send Message" android:id="@+id/button3"
	android:layout_width="wrap_content" android:layout_height="wrap_content"
	android:onClick="sendMessage"></Button>
</LinearLayout>

			
Change "ServerSimulator" to the following. Make your to use your registration key, your user and your password.

				
package de.vogella.android.c2dm.server;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

public class ServerSimulator extends Activity {
	private SharedPreferences prefManager;
	private final static String AUTH = "authentication";

	private static final String UPDATE_CLIENT_AUTH = "Update-Client-Auth";

	public static final String PARAM_REGISTRATION_ID = "registration_id";

	public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";

	public static final String PARAM_COLLAPSE_KEY = "collapse_key";

	private static final String UTF8 = "UTF-8";

	// Registration is currently hardcoded
	private final static String YOUR_REGISTRATION_STRING = "APA91bFQut1tqA-
nIL1ZaV0emnp4Rb0smwCkrMHcoYRXeYVtIebJgrzOHQj0h76qKRzd3bC_JO37uJ
0NgTcFO87HS9V7YC-yOP774pm0toppTHFO7Zc_PAw";

	private SharedPreferences prefs;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
	prefManager = PreferenceManager.getDefaultSharedPreferences(this);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.mymenu, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.menuitem_user:
			Intent intent = new Intent(this, UserPreferences.class);
			startActivity(intent);
			break;
		default:
			break;
		}
		return false;
	}

	public void getAuthentification(View view) {
		SharedPreferences prefs = PreferenceManager
				.getDefaultSharedPreferences(this);

		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(
				"https://www.google.com/accounts/ClientLogin");

		try {

		List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
		nameValuePairs.add(new BasicNameValuePair("Email", prefs.getString(
				"user", "undefined")));
		nameValuePairs.add(new BasicNameValuePair("Passwd", prefs
				.getString("password", "undefined")));
		nameValuePairs.add(new BasicNameValuePair("accountType", "GOOGLE"));
		nameValuePairs.add(new BasicNameValuePair("source",
				"Google-cURL-Example"));
		nameValuePairs.add(new BasicNameValuePair("service", "ac2dm"));
			post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
		HttpResponse response = client.execute(post);
		BufferedReader rd = new BufferedReader(new InputStreamReader(
				response.getEntity().getContent()));
			String line = "";
		while ((line = rd.readLine()) != null) {
			Log.e("HttpResponse", line);
			if (line.startsWith("Auth=")) {
				Editor edit = prefManager.edit();
				edit.putString(AUTH, line.substring(5));
				edit.commit();
				String s = prefManager.getString(AUTH, "n/a");
				Toast.makeText(this, s, Toast.LENGTH_LONG).show();
			}
			}
	} catch (IOException e) {
		e.printStackTrace();
	}
}
	public void showAuthentification(View view) {
	String s = prefManager.getString(AUTH, "n/a");
	Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}

public void sendMessage(View view) {
	try {
		Log.e("Tag", "Started");
		String auth_key = prefManager.getString(AUTH, "n/a");
		// Send a sync message to this Android device.
		StringBuilder postDataBuilder = new StringBuilder();
		postDataBuilder.append(PARAM_REGISTRATION_ID).append("=")
				.append(YOUR_REGISTRATION_STRING);
			// if (delayWhileIdle) {
		// postDataBuilder.append("&").append(PARAM_DELAY_WHILE_IDLE)
		// .append("=1");
			// }
		postDataBuilder.append("&").append(PARAM_COLLAPSE_KEY).append("=")
				.append("0");

	postDataBuilder.append("&").append("data.payload").append("=")
			.append(URLEncoder.encode("Lars war hier", UTF8));

		byte[] postData = postDataBuilder.toString().getBytes(UTF8);

			// Hit the dm URL.

		URL url = new URL("https://android.clients.google.com/c2dm/send");
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setDoOutput(true);
		conn.setUseCaches(false);
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type",
		"application/x-www-form-urlencoded;charset=UTF-8");
		conn.setRequestProperty("Content-Length",
				Integer.toString(postData.length));
		conn.setRequestProperty("Authorization", "GoogleLogin auth="
				+ auth_key);
		OutputStream out = conn.getOutputStream();
		out.write(postData);
		out.close();

		int responseCode = conn.getResponseCode();

		Log.e("Tag", String.valueOf(responseCode));
		// Validate the response code
			if (responseCode == 401 || responseCode == 403) {
		// The token is too old - return false to retry later, will
		// fetch the token
		// from DB. This happens if the password is changed or token
		// expires. Either admin
		// is updating the token, or Update-Client-Auth was received by
		// another server,
		// and next retry will get the good one from database.
			Log.e("C2DM", "Unauthorized - need token");
		}
			// Check for updated token header
		String updatedAuthToken = conn.getHeaderField(UPDATE_CLIENT_AUTH);
	if (updatedAuthToken != null && !auth_key.equals(updatedAuthToken)) {
		Log.i("C2DM",
		"Got updated auth token from datamessaging servers: "
						+ updatedAuthToken);
			Editor edit = prefManager.edit();
			edit.putString(AUTH, updatedAuthToken);
		}
		String responseLine = new BufferedReader(new InputStreamReader(
				conn.getInputStream())).readLine();
	// NOTE: You *MUST* use exponential backoff if you receive a 503
		// response code.
	// Since App Engine's task queue mechanism automatically does this
	// for tasks that
	// return non-success error codes, this is not explicitly
	// implemented here.
	// If we weren't using App Engine, we'd need to manually implement
	// this.
	if (responseLine == null || responseLine.equals("")) {
			Log.i("C2DM", "Got " + responseCode
					+ " response from Google AC2DM endpoint.");
			throw new IOException(
						"Got empty response from Google AC2DM endpoint.");
			}

			String[] responseParts = responseLine.split("=", 2);
			if (responseParts.length != 2) {
				Log.e("C2DM", "Invalid message from google: " + responseCode
						+ " " + responseLine);
				throw new IOException("Invalid response from Google "
						+ responseCode + " " + responseLine);
			}

			if (responseParts[0].equals("id")) {
				Log.i("Tag", "Successfully sent data message to device: "
						+ responseLine);
			}

			if (responseParts[0].equals("Error")) {
				String err = responseParts[1];
				Log.w("C2DM",
						"Got error response from Google datamessaging endpoint: "
								+ err);
				// No retry.
				throw new IOException(err);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
}
			

Monday, July 25, 2011

Implicit Intent For Android Developer


We have seen in Part 2 how to use Explicit Intents to invoke activities through a very simple example. Now, we will move on to a more interesting concept of Implicit Intents and Intent Filters. 

This requires a little of theoretical understanding before we move on to an example. 

As described earlier, an implicit intent does not name a target component that should act upon the intent. I 
also said that the android platform resolves as to which component is best suited to respond to an Implicit Intent. How does this happen?

Basically, an Intent object has the following information (among other things like Component name, extras and flags) which is of interest for implicit intents:
  • Action
  • Category
  • Data
So, the android platform compares these 3 (action, category and data) to something called "Intent Filters" that are declared by probable target components who are willing to accept Implicit Intent calls.
i.e. Intent Filters are the way of any component to advertise its own capabilities to the Android system. This is done declaratively in the AndroidManifest.xml file.

So here are some important points to remember:
  1. Implicit Intents do not specify a target component
  2. Components willing to receive implicit intents have to declare their ability to handle a specific intent by declaring intent filters
  3. A component can declare any number of Intent Filters
  4. There can be more than one component that declares the same Intent Filters and hence can respond to the same implicit intent. In that case the user is presented both the component options and he can choose which one he wants to continue with
  5. You can set priorities for the intent filters to ensure the order of responses.
There are 3 tests conducted in order to match an intent with intent filters:
  1. Action Test
  2. Category Test
  3. Data Test
For more details about them, you may visit the Android developer documentation here.

Finally we shall look at declaring an implicit intent in one activity which will invoke one of the native activities of the platform by matching the intent filters declared by the same.

The complete code for a very simple implicit intent example that has been described in this article is available for download here.

The InvokeImplicitIntent Activity creates an implicit intent object "contacts". This intent object's component is not set. However, the action is set to "android.content.intent.ACTION_VIEW" and the data's URI is set to "People.CONTENT_URI". 

Such an intent matches with the intent filter declared by the view contacts native activity.
So, when you run this application, it displays the native UI for viewing the existing contacts on the phone!

Here is the relevant piece of code for the same:
           Button viewContacts = (Button)findViewById(R.id.ViewContacts);
        
            viewContacts.setOnClickListener(new OnClickListener() {
            
             public void onClick(View v) {
              Intent contacts = new Intent();
              contacts.setAction(android.content.Intent.ACTION_VIEW);
              contacts.setData(People.CONTENT_URI);
              startActivity(contacts);
             }
            });


In this manner many of the native applications can be seamlessly invoked as one of the activities in our applications through implicit intents.

Explicit Intent For Android

Having introduced you to the basic anatomy of an android application in the Part 1 of the series, I would like to show you an example where communication between 2 activities happens through an intent.



However, just one more detail to be introduced as promised and that is -
There are 2 types of intents that Android understands.
1. Explicit Intent
2. Implicit Intent


In an Explicit intent, you actually specify the activity that is required to respond to the intent. In other words, you explicitly designate the target component. This is typically used for application internal messages.


In an Implicit intent (the main power of the android design), you just declare an intent and leave it to the platform to find an activity that can respond to the intent. Here, you do not declare the target component and hence is typically used for activating components of other applications seamlessly
Note: Here for simplicity sake I tell an activity responds to an intent, it could as well be other types of components.
Now I will jump into the example which you can download from here:


This example has 2 activities:
1. InvokingActivity
2. InvokedActivity
The InvokingActivity has a button "Invoke Next Activity" which when clicked explicitly calls the "InvokedActivity" class.
The relevant part of the code is here:

        Button invokingButton = (Button)findViewById(R.id.invokebutton);
        invokingButton.setOnClickListener(new OnClickListener() {
        

        
public void onClick(View v) {
        
Intent explicitIntent = new Intent(InvokingActivity.this,InvokedActivity.class);
         startActivity(explicitIntent);
        
}
        });

As explained in part 1 of the series, this is very much like an API call with compile time binding.
NOTE: The layout for InvokingActivity is defined in main.xml and for InvokedActivity in InvokedActivity.xml. The downloadable example can be opened in Eclipse Ganymede as an android project and can be executed.
In the next part of the series, we will see how to work with implicit intents which also needs us to understand intent-filters

The Uniqueness of Android – The Mobile Platform from Google

There is a huge furore and expectation in the mobile industry, after the announcement of “Android”, the mobile platform (consisting of an operating system, middleware and key applications), by Google last November. Its ripple effect in the developer community and the services industry is also pretty palpable.

In Google’s typical way, the 10 million dollar awards towards the best android applications had added the required momentum to bring the large developer community into its fold, and at the same time, harvest collective intelligence.

What is so unique about Android?
We have had many mobile OS like the Symbian, Windows Mobile OS, Mac OS X and Linux based Palm Nova, ALP etc, already flooding the market and fragmenting it.
So, why is another OS required?
If we are talking about something very radical, iPhone did it all. It took the market by storm with its mind-boggling features.
With all these in the market, isn’t android late? Still, why the interest?

Market Buy-in


Google has first of all roped in most of the players in the mobile market be it mobile operators, handset manufacturers, mobile application providers or software companies and even some commercialization companies.

The only significant players who are missing from the list for obvious reasons are:
1. Microsoft
2. Apple
3. Nokia
4. Palm
Two Major National US carriers missing are AT&T and Verizon.

Google is planning to keep it open source, the most happening way to get the acceptance in the market

Technically,
1. It is the only OS so far that allows integration / use of the basic phone features with any externally developed mobile application,
2. It is designed to simplify the reuse of components. Any application can publish its capabilities and any other application can use it. Taking the cue from the enterprise world on service oriented architecture, android has built it into its fundamental architecture
3. It is built on the open Linux Kernel
4. It will be open source
5. Mashup-ready: Embed browser and map views directly in your applications

To the End User:
1. Choice will be unlimited. The end user is not tied to using the basic features of the phone as provided by the handset manufacturer. He could use his own choice from any third party provider as the default feature of the phone
2. The ability to seamlessly integrate core and new applications will produce a new generation of unheard-of applications that are going to change the way the mobile would be used. Like for example, the ability to alert friends who are in the vicinity about your presence or the ability to be notified of all the shops that have a product you are looking for, as you enter a mall.
3. The open platform should provide more cheaper and innovative mobile devices and services
4. Application life cycle and automatic process management frees the end user from worrying about what applications are active
5. Location based services and notifications
6. Standard 2d and 3d graphics with optional acceleration
7. High quality audio/video support including H.264 (AVC).
8. Low power operation

Android - Technically, What Is It?

Android - Technically what is it?
    (an overivew of what is Android and its uniqueness is discussed in another post below)
    1. It is an operating system for mobile
    2. Interestingly, it is not yet another OS, but component based
      OS
    3. It has an integrated Java Virtual Machine
    4. System interfaces are exposed only through Java libraries
    5. It is based on the Linux Kernel
    6. An Android SDK is provided to let developers build applications on top of the OS but not extend the OS itself
    7. The SDK includes
      • Android packages
      • sample files
      • Documentation
      • A Simulator
The offical site of Android broadly classfies the technical parts of the OS as
consisting of
  1. Linux Kernel
  2. Android Runtime
  3. Libraries
  4. Application Framework
over which the core applications run as well as the applications developed by anyone using this framework

iPad stays top tablet: Faces growing Android challenge as Amazon circles

Apple’s iPad continues to dominate the tablet market, with global shipments up to 9.3m units according to the latest figures, though its percentage marketshare has declined by over 33-percent thanks to increasing competition from consumer-centric rivals. 61.3-percent of tablets sold worldwide run iOS, analysts Strategy Analytics calculate, with Android taking the lion’s share of what remains, at 30.1-percent.




Android’s rise from Q2 2010 is significant, with the Google platform on just 2.9-percent of the tablets a year ago and seeing shipping numbers of just 100,000 units. The analysts now believe that 4.6m Android tablets were sold in Q2, not least because of the dedicated tablet version of the OS, 3.x Honeycomb, which was designed specifically to suit larger-screen devices.




Despite generally negative reviews, Windows 7 holds third place, with a reported 4.6-percent of the marketshare. That puts it ahead of QNX, the platform RIM has used for the BlackBerry PlayBook, which lags in fourth place with 3.3-percent. Strategy Analytics blame the absence of key apps like a native email client on the PlayBook for its placement
Since the figures detail Q2 2011 – April through June inclusive – newer slates like the HP TouchPad don’t get a look in. Looking forward, the Android tablet segment is open to a single, dominant vendor, and there are suggestions that it could well be Amazon. “If Amazon decides to enter the Android tablet category later this year, that will bring fresh excitement and buzz to the Android community” Neil Mawston, Director at Strategy Analytics suggests, ”but Amazon will need to deliver a truly standout offering if it really wants to make headway against the popular iPad.”


Wednesday, July 20, 2011

Samsung launches new Galaxy Tab to rival iPad

Samsung Electronics Co launched a thinner and lighter version of its Galaxy tablet in its lucrative home market, trying to halt the runaway success of Apple Inc's iPad.

The Galaxy Tab 10.1 is an upgraded version of the 7-inch Tab introduced in October.

Blockbuster iPad sales announced by Apple Tuesday underscore the challenge for the South Korean company.

Apple sold 14 million iPads in the first half of the year, compared with analysts' sales estimates of about 7.5 million units for the Galaxy Tab over 2011.

"As our smartphone business grew very fast within a very short period of time, I believe it's just a matter of time for our tablet business to improve," J.K. Shin, head of Samsung's mobile division, told reporters.

Samsung is Apple's nearest rival in the booming mobile device industry as it leverages its cost competitiveness and access to chips and core tablet components.

It has sharply narrowed the gap with Apple in the smartphone market, but remains a distant second in the tablet market, which research firm Gartner forecasts will surge to 108 million devices next year from an estimated 70 million in 2011.

"Apple's quarterly results showed again it's indeed the strongest rival to beat. Samsung will have a tough second half due to growing competition from Apple as it is set to introduce a new iPhone," said James Song, an analyst at Daewoo Securities. 





Blockbuster sales of the iPhone and iPad again helped Apple crush Wall Street's expectations for its third-quarter results. Apple said Tuesday that concern over iPad 2 supply constraints had eased and demand was still outstripping supply in some markets.

The sale of the Tab in Korea is Samsung's fifth launch after its U.S. debut a month ago and its sales kickoff in Indonesia, where the company says it commands a 65 percent market share. It has also launched the device in Italy and Sweden.

Pricing for the new product, slightly thinner and lighter than the iPad 2, starts from $500 in the U.S. market, the same price as the iPad 2.

Samsung faces the challenge of moving beyond being a hardware company, clever at copying ideas, to becoming more creative and better adept at software at a time when consumer gadgets are getting smarter.

Second-quarter profit at Samsung, the world's largest maker of memory chips and televisions, fell by a quarter as weak earnings at its flat screen unit dragged, overshadowing robust sales from its mobile division.

Samsung reiterated Wednesday it aimed to boost tablet sales by more than five fold this year. It didn't provide specific numbers but analysts expect the company to have sold about 1.5 million units last year.



Tuesday, July 19, 2011

Custom ListView For Android

Now, we shall look at creating a custom list view – a custom layout, a custom row layout and how we bind the custom data holder to these layouts. (please see the earlier article on simple list view for the fundamentals of list view).

So, here again, we start with extending ListActivity.

public class MyCustomListView extends ListActivity {

Now let us create the custom list view in an xml file – custom_list_view.xml. This will be set using the setContentView() in onCreate() method:
<ListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000fff"
android:layout_weight="2"
android:drawSelectorOnTop="false">
</ListView>
<TextView android:id="@id/android:empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#FFff00"
android:text="No data"
/>
Note that it must contain a ListView object with android:id="@id/android:list"

I have also declared a TextView which should be whon when the list if empty by declaring with android:id="@id/android:empty"

Now we will declare how each row in this ListView should be displayed by creating a new xml file – custom_row_view.xml

I plan to have 3 items one below the other in each row. So, here is the declaration for the same:
<TextView android:id="@+id/text1"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#FFFF00"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<TextView android:id="@+id/text2"
android:textSize="12sp"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="fill_parent"/>
<TextView android:id="@+id/text3"
android:typeface="sans"
android:textSize="14sp"
android:textStyle="italic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
So, now, how do I tie all of this together? The MyCustomListView class, the listview layout and the row layout. Just like in the earlier example, we need a ListAdpater object. Here I plan to use a SimpleAdpater provided by the SDK.

An adapter expects the context, the layout and the handle to the data that needs to be displayed. So, let us create a list of data in an ArrayList of HashMaps. This way, the HashMap can store any amount of data.

static final ArrayList<HashMap<String,String>> list =

new ArrayList<HashMap<String,String>>();
This is just a declaration of the list object. We need to populate it with data. Our custom row layout expects each row to have 3 prices of data…

This list is populated in a method as shown below with the 3 keys as ‘pen’, ‘price’ and ‘color’:

private void populateList() {
HashMap<String,String> temp = new HashMap<String,String>();
temp.put("pen","MONT Blanc");
temp.put("price", "200.00$");
temp.put("color", "Silver, Grey, Black");
list.add(temp);
HashMap<String,String> temp1 = new HashMap<String,String>();
temp1.put("pen","Gucci");
temp1.put("price", "300.00$");
temp1.put("color", "Gold, Red");
list.add(temp1);
HashMap<String,String> temp2 = new HashMap<String,String>();
temp2.put("pen","Parker");
temp2.put("price", "400.00$");
temp2.put("color", "Gold, Blue");
list.add(temp2);
HashMap<String,String> temp3 = new HashMap<String,String>();
temp3.put("pen","Sailor");
temp3.put("price", "500.00$");
temp3.put("color", "Silver");
list.add(temp3);
HashMap<String,String> temp4 = new HashMap<String,String>();
temp4.put("pen","Porsche Design");
temp4.put("price", "600.00$");
temp4.put("color", "Silver, Grey, Red");
list.add(temp4);
}
So, now how do we tie up the data with the row layout and the listview layout. It is in this simple piece of code in the onCreate() method of MyCustomListView class:
setContentView(R.layout.custom_list_view);
SimpleAdapter adapter = new SimpleAdapter(
this,
list,
R.layout.custom_row_view,
new String[] {"pen","price","color"},
new int[] {R.id.text1,R.id.text2, R.id.text3}

);
populateList();


setListAdapter(adapter);
Here we have set the default view to custom_list_view.
Then, using the SimpleAdapter, we have set the context, the list containing the data for display, the custom_row_view, the keys by which the data has to be fetched from the list, the TextViews into which the corresponding data has to be displayed.

Now execute and you will have a custom list view. Here is what you will get to see:


NOTE: if you do not populate the list with any data, you will see another view – the empty listview that we have defined in the custom_list_view.xml

You can download the complete source code for this example here.

Added later:
Based on a question below, I would like to add that an item can be clicked even in this custom list and the even captured by overriding the onListItemClick() method on the ListActivity class.

Here is the piece of code you can add to my sample, if you haev downlaoded and ti will toast a message on what has been selected:



protected void onListItemClick(ListView l, View v, int position, long id) {

    super.onListItemClick(l, v, position, id);
    Object o = this.getListAdapter().getItem(position);
    String pen = o.toString();
    Toast.makeText(this, "You have chosen the pen: " + " " + pen, Toast.LENGTH_LONG).show();
}