Friday, October 7, 2011

Simple Android Project

main_page.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/linear1"
    android:layout_weight="1" android:orientation="vertical"
    android:layout_width="fill_parent" android:layout_height="fill_parent"
    android:background="#ffffffff" xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout android:id="@+id/linear2"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android">

        <ImageView android:id="@+id/test_image" android:src="@drawable/header"
            android:layout_width="fill_parent" android:layout_height="fill_parent" />

    </LinearLayout>
    <GridView xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/gridview" android:layout_width="wrap_content"
        android:layout_height="fill_parent" android:columnWidth="90dp"
        android:numColumns="2" android:verticalSpacing="5dp"
        android:horizontalSpacing="5dp" android:stretchMode="columnWidth"
        android:gravity="center" android:background="@layout/black_white_gradient" />

</LinearLayout>

Home.class


import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

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

        GridView gridview = (GridView) findViewById(R.id.gridview);
        gridview.setAdapter(new ImageAdapter(this));

        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,
                R.layout.custom_title);

        gridview.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View v,
                    int position, long id) {

                Intent myIntent = null;
                String positionString = "";
                switch (position) {
                case 0:
                    // FlightInfo
                    positionString = "Flight Info";
                    myIntent = new Intent(Home.this, SearchFlights.class);
                    startActivity(myIntent);
                    break;
                // case 1:
                // // AirportInfo
                // positionString = "Airport Info";
                // myIntent = new Intent(Home.this, SearchFlights.class);
                // startActivity(myIntent);
                // break;

                default:
                    break;
                }
                Toast.makeText(Home.this, "" + positionString,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }
}

ImageAdapter.class

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;

public class ImageAdapter extends BaseAdapter {

    private Context mContext;

    public ImageAdapter(Context c) {
        mContext = c;
    }

    @Override
    public int getCount() {
        return mThumbIds.length;
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

    @Override
    public long getItemId(int position) {
        return 0;
    }

    // create a new ImageView for each item referenced by the Adapter
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        if (convertView == null) { // if it's not recycled, initialize some
                                    // attributes
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(126, 126));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
        } else {
            imageView = (ImageView) convertView;
        }

        imageView.setImageResource(mThumbIds[position]);
        return imageView;
    }

    // references to our images
    private Integer[] mThumbIds = { R.drawable.flight_info,
            R.drawable.flight_schedule, R.drawable.check_in,
            R.drawable.check_out };
}

list_view.xml

<?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"
   >
<ListView
    android:id="@+id/android:list"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />
<TextView
    android:id="@+id/android:empty"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:text="@string/main_no_items"/>
</LinearLayout>

ListViewByFligh.class


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

import com.al.srv.dto.FlightVO;
import com.al.srv.parser.Parser;
import com.serendibit.flightstats.webservice.WebserviceClient;

