Sunday, November 27, 2011

Implement Pinch Zoom in OnTouchListener

Modify the last exercise of "Implement OnTouchListener to handle multi-touch event" to implement our own pinch detection.

Implement Pinch Zoom in OnTouchListener

package com.exercise.AndroidTouchPinchZoom;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.TextView;

public class AndroidTouchPinchZoomActivity extends Activity {

TextView myTouchEvent;
ImageView myImageView;
Bitmap bitmap;
int bmpWidth, bmpHeight;

//Touch event related variables
int touchState;
final int IDLE = 0;
final int TOUCH = 1;
final int PINCH = 2;
float dist0, distCurrent;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myTouchEvent = (TextView)findViewById(R.id.touchevent);
myImageView = (ImageView)findViewById(R.id.imageview);

bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
bmpWidth = bitmap.getWidth();
bmpHeight = bitmap.getHeight();

distCurrent = 1; //Dummy default distance
dist0 = 1; //Dummy default distance
drawMatrix();

myImageView.setOnTouchListener(MyOnTouchListener);
touchState = IDLE;
}

private void drawMatrix(){
float curScale = distCurrent/dist0;
if (curScale < 0.1){
curScale = 0.1f;
}

Bitmap resizedBitmap;
int newHeight = (int) (bmpHeight * curScale);
int newWidth = (int) (bmpWidth * curScale);
resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
myImageView.setImageBitmap(resizedBitmap);
}

OnTouchListener MyOnTouchListener
= new OnTouchListener(){

@Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub

float distx, disty;

switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//A pressed gesture has started, the motion contains the initial starting location.
myTouchEvent.setText("ACTION_DOWN");
touchState = TOUCH;
break;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
myTouchEvent.setText("ACTION_POINTER_DOWN");
touchState = PINCH;

//Get the distance when the second pointer touch
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
dist0 = FloatMath.sqrt(distx * distx + disty * disty);

break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
myTouchEvent.setText("ACTION_MOVE");

if(touchState == PINCH){
//Get the current distance
distx = event.getX(0) - event.getX(1);
disty = event.getY(0) - event.getY(1);
distCurrent = FloatMath.sqrt(distx * distx + disty * disty);

drawMatrix();
}

break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
myTouchEvent.setText("ACTION_UP");
touchState = IDLE;
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
myTouchEvent.setText("ACTION_POINTER_UP");
touchState = TOUCH;
break;
}

return true;
}

};
}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:id="@+id/touchevent"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageview"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center" />

</LinearLayout>


Download the files.


Related article:
- Scale bitmap according to ScaleGestureDetector



Saturday, November 26, 2011

Implement OnTouchListener to handle multi-touch event

In this exercise, we will implement our OnTouchListener to handle the following MotionEvent:
ACTION_DOWN: A pressed gesture has started, the motion contains the initial starting location.
ACTION_POINTER_DOWN: A non-primary pointer has gone down.
ACTION_MOVE: A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
ACTION_UP: A pressed gesture has finished.
ACTION_POINTER_UP: A non-primary pointer has gone up.

It will be used to implement our pinch zoom in coming exercise.

Implement OnTouchListener to handle multi-touch event

package com.exercise.AndroidTouchPinchZoom;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.TextView;

public class AndroidTouchPinchZoomActivity extends Activity {

TextView myTouchEvent;
ImageView myImageView;
Bitmap bitmap;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myTouchEvent = (TextView)findViewById(R.id.touchevent);
myImageView = (ImageView)findViewById(R.id.imageview);

bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
myImageView.setImageBitmap(bitmap);

myImageView.setOnTouchListener(MyOnTouchListener);
}

OnTouchListener MyOnTouchListener
= new OnTouchListener(){

@Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub

switch(event.getAction() & MotionEvent.ACTION_MASK){
case MotionEvent.ACTION_DOWN:
//A pressed gesture has started, the motion contains the initial starting location.
myTouchEvent.setText("ACTION_DOWN");
break;
case MotionEvent.ACTION_POINTER_DOWN:
//A non-primary pointer has gone down.
myTouchEvent.setText("ACTION_POINTER_DOWN");
break;
case MotionEvent.ACTION_MOVE:
//A change has happened during a press gesture (between ACTION_DOWN and ACTION_UP).
myTouchEvent.setText("ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
//A pressed gesture has finished.
myTouchEvent.setText("ACTION_UP");
break;
case MotionEvent.ACTION_POINTER_UP:
//A non-primary pointer has gone up.
myTouchEvent.setText("ACTION_POINTER_UP");
break;
}

return true;
}

};
}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:id="@+id/touchevent"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageview"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center" />