import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class ListViewByFlight extends ListActivity {

    private ProgressDialog m_ProgressDialog = null;
    private ArrayList<FlightVO> m_flights = null;
    private FlightsAdapter m_adapter;
    private Runnable viewFlights;

    Intent myIntent = null;

    private String strAirLine, strFlightNo, strFlightFromDate, strFlightToDate;

    public static SearchByFlightNo byFlightNo;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.list_view_by_flight);

        strAirLine = byFlightNo.getStrAirLine();
        strFlightNo = byFlightNo.getStrFlightNo();
        strFlightFromDate = byFlightNo.getStrFlightFromDate();
        strFlightToDate = byFlightNo.getStrFlightToDate();

        m_flights = new ArrayList<FlightVO>();
        this.m_adapter = new FlightsAdapter(this, R.layout.row, m_flights);
        setListAdapter(this.m_adapter);

        viewFlights = new Runnable() {
            @Override
            public void run() {
                getFlights();
            }
        };
        Thread thread = new Thread(null, viewFlights, "MagentoBackground");
        thread.start();
        m_ProgressDialog = ProgressDialog.show(ListViewByFlight.this,
                "Please wait...", "Retrieving data ...", true);
    }

    private Runnable returnRes = new Runnable() {

        @Override
        public void run() {
            if (m_flights != null && m_flights.size() > 0) {
                m_adapter.notifyDataSetChanged();
                for (int i = 0; i < m_flights.size(); i++)
                    m_adapter.add(m_flights.get(i));
            }
            m_ProgressDialog.dismiss();
            m_adapter.notifyDataSetChanged();
        }
    };

    private void getFlights() {
        try {
            int count = 0;
            int objLength;
            // Call flight info web service
            WebserviceClient client = new WebserviceClient();

            String resultXML = client.getFlightInfoByFlightNo(strAirLine,
                    strFlightNo, strFlightFromDate, strFlightToDate);

            Object obj = new Parser().parse(resultXML);

            if (obj instanceof String) {
                // SearchByFlightNo.error =
                // "Incorrect parameter or Connection error";
                myIntent = new Intent(ListViewByFlight.this,
                        SearchByFlightNo.class);
                startActivity(myIntent);
            } else if (obj instanceof List) {

                m_flights = new ArrayList<FlightVO>();
                FlightVO o1;
               
                List<HashMap<String, String>> fillMaps = new ArrayList<HashMap<String, String>>();
               
                for (count = 0; count < ((List<FlightVO>) obj).size(); count++) {
                    o1 = new FlightVO();
                   
                    o1.setFlightNumber(((List<FlightVO>) obj).get(count)
                            .getAirline().getAirlineCode()
                            + ((List<FlightVO>) obj).get(count)
                                    .getFlightNumber());
                    o1.setDepartureDate(((List<FlightVO>) obj).get(count)
                            .getDepartureDate().substring(0, 10)
                            + " "
                            + ((List<FlightVO>) obj).get(count)
                                    .getDepartureDate().substring(11, 16));
                    o1.setFlyRoot("from ("
                            + ((List<FlightVO>) obj).get(count).getOrigin()
                                    .getAirportCode()
                            + ") to "
                            + "("
                            + ((List<FlightVO>) obj).get(count).getDestination()
                                    .getAirportCode() + ")");
                    m_flights.add(o1);
                }
            }
            Thread.sleep(5000);
            Log.i("ARRAY", "" + m_flights.size());
        } catch (Exception e) {
            Log.e("BACKGROUND_PROC", e.getMessage());
        }
        runOnUiThread(returnRes);
    }

    private class FlightsAdapter extends ArrayAdapter<FlightVO> {

        private ArrayList<FlightVO> items;

        public FlightsAdapter(Context context, int textViewResourceId,
                ArrayList<FlightVO> items) {
            super(context, textViewResourceId, items);
            this.items = items;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View v = convertView;
            if (v == null) {
                LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                v = vi.inflate(R.layout.row, null);
            }

            FlightVO o = items.get(position);
            if (o != null) {
                TextView tvFlightNo = (TextView) v
                        .findViewById(R.id.flightNumber);
                TextView tvDateTime = (TextView) v.findViewById(R.id.dateTime);
                if (tvFlightNo != null) {
                    tvFlightNo.setText("Flight: " + o.getFlightNumber());
                }
                if (tvDateTime != null) {
                    tvDateTime.setText("Fly Date: "
                            + o.getDepartureDate()
                            + "\nRoot: "
                            + o.getFlyRoot());
                }
            }
            return v;
        }
    }
}

WebserviceClient.class

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

import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import android.util.Log;

public class WebserviceClient {

    String URLbase = "http://www.pathfinder-xml.com/development/xml?Service=FlightHistoryGetRecordsService&login.accountID=6686&login.userID=nhliyanage&login.password=nandika";

    private String call(String url) {
        Log.i("URL :", url);
        String content = null;
        HttpClient mClient = new DefaultHttpClient();
        HttpGet get = new HttpGet(url);
        try {
            mClient.execute(get);
            HttpResponse res = mClient.execute(get);

            InputStream is = res.getEntity().getContent();
            BufferedReader rd = new BufferedReader(new InputStreamReader(is));
            String line;
            StringBuffer response = new StringBuffer();
            while ((line = rd.readLine()) != null) {
                response.append(line);
                response.append('\r');
            }
            rd.close();

            content = response.toString();

            System.out.println("content :" + content);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    public String getFlightInfoByFlightNo(String airlineCode, String flightNo,
            String fromDate, String toDate) {
        String url = URLbase
                + "&info.specificationFlights[0].airline.airlineCode="
                + airlineCode + "&info.specificationFlights[0].flightNumber="
                + flightNo
                + "&info.specificationFlights[0].searchCodeshares=true"
                + "&info.specificationDateRange.departureDateTimeMin="
                + fromDate + "T00:00"
                + "&info.specificationDateRange.departureDateTimeMax=" + toDate
                + "T23:59";

        return call(url);
    }

    public String getFlightInfoByDestination(String from, String to,
            String fromDate, String toDate) {

        String url = URLbase
                + "&info.specificationDepartures[0].airport.airportCode="
                + from + "&info.specificationArrivals[0].airport.airportCode="
                + to + "&info.specificationDateRange.departureDateTimeMin="
                + fromDate + "T00:00"
                + "&info.specificationDateRange.departureDateTimeMax=" + toDate
                + "T23:59"
                + "&info.flightHistoryGetRecordsRequestedData.codeshares=true";

        return call(url);
    }
}

Thursday, October 6, 2011

How to develop games on Android

One of the biggest markets in smart phones is games.  I am sure most of us would have played around with “Angry Birds” .  In this games tutorial you will learn different methods of developing  “Android Games”
At the end of the post is the video on how the code below works and the zip of all the source files used in this example.
Android Games can be developed in 3 ways
1)   Developing games using Android/Java libraries
2)   Developing games using External Libraries like OpenGL
3)   Developing games by porting existing c-game (pc game) to android.
Before getting started I assume that the reader is familiar with the basics of android programming like using activity, creating views, handling motion/touch events etc.
Also, apart from android sdk and eclipse some other tools are required for the game development. Details of the tools required, how to configure and use them will be explained as and when required. I will recommend to check out our last posts