</LinearLayout>


Download the files.

Thursday, November 24, 2011

Scale bitmap according to ScaleGestureDetector

In the last exercise, "Detect pinch zoom using ScaleGestureDetector" was shown. it's modified to scale a bitmap accordingly. (It's for demonstration only, may be not practical in real use.

Scale bitmap according to ScaleGestureDetector

package com.exercise.AndroidPinchZoom;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.widget.ImageView;
import android.widget.TextView;

public class AndroidPinchZoomActivity extends Activity {

TextView scaleGesture;
ImageView myImageView;
float curScale = 1F;
Bitmap bitmap;
int bmpWidth, bmpHeight;

ScaleGestureDetector scaleGestureDetector;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
scaleGesture = (TextView)findViewById(R.id.ScaleGesture);
myImageView = (ImageView)findViewById(R.id.imageview);

bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
bmpWidth = bitmap.getWidth();
bmpHeight = bitmap.getHeight();
drawMatrix();

scaleGestureDetector = new ScaleGestureDetector(this, new simpleOnScaleGestureListener());
}

private void drawMatrix(){

curScale = ((curScale - 1) * 10) + 1;
if (curScale < 0.1){
curScale = 0.1f;
}

Bitmap resizedBitmap;
int newHeight = (int) (bmpHeight * curScale);
int newWidth = (int) (bmpWidth * curScale);
resizedBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, false);
myImageView.setImageBitmap(resizedBitmap);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
scaleGestureDetector.onTouchEvent(event);
return true;
}

public class simpleOnScaleGestureListener extends SimpleOnScaleGestureListener {

@Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
curScale = detector.getScaleFactor();
scaleGesture.setText(String.valueOf(curScale));
drawMatrix();
return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
super.onScaleEnd(detector);
}

}
}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:id="@+id/ScaleGesture"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<ImageView
android:id="@+id/imageview"
android:layout_gravity="center"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="center" />
</LinearLayout>


Download the files.

Related article:
- Implement Pinch Zoom in OnTouchListener



Wednesday, November 23, 2011

Detect pinch zoom using ScaleGestureDetector

For Android 2.2 (Froyo) android.view.ScaleGestureDetector was added for processing the most commonly requested two-finger gesture: pinch zooming.

Detect pinch zoom using ScaleGestureDetector

package com.exercise.AndroidScaleGestureDetector;

import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View;
import android.widget.TextView;

public class AndroidScaleGestureDetectorActivity extends Activity {

TextView scaleGesture;
ScaleGestureDetector scaleGestureDetector;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
scaleGesture = (TextView)findViewById(R.id.ScaleGesture);

scaleGestureDetector = new ScaleGestureDetector(this, new simpleOnScaleGestureListener());
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
scaleGestureDetector.onTouchEvent(event);
return true;
}

public class simpleOnScaleGestureListener extends
SimpleOnScaleGestureListener {

@Override
public boolean onScale(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
scaleGesture.setText(String.valueOf(detector.getScaleFactor()));
return true;
}

@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
scaleGesture.setVisibility(View.VISIBLE);
return true;
}

@Override
public void onScaleEnd(ScaleGestureDetector detector) {
// TODO Auto-generated method stub
scaleGesture.setVisibility(View.INVISIBLE);
}

}
}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
<TextView
android:id="@+id/ScaleGesture"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

</LinearLayout>


Download the files.

next:
- Scale bitmap according to ScaleGestureDetector



Tuesday, November 22, 2011

Beginning Building Mobile Application Development in the Cloud


Learn to build mobile apps on the major cloud platforms

Serving as a practical guide for building mobile apps and cloud services, this book is essential reading for web developers eager to create cross-platform applications for mobile devices that are supported by the power of cloud-based services such as Amazon Web Services. Author Richard Rodger walks you through building your first app with HTML5, setting it up in the cloud, and working with cloud databases. Packed with examples that show you how to build complete apps, this book elevates your already-existing skills while also acting as a stepping stone for making the leap into mobile and cloud development.

Beginning Mobile Application Development in the Cloud:

  • Demonstrates how to get the right look and feel for your mobile apps

  • Highlights ways to enhance the user experience

  • Addresses app caching, touch events, and data storage

  • Details ways to create hybrid apps that run natively

  • Looks at how best to use JSON, REST, Oauth, jQuery, AJAX, and more

  • Shares insight as to how the Apple App Store and the Android Marketplace work

  • Offers advice for marketing, advertising, and selling your apps





Thursday, November 17, 2011

Detect swipe using SimpleOnGestureListener

Last exercise demonstrate how to "Using GestureDetector with SimpleOnGestureListener". In this exercise, we modify the SimpleOnGestureListener to detect swipe by overriding onFling() method.

Detect swipe using SimpleOnGestureListener

package com.exercise.AndroidSimpleGesture;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.widget.TextView;

public class AndroidSimpleGestureActivity extends Activity {

TextView gestureEvent;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gestureEvent = (TextView)findViewById(R.id.GestureEvent);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}

SimpleOnGestureListener simpleOnGestureListener
= new SimpleOnGestureListener(){


@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
String swipe = "";
float sensitvity = 50;

// TODO Auto-generated method stub
if((e1.getX() - e2.getX()) > sensitvity){
swipe += "Swipe Left\n";
}else if((e2.getX() - e1.getX()) > sensitvity){
swipe += "Swipe Right\n";
}else{
swipe += "\n";
}

if((e1.getY() - e2.getY()) > sensitvity){
swipe += "Swipe Up\n";
}else if((e2.getY() - e1.getY()) > sensitvity){
swipe += "Swipe Down\n";
}else{
swipe += "\n";
}

gestureEvent.setText(swipe);

return super.onFling(e1, e2, velocityX, velocityY);
}
};

GestureDetector gestureDetector
= new GestureDetector(simpleOnGestureListener);
}



Download the files.

Using GestureDetector with SimpleOnGestureListener

GestureDetector.SimpleOnGestureListener is a convenience class to extend when you only want to listen for a subset of all the gestures.

Using GestureDetector with SimpleOnGestureListener

package com.exercise.AndroidSimpleGesture;

import android.app.Activity;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.widget.TextView;

public class AndroidSimpleGestureActivity extends Activity {

TextView gestureEvent;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
gestureEvent = (TextView)findViewById(R.id.GestureEvent);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
// TODO Auto-generated method stub
return gestureDetector.onTouchEvent(event);
}

SimpleOnGestureListener simpleOnGestureListener
= new SimpleOnGestureListener(){

@Override
public boolean onDoubleTap(MotionEvent e) {
// TODO Auto-generated method stub
gestureEvent.setText("onDoubleTap: \n" + e.toString());
return super.onDoubleTap(e);
}

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// TODO Auto-generated method stub
gestureEvent.setText("onFling: \n" + e1.toString() + "\n" + e2.toString() +"\n"
+ "velocityX= " + String.valueOf(velocityX) + "\n"
+ "velocityY= " + String.valueOf(velocityY) + "\n");
return super.onFling(e1, e2, velocityX, velocityY);
}

@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
gestureEvent.setText("onLongPress: \n" + e.toString());
super.onLongPress(e);
}

@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO Auto-generated method stub
gestureEvent.setText("onSingleTapConfirmed: \n" + e.toString());
return super.onSingleTapConfirmed(e);
}

};

GestureDetector gestureDetector
= new GestureDetector(simpleOnGestureListener);
}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />

<TextView
android:id="@+id/GestureEvent"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />

</LinearLayout>


Download the files.

next:
- Detect swipe using SimpleOnGestureListener



Wednesday, November 16, 2011

App Inventor for Android: Build Your Own Apps - No Experience Required!


Create Android mobile apps, no programming required!

Even with limited programming experience, you can easily learn to create apps for the Android platform with this complete guide to App Inventor for Android. App Inventor for Android is a visual language that relies on simple programming blocks that users can drag and drop to create apps. This handy book gives you a series of fully worked-out apps, complete with their programming blocks, which you can customize for your own use or use as a starting point for creating the next killer app. And it's all without writing a single line of code. Don't miss the book's special section on Apps Inventor Design Patterns, which explains computer terms in simple terms and is an invaluable basic reference.

  • Teaches programmers and non-programmers alike how to use App Inventor for Android to create Android apps
  • Provides a series of fully worked-out apps that you can customize, download, and use on your Android phone or use as a starting point for building the next great app
  • Includes a valuable reference section on App Inventor Design Patterns and general computer science concepts
  • Shows you how to create apps that take advantage of the Android smartphone?s handy features, such as GPS, messaging, contacts, and more