Before starting with the game development lets understand the term “Main Loop”.

Main Loop:

Not just android any game written on any language consists of a set of custom views which are constantly refreshed/re-painted/re-drawn.  In some games we need to update the screen or re-draw the view elements at regular intervals.
E.g. consider two games (i) Android Snake Game (ii) Android Number Game for kids as shown.

In android snake game the snake has to move irrespective of the user input. Here the screen has to regularly re-drawn/re-painted to create the motion effect of the snake. Where as in the Number  game we need not redraw the screen now and then. The screen must be updated/re-drawn only when the user clicks/touches the screen.
So in the snake game we must re-draw the screen at regular intervals say 1sec in order to do that we make use of threads, while loops etc. and create a refresh/re-draw handler that updates screen regularly. This refresh/re-draw loop created is called “Main Loop”.
Note: It’s not that all games require Main Loop it depends on the game you are building.
In order to understand refresh handler we shall build a
“Ball Game” . In this game the user gets some points when ever he clicks on the ball, when the ball in the circle of the same color,  here I’m neither going to explain the score logic nor the UI events all that I’m going to focus on is the game view and refresh handler.


Before dicing into this code, I will recommend going through

Creating a Custom Widget in android

public class ballview extends View{
/* Refer to the above link for more information on using View class */
int width,height;
private Paint circlePaint,circlePaint1,circlePaint2,circlePaint3;
private Drawable[] mDrawable;
/* This will start your Refresh Handler  */
private RefreshHandler mRedrawHandler=new RefreshHandler();
private int[] posx,posy;
/* Create different constructors for your view class */
public ballview(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initCalview();
}
/* Create different constructors for your view class */
public ballview(Context context, AttributeSet attrs) {
super(context, attrs);
initCalview();
}
/* Create different constructors for your view class */
public ballview(Context context) {
super(context);
initCalview();
}
Initialize all the variables. The image files can be downloaded from the zip file attached to the post.
Here the random function is used to select random position on the screen while the balls are falling down from the top

protected void initCalview()
{
mDrawable=new Drawable[4];
posx=new int[16];
posy=new int[16];
mDrawable[0] = this.getResources().getDrawable(R.drawable.bluen);
mDrawable[1] = this.getResources().getDrawable(R.drawable.greenn);
mDrawable[2] = this.getResources().getDrawable(R.drawable.redn);
mDrawable[3] = this.getResources().getDrawable(R.drawable.yellown);
for(int i=0;i<16;i++)
{
posx[i]=(int)(Math.random() * (250- 1 + 1) ) + 1;
posy[i]=(int)(Math.random() * (250 – 1 + 1) ) + 1;
}
}
More information on onMeasure method  can be found on Creating a Custom Widget in android
@Override
protected void onMeasure(int widthSpec, int heightSpec)
{
int specWidth=MeasureSpec.getSize(widthSpec);
int specHeight=MeasureSpec.getSize(heightSpec);
setMeasuredDimension(specWidth,specHeight);
set_pos();
}
public void set_pos()
{
int x;
int px=this.getMeasuredWidth();
int py=this.getMeasuredHeight();
width=(int)px;
height=(int)py;
this.layout(width/2, 0, 0, 0);
/* Initialize different paint objects to draw circles of different colors */
circlePaint=new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint.setColor(Color.BLUE);
circlePaint.setStrokeWidth(1);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint1=new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint1.setStrokeWidth(1);
circlePaint1.setStyle(Paint.Style.STROKE);
circlePaint2=new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint2.setStrokeWidth(1);
circlePaint2.setStyle(Paint.Style.STROKE);
circlePaint3=new Paint(Paint.ANTI_ALIAS_FLAG);
circlePaint3.setStrokeWidth(1);
circlePaint3.setStyle(Paint.Style.STROKE);
}
@Override
protected void onDraw(Canvas canvas)
{
int rad=Math.min(width/4, height/4);
/* Paint the circle objects on the canvas */
canvas.drawCircle(width/4,height/4, rad, circlePaint);
circlePaint1.setColor(Color.RED);
canvas.drawCircle(width-rad,height-rad, rad, circlePaint1);
circlePaint2.setColor(Color.YELLOW);
canvas.drawCircle(width-rad,height/4,rad/2, circlePaint2);
circlePaint3.setColor(Color.GREEN);
canvas.drawCircle(width/4,height-rad, rad/2, circlePaint3);
//int i=0;
for(int i=0;i<8;i++)
{
mDrawable[0].setBounds(posx[i],posy[i],posx[i]+35,posy[i]+35);
mDrawable[0].draw(canvas);
i++;
mDrawable[1].setBounds(posx[i],posy[i],posx[i]+35,posy[i]+35);
mDrawable[1].draw(canvas);
i++;
mDrawable[2].setBounds(posx[i],posy[i],posx[i]+35,posy[i]+35);
mDrawable[2].draw(canvas);
i++;
mDrawable[3].setBounds(posx[i],posy[i],posx[i]+35,posy[i]+35);
mDrawable[3].draw(canvas);
/* Refresh Handler re-draws the view and at this point waits/stops for a sec and after the
waiting period it again redraws the view and the process repeats.
Thus your screen gets updated regularly and you feel as if balls are falling from the top*/
mRedrawHandler.sleep(1000);
}
}
RefreshHandler class : This class updates the view every sec. It increments the Y-axis value and selects the
random X-axis position every second and repaints the screen this creates a feel as if  balls are falling from the top
class RefreshHandler extends Handler {
@Override
public void handleMessage(Message msg) {
for(int i=0;i<8;i++)
{
posy[i]=posy[i]+20;
posx[i]=(int)(Math.random() * (250- 1 + 1) ) + 1;
if(posy[i]<height)
{}
else
{posy[i]=0;}
}
ballview.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
}
/* Now Create an Activity and Display the View created above */
public class main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LinearLayout linear=new LinearLayout(this);
ballview ball= new ballview(this);
linear.addView(ball);
setContentView(linear);
}
}