With App Inventor for Android and this complete guide, you'll soon be creating apps that incorporate all of the Android smartphone's fun features, such as the accelerometer, GPS, messaging, and more.



Saturday, November 12, 2011

NDK updated for Android 4.0

Google released an updated version of the Android NDK, now in revision 7. The updated NDK lets developers who are using native code get started with the new native APIs available in Android 4.0.

details: Updated NDK for Android 4.0

Tuesday, November 8, 2011

Cancel ProgressDialog

In the exercise "Display a indeterminate progress bar on title bar", the ProgressDialog will be dismissed by BackgroundThread after a certain time. How can user stop the waiting and force the BackgroundThread stop? we can set the ProgressDialog cancelable by calling progressDialog.setCancelable(true).

Cancel ProgressDialog

It's modified to be cancelable, such that user can cancel the waiting by BACK key. And a OnCancelListener is implemented to stop BackgroundThread. Also, a OnDismissListener is implemented. OnCancelListener will be called only when the dialog is canceled, if you needs to know when it is dismissed in general, use OnDismissListener.

package com.exercise.AndroidProgressDialog;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnDismissListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Window;
import android.widget.Toast;

public class AndroidProgressDialogActivity extends Activity {

ProgressDialog progressDialog;
BackgroundThread backgroundThread;

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

requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.main);

setProgressBarIndeterminateVisibility(true);

progressDialog = ProgressDialog.show(AndroidProgressDialogActivity.this,
"ProgressDialog", "Wait!");

backgroundThread = new BackgroundThread();
backgroundThread.setRunning(true);
backgroundThread.start();

progressDialog.setCancelable(true);

progressDialog.setOnCancelListener(new OnCancelListener(){

public void onCancel(DialogInterface dialog) {
// TODO Auto-generated method stub
backgroundThread.setRunning(false);
Toast.makeText(AndroidProgressDialogActivity.this,
"ProgressDialog Cancelled!",
Toast.LENGTH_LONG).show();
}});

progressDialog.setOnDismissListener(new OnDismissListener(){

public void onDismiss(DialogInterface dialog) {
// TODO Auto-generated method stub
Toast.makeText(AndroidProgressDialogActivity.this,
"ProgressDialog Dismissed!",
Toast.LENGTH_LONG).show();
}});
}



public class BackgroundThread extends Thread{
volatile boolean running = false;
int cnt;

void setRunning(boolean b){
running = b;
cnt = 10;
}

@Override
public void run() {
// TODO Auto-generated method stub
while(running){
try {
sleep(1000);
if(cnt-- == 0){
running = false;
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
handler.sendMessage(handler.obtainMessage());
}
}

Handler handler = new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub

setProgressBarIndeterminateVisibility(false);
progressDialog.dismiss();

boolean retry = true;
while(retry){
try {
backgroundThread.join();
retry = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

Toast.makeText(AndroidProgressDialogActivity.this,
"dismissed", Toast.LENGTH_LONG).show();
}

};
}

Monday, November 7, 2011

More on onSaveInstanceState() and onRestoreInstanceState()

In the article onSaveInstanceState() and onRestoreInstanceState() described how to save and restore state in onSaveInstanceState() and onRestoreInstanceState() methods. It's another exercise, we can know more of life cycle of a activity about onSaveInstanceState() and onRestoreInstanceState().

onSaveInstanceState() and onRestoreInstanceState()

package com.exercise.AndroidSaveState;

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

public class AndroidSaveStateActivity extends Activity {

TextView textOnPause;
TextView textOnResume;
TextView textOnRestoreInstanceState;
TextView textOnSaveInstanceState;

int cntOnPause;
int cntOnResume;
int cntOnRestoreInstanceState;
int cntOnSaveInstanceState;

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

textOnPause = (TextView)findViewById(R.id.onPause);
textOnResume = (TextView)findViewById(R.id.onResume);
textOnRestoreInstanceState = (TextView)findViewById(R.id.onRestoreInstanceState);
textOnSaveInstanceState = (TextView)findViewById(R.id.onSaveInstanceState);

}

@Override
protected void onPause() {
// TODO Auto-generated method stub
cntOnPause++;
super.onPause();
}


@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();

cntOnResume++;
textOnPause.setText("cntOnPause: " + String.valueOf(cntOnPause));
textOnResume.setText("cntOnResume: " + String.valueOf(cntOnResume));
textOnRestoreInstanceState.setText("cntOnRestoreInstanceState: " + String.valueOf(cntOnRestoreInstanceState));
textOnSaveInstanceState.setText("cntOnSaveInstanceState: " + String.valueOf(cntOnSaveInstanceState));
}

@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
cntOnSaveInstanceState++;
outState.putInt("CNT_OnPause", cntOnPause);
outState.putInt("CNT_OnResume", cntOnResume);
outState.putInt("CNT_OnRestoreInstanceState", cntOnRestoreInstanceState);
outState.putInt("CNT_OnSaveInstanceState", cntOnSaveInstanceState);


super.onSaveInstanceState(outState);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
// TODO Auto-generated method stub

if (savedInstanceState != null){
if(savedInstanceState.containsKey("CNT_OnPause")){
cntOnPause = savedInstanceState.getInt("CNT_OnPause");
}

if(savedInstanceState.containsKey("CNT_OnResume")){
cntOnResume = savedInstanceState.getInt("CNT_OnResume");
}

if(savedInstanceState.containsKey("CNT_OnRestoreInstanceState")){
cntOnRestoreInstanceState = savedInstanceState.getInt("CNT_OnRestoreInstanceState");
}

if(savedInstanceState.containsKey("CNT_OnSaveInstanceState")){
cntOnSaveInstanceState = savedInstanceState.getInt("CNT_OnSaveInstanceState");
}
}
cntOnRestoreInstanceState++;

super.onRestoreInstanceState(savedInstanceState);
}

}


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

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />

<TextView
android:id="@+id/onPause"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/onResume"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/onRestoreInstanceState"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/onSaveInstanceState"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>

</LinearLayout>

Thursday, November 3, 2011

No applications can perform this action - Check Intent available and force to install

In the article "Send email using Intent.ACTION_SEND", we startActivity with intent of Intent.ACTION_SEND. I assume all Android should have gmail, or Composer, installed. But somebody complain with error of "No applications can perform this action".

We can check if any package installed to handle the intent in advance by calling queryIntentActivities(). If the returned list not ">0" (no aplications installed), open Market to install "market://details?id=com.google.android.gm".

Open Market if No applications can perform this action

Such a approach can be applied to other intent.

package com.exercise.AndroidEMail;

import java.util.List;

import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

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

final EditText edittextEmailAddress = (EditText)findViewById(R.id.email_address);
final EditText edittextEmailSubject = (EditText)findViewById(R.id.email_subject);
final EditText edittextEmailText = (EditText)findViewById(R.id.email_text);
Button buttonSendEmail_intent = (Button)findViewById(R.id.sendemail_intent);

//Preset default
edittextEmailAddress.setText("---@gmail.com");
edittextEmailSubject.setText("test");
edittextEmailText.setText("email body");

buttonSendEmail_intent.setOnClickListener(new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

String emailAddress = edittextEmailAddress.getText().toString();
String emailSubject = edittextEmailSubject.getText().toString();
String emailText = edittextEmailText.getText().toString();

String emailAddressList[] = {emailAddress};

Intent intent = new Intent(Intent.ACTION_SEND);

intent.setType("plain/text");
intent.putExtra(Intent.EXTRA_EMAIL, emailAddressList);
intent.putExtra(Intent.EXTRA_SUBJECT, emailSubject);
intent.putExtra(Intent.EXTRA_TEXT, emailText);

//Check if Intent available
List<ResolveInfo> list = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
if (list.size() >0 ){
startActivity(Intent.createChooser(intent, "Choice App to send email:"));
}else{
//Start Market to downoad and install gmail
Uri uri = Uri.parse("market://details?id=com.google.android.gm");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
startActivity(it);
}

}});
}
}


The layout, main.xml, refer to the article "Send email using Intent.ACTION_SEND".

Download the files.

Wednesday, November 2, 2011

Old AdWords API with be deprecated

AdWords API blog just posted of "120 days until deprecation deadline".

With the release of AdWords API v201109, the following versions and services will be deprecated on February 29, 2012:
  • API versions v13, v200909, v201003, v201008, v201101
  • API version v13 AccountService will only be available on a whitelist basis