Parsing JSON Results in Android

One of the common requirements for an android programmer is how to parse incoming JSON results from another web service or application into android. JSON is a data interchange format, and serves the same purpose as that of XML. XML is slowly being replaced by JSON because it’s easy to parse, light weight, and efficient too. We had talked about SOAP vs JSON earlier and not going to go into details again.
Let us focus on how to parse JSON results in Android.
We can parse JSON by 2 methods
i)  Using JSONObject and JSONTokener classes provided by Android SDK.
ii) Using external libraries. E.g GSON, Jackson etc.
(for more details refer http://json.org)
Let’s consider JSON data of the form:
{
“name”: “myName”,
“message”: ["myMessage1","myMessage2"],
“place”: “myPlace”,
“date”:  ”thisDate”
}

Parsing JSON data using JSONTokener


public
class main extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try {
/* Inflate TextView from the layout */
TextView tv = (TextView)findViewById(R.id.TextView01);
/* JSON data considered as an example. Generally this data is obtained
from a web service.*/
String json = “{”
+ “  \”name\”: \”myName\”, ”
+ “  \”message\”: [\"myMessage1\",\"myMessage2\"],”
+ “  \”place\”: \”myPlace\”, ”
+ “  \”date\”: \”thisDate\” ”
+ “}”;
/* Create a JSON object and parse the required values */
JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
String name = object.getString(“name”);
String place = object.getString(“place”);
String date = object.getString(“date”);
JSONArray message = object.getJSONArray(“message”);
tv.setText(“Name: “+ name +”\n\n”);
tv.append(“Place: “+ place +”\n\n”);
tv.append(“Date: “+ date +”\n\n”);
for(int i=0;i<message.length();i++)
{
tv.append(“Message: “+ message.getString(i) +”\n\n”);
}
} catch (JSONException e) {e.printStackTrace();}
catch(Exception ex){ex.printStackTrace();}
}
}