Monday, August 10, 2009

Android Parsing JSON

1. Parse the live JSON response from http://beer.androidph.com/beerws. See App Engine Generating JSON.

2. Use the code from Android Networking Tutorial

3. JSON Parser Code



JSONArray parseArray = new JSONArray(message);
for (int i = 0; i < jo =" parseArray.getJSONObject(i);


4. Modify nextscreen.xml of the source code from #2.


















5. Modify NextScreen.java from source code in #2 or Android Networking Tutorial


public class NextScreen extends Activity implements OnClickListener {
private Button btnBack;

private ViewGroup.LayoutParams layoutName;
private ViewGroup.LayoutParams layoutAddress;
private ViewGroup.LayoutParams layoutPrice;
private ViewGroup.LayoutParams layoutLastCallTime;
private ViewGroup.LayoutParams layoutLocation;

private String message;
private LinearLayout linearLayout;
private LinearLayout linearInnerLayout;

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

public void initComponents() {

linearLayout = (LinearLayout) findViewById(R.id.linearLayout);
linearInnerLayout = (LinearLayout) linearLayout.getChildAt(0);

layoutName = ((TextView) linearInnerLayout.getChildAt(0))
.getLayoutParams();
layoutAddress = ((TextView) linearInnerLayout.getChildAt(1))
.getLayoutParams();
layoutPrice = ((TextView) linearInnerLayout.getChildAt(2))
.getLayoutParams();
layoutLastCallTime = ((TextView) linearInnerLayout.getChildAt(3))
.getLayoutParams();
layoutLocation = ((TextView) linearInnerLayout.getChildAt(4))
.getLayoutParams();

message = getIntent().getStringExtra(NetworkConnection.NC_RESPONSE);

LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);

try {
JSONArray parseArray = new JSONArray(message);
for (int i = 0; i < parseArray.length(); i++) {
JSONObject jo = parseArray.getJSONObject(i);

LinearLayout newRow = new LinearLayout(this);
newRow.setLayoutParams(linearInnerLayout.getLayoutParams());

String name = jo.getString("name") + " | ";
TextView curTvName = new TextView(this);
curTvName.setText(name);
curTvName.setLayoutParams(layoutName);

String address = jo.getString("address") + " | ";
TextView curTvAddress = new TextView(this);
curTvAddress.setText(address);
curTvAddress.setLayoutParams(layoutAddress);

String price = jo.getString("price") + " | ";
TextView curTvPrice = new TextView(this);
curTvPrice.setText(price);
curTvPrice.setLayoutParams(layoutPrice);

String lastCallTime = jo.getString("lastCallTimeString") + " | ";
TextView curTvLastCallTime = new TextView(this);
curTvLastCallTime.setText(lastCallTime);
curTvLastCallTime.setLayoutParams(layoutLastCallTime);

String location = jo.getString("latitude") + "," + jo.getString("longitude");
TextView curTvLocation = new TextView(this);
curTvLocation.setText(location);
curTvLocation.setLayoutParams(layoutLocation);

newRow.addView(curTvName);
newRow.addView(curTvAddress);
newRow.addView(curTvPrice);
newRow.addView(curTvLastCallTime);
newRow.addView(curTvLocation);

linearLayout.addView(newRow, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

@Override
public void onClick(View v) {
if (v == btnBack) {
finish();
}
}
}



6. Screenshot of the android JSON parser.

Friday, August 7, 2009

Generating JSON with Java App Engine

1. Requirements App Engine 1.2.2. (The older version of App Engine has a problem compiling the org.json package)

2. Download Java JSON.zip from http://www.json.org

3. Read my first app engine tutorial.

4. Generate a JSON string from any java.util.List of Objects. For this instance I used the objects from #3.


public class BeerWS extends HttpServlet {

public void process(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
resp.setContentType("text/plain");
PersistenceManager persistenceManager = PMF.get()
.getPersistenceManager();
String query = "select from " + Beer.class.getName()
+ " order by name asc";
List beerLocations = (List)persistenceManager.newQuery(query).execute();
List list = new ArrayList();
for(Beer beer : beerLocations) {
//Add the pojo as a JSONObject
list.add(new JSONObject(beer));
}
//Create a JSONArray based from the list of JSONObejcts
JSONArray jsonArray = new JSONArray(list);
//Then output the JSON string to the servlet response
resp.getWriter().println(jsonArray.toString());
}

public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
process(req, resp);
}

public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException, ServletException {
process(req, resp);
}
}


5. Here's the link of the live demo. http://beer.androidph.com/beerws. Below is the object used if you have not already check my previous app engine tutorial.


@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Beer {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String name;
@Persistent
private Double price;
@Persistent
private Date lastCallTime;
@Persistent
private String promotions;
@Persistent
private String address;
@Persistent
private Double longitude;
@Persistent
private Double latitude;

public Beer() {}

public Beer(String name, Double price,
Date lastCallTime, String promotions,
String address, Double longitude, Double latitude) {
this.name = name;
this.price = price;
this.lastCallTime = lastCallTime;
this.promotions = promotions;
this.address = address;
this.longitude = longitude;
this.latitude = latitude;
}

public Beer(Long id, String name, Double price,
Date lastCallTime, String promotions, String address,
Double longitude, Double latitude) {
this.id = id;
this.name = name;
this.price = price;
this.lastCallTime = lastCallTime;
this.promotions = promotions;
this.address = address;
this.longitude = longitude;
this.latitude = latitude;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Date getLastCallTime() {
return lastCallTime;
}
public void setLastCallTime(Date lastCallTime) {
this.lastCallTime = lastCallTime;
}
private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
public String getLastCallTimeString() {
if(lastCallTime != null) {
return sdf.format(lastCallTime);
} else {
return "-";
}
}
public String getPromotions() {
return promotions;
}
public void setPromotions(String promotions) {
this.promotions = promotions;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
}

Tuesday, July 21, 2009

Google App Engine - Spring Integration Issues

I'm currently building the server component of Beer Radar using Google App Engine and Spring Framework 3.0.

I've encountered these problems below and posting their corresponding solutions.

Problem:
Google App Engine cannot read JSTL and Spring Expression on JSPs

Solution:
Google App Engine has isELIgnored="true" by default.
Just add the isELIgnored="false" on the <%@page section in the JSP.


Problem:
org.springframework.web.servlet.tags.RequestContextAwareTag doStartTag: access denied (java.lang.RuntimePermission getClassLoader)

Solution:
Add the following on your Controller

For Annotated controllers use
@InitBinder
public void initLogin(WebDataBinder binder) {
System.out.println("initLogin");
binder.registerCustomEditor(String.class, new StringTrimmerEditor
(false));
}

For sub-classed controllers
Override the InitBinder method and register string trimmed editor in the binder
binder.registerCustomEditor(String.class, new StringTrimmerEditor
(false));


Problem:
org.springframework.web.HttpSessionRequiredException: Session attribute '' required - not found in session

and/or

Uncaught exception from servlet
java.lang.RuntimeException: java.io.NotSerializableException:

Solution :
implements Serializable {
private static final long serialVersionUID = <generated value>L

The unserialized Session object will work on development but, it will throw a java.io.NotSerializableException on the appspot server.


Problem :
Attempt was made to manually set the id component of a Key primary key. If you want to control the value of the primary key, set the name component instead.

Solution :
This happens when I update a child object. The solution for this, is to re-attach the parent object first, by
Parent attached = pm.getObjectById(Parent.class, detachedParent.getId());
then
Modify the child
attached.getChild().setName("new value");
then
pm.makePersistent(attachedDrunkard);

This solved the problem for me. Here's a link of a couple of alternative solutions.

Tuesday, April 14, 2009

App Engine Tutorial : Creating the Beer Radar Server component

App Engine App #1.0 - Server Component for Android Application Beer Radar.

I created the Beer Radar Android Application last February 2009, and I was looking for a suitable Java server application. I don't know when it was announced, but I just heard about the Google App Engine support for Java last week (April 10, 2009).

Here's the basic App Engine Application which I'll connect the Beer Radar app soon. :) http://beer.androidph.com/

I quickly signed up to see what is it all about. So, here's what I learned so far.

Here are the steps to create a web app using Google App Engine with Java Lanuage Support.

1. Sign-up for an App Engine Account http://appengine.google.com/ you should click link for Java then click the sign-up button (I waited for 2 days for the confirmation). I don't have a screenshot for it, but its easy to spot.

2. Download the Google App Engine SDK http://code.google.com/appengine/downloads.html

3. Install the App Engine Eclipse Plugin
From the Eclipse Menu, go to HELP > Software Update...
Click the Available Software Tab
Click Add Site, type http://dl.google.com/eclipse/plugin/3.4
Then Install
See http://code.google.com/appengine/docs/java/tools/eclipse.html for more details.
Restart Eclipse


4. Click the New Web Application Icon as shown below


5. Type in the Project name and package. I unchecked the GWT option since I'm not going to use it.


6. Then click Finish. Everything is created for you, even the libraries that you need for your application.


7. Run the server by right-clicking on the project, select Debug As > Web Application


8. If you have tomcat running by default, you'd probably get this error message
WARNING: failed SelectChannelConnector@127.0.0.1:8080
java.net.BindException: Address already in use: bind


9. Fix this by going to the Debug Configurations, then on the Main Tab, set a new port or click "Automatically select an unused port".





10. Click Debug.

11. Now open your browser to http://localhost:8989.

12. Now you can test your application.


13. Once you're done with you application and you already got the Google confirmation message, you can now create an application.


14. Type in a unique Application Identifier and the Application Title. This will be used in deploying your application.


15. Once everything is done, you can now deploy by clicking the Deploy App Engine Project icon.


16. You will need to click the App Engine project settings to enter your Application Identifier.


17. Enter you Application ID then type in a version then click OK.


18. Enter your Google App Engine Account Email and Password and that is done.


20. Here's the app http://beerradar.appspot.com/ I deployed, to be used as the server component of the Beer Radar project.

21. AND YES, I had to put in the ads. ;)

What I really like about the app engine is the JDO support.

Here's the code for my Persistent Object
Beer.java

package com.androidph.beerserver;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Beer {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;

@Persistent
private String name;

@Persistent
private Double price;

@Persistent
private Date lastCallTime;

@Persistent
private String promotions;

@Persistent
private String address;

@Persistent
private Double longitude;

@Persistent
private Double latitude;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Double getPrice() {
return price;
}

public void setPrice(Double price) {
this.price = price;
}

public Date getLastCallTime() {
return lastCallTime;
}

public void setLastCallTime(Date lastCallTime) {
this.lastCallTime = lastCallTime;
}

private SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");

public String getLastCallTimeString() {
if(lastCallTime != null) {
return sdf.format(lastCallTime);
} else {
return "-";
}
}

public String getPromotions() {
return promotions;
}

public void setPromotions(String promotions) {
this.promotions = promotions;
}

public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

public Double getLongitude() {
return longitude;
}

public void setLongitude(Double longitude) {
this.longitude = longitude;
}

public Double getLatitude() {
return latitude;
}

public void setLatitude(Double latitude) {
this.latitude = latitude;
}

public String toString() {
return id + " | " + name + " | " + price + " | " + lastCallTime + " | " + promotions + " | " + address + " | " + longitude + " | " + latitude;
}

public byte[] convertToStream() {
return null;
}

public void fromStream(byte[] data) {

}
}

Here's the code to get a Persistence Manager. I got this from the GuestBook demo included in the AppEngine SDK

PMF.java

package com.androidph.beerserver;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");

private PMF() {}

public static PersistenceManagerFactory get() {
return pmfInstance;
}
}


Here are code snippets on how to use the Persistence Manager.

Persisting an Object

PersistenceManager persistenceManager = PMF.get()
.getPersistenceManager();
Beer beer = beerValidator.getBeer(name, address, price,
lastCallTime, promotions, longitude, latitude);
persistenceManager.makePersistent(beer);

Retrieving Data

String query = "select from " + Beer.class.getName()
+ " order by name asc";
List beerLocations = (List) persistenceManager
.newQuery(query).execute();
for (Beer beer : beerLocations) {
beerServletData.addLog("beer.toString() : " + beer.toString());
}

Sunday, February 8, 2009

App #1.0 - Beer Radar

I want to create an application, where it downloads bar location and some necessary details such as price of a beer, location and last call time. Also, I want it to be plotted with the user's current GPS location. Also, I want to have a server where user's can upload their recommended bars so that other user's may see it on their screens.


Download Source Code Below.

Objectives:

1. Download Bar Details from Server
* 2. Plot Bar Details with respect to the user's current gps location
3. Upload Bar Details to Server
4. Be able adjust the coverage of the radar (1 km - 10 km)

But for this tutorial, I just want to implement #2, since I still can't find a nice free server to host my server app.

The MODEL

STEP 1. Create BeerLocation.java
- this is just a POJO for the Bar Details. It also stores the longitude and latitude values of the Bar.


package com.androidph.beer.model;

import java.text.DecimalFormat;

public class BeerLocation {

private int id;
private String name;
private String price;
private String description;
private String lastCallTime;
private double latitude;
private double longitude;
private DecimalFormat df = new DecimalFormat("####.##");

public BeerLocation(String name, String price, String description,
String lastCallTime, double latitude, double longitude) {
this.name = name;
this.price = price;
this.description = description;
this.lastCallTime = lastCallTime;
this.latitude = latitude;
this.longitude = longitude;
}

public String toString() {
return name + ", " + price + " ( " + df.format(longitude) + ", " + df.format(latitude) + " ) ";
}

public BeerLocation() {
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPrice() {
return price;
}

public void setPrice(String price) {
this.price = price;
}

public String getDescription() {
return description;
}

public void setDescription(String description) {
this.description = description;
}

public String getLastCallTime() {
return lastCallTime;
}

public void setLastCallTime(String lastCallTime) {
this.lastCallTime = lastCallTime;
}

public double getLatitude() {
return latitude;
}

public void setLatitude(double latitude) {
this.latitude = latitude;
}

public double getLongitude() {
return longitude;
}

public void setLongitude(double longitude) {
this.longitude = longitude;
}

}


STEP 2. Create interface BarLocationService.java
- contains the method to retrieve a List of Bar Details. Currently, the implementation is just hard-coded and will be replaced on the next iterationsof this application.


package com.androidph.beer.model;

import java.util.List;

public interface BeerLocationService {
public List retrieveAllBeerLocations();
}


STEP 3. Create BeerLocationServiceMemory.java
- Just create a java.util.List and add BeerLocation to the list.


package com.androidph.beer.model.impl;

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

import com.androidph.beer.model.BeerLocation;
import com.androidph.beer.model.BeerLocationService;

public class BeerLocationServiceMemory implements BeerLocationService {

private List beerLocations = new ArrayList();
@Override
public List retrieveAllBeerLocations() {
beerLocations.add( new BeerLocation("Chili Pepper's", "Php45.0", "", "9:00AM", 50, 50));
beerLocations.add( new BeerLocation("OTB", "Php45.0", "", "3:00AM", 0, 0));
beerLocations.add( new BeerLocation("Gerry's Grill", "Php38.0", "", "00:00AM", 235, 100));
beerLocations.add( new BeerLocation("Giligan's", "Php40.0", "", "03:00AM", 180, 150));
return beerLocations;
}
}



The VIEW

STEP 1. Create BeerRadarView
- Extend the android.view.View class. The View class contains the onDraw(Canvas) method which is where the painting is done. The onDraw(Canvas) method is similar to the paint(Graphics g) of AWT and/or Swing components. Even the draw methods are almost the same. (example. g.drawArc(...), canvas.drawArc(...), g.drawLine(...), canvas.drawLine(...) and so forth. The only difference is that the canvas stores most of its paint/draw properties in a Paint object.
- The Paint class stores common drawing properties such as color, font size, stoke etc.
- The coordinates are also the same for both canvas and graphics. 0,0 is the upperleft-most and MAX_WIDTH, MAX_HEIGHT is on the lower right-most.


@Override
protected void onDraw(Canvas canvas) {
if(!hasInitialized) {
initializeConstants();
}
drawRadarGrid(canvas);
drawRadar(canvas);
drawBeer(canvas, beerLocations);
String currentLocation = "( " + df.format(currentLongitude) + ", " + df.format(currentLatitude) + " )";
canvas.drawText(currentLocation, midpointX-30, midpointY-5, paintScreenText);
}


STEP 2. Drawing the Radar Grid
- Just plot two lines that looks like a corsair.

STEP 3. Drawing the Radar Circles
- This uses the canvas.drawCircle(...) which actually is not on the graphics class. The canvas.drawArc(...), is different from the drawCircle, since it uses a RectF to get the size of the arc/ellipse to be drawn.


private void drawRadarGrid(Canvas canvas) {
canvas.drawLine(0, midpointY, screenWidth, midpointY, paintRadarGrid);
canvas.drawLine(midpointX, 0, midpointX, screenHeight, paintRadarGrid);


STEP 4. Animated the Radar
- Use the circle path formula
x = A cos(radian);
y = A Sine(radian);
Where A is the amplitude.
- Since I am using 0-360 degrees for the angles, it is necessary to convert degrees to radians.
- Radian = Angle * (PI / 180)


private void drawRadar(Canvas canvas) {
if (startAngle > 360) {
startAngle = 0;
}
float x = (float) (minimumScreenSize / 2 * Math.cos(startAngle
* (Math.PI / 180)));
float y = (float) (minimumScreenSize / 2 * Math.sin(startAngle
* (Math.PI / 180)));
canvas.drawLine(getWidth() / 2, getHeight() / 2, x + getWidth() / 2, y
+ getHeight() / 2, paintRadar);
startAngle += 3;
}


STEP 5. Plotting the BeerLocation data.
- Plot the longitude and latitude of the BeerLocation with respect to the user's current GPS location
- x = (beerLoc.getLongitude() + midpointX - currentLongitude - BEER_ICON_SIZE);
Since the screens midpoint is not 0, the longitude must the adjusted. The + (positive) value of X should be in the WEST part of the screen. So the midpoint of the X-axis must be added to the location's longitude. Also, to adjust it to the user's current location, the currentLongitude is subtracted. The BEER_ICON_SIZE is subtracted just so it becomes centered.
- y = (midpointY - beerLoc.getLatitude() + currentLatitude - BEER_ICON_SIZE);
There's a slight difference between the computation of Y, since the + (positive) value of y should be on the NORTH part of the screen, thus, the point is adjusted. So, midpoint of Y is subtracted with the beer location's latitude. Also, the currentLatitude is added to the equation.
- The maximum coverage is not yet adjustable yet. In the future, I'll modify so that the user can select a minimum radius of 1KM and a maximum radius of 10KM. I currently do not know how each degrees of the longitude and latitude translates into KM. I'll research on it first.


private void drawBeer(Canvas canvas, List beerLocations) {
if(beerIcon == null) {
beerIcon = BitmapFactory.decodeResource(this.getResources(), R.drawable.beer);
}
for(BeerLocation beerLoc : beerLocations) {
float x = (float)(beerLoc.getLongitude() + midpointX - currentLongitude - BEER_ICON_SIZE);
float y = (float)(midpointY - beerLoc.getLatitude() + currentLatitude - BEER_ICON_SIZE);
canvas.drawBitmap(beerIcon, x, y, paintScreenText);
canvas.drawText(beerLoc.toString(), x, y, paintScreenText);
}
}


The ACTIVITY
STEP 1. Set the BeerRadarView as the Activity's contentView


@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
beerRadarView = new BeerRadarView(this);
setContentView(beerRadarView);
setCurrentGpsLocation(null);
thread = new Thread(new MyThreadRunner());
thread.start();
}


STEP 2. Create a android.os.Handler that invalidate's the view when called. Invalidate is similar to calling awt/swing component's repaint() method.

"When a process is created for your application, its main thread is dedicated to running a message queue that takes care of managing
the top-level application objects (activities, intent receivers, etc) and any windows they create. You can create your own threads, and communicate back with the main application thread through a Handler. This is done by calling the same post or sendMessage methods as before, but from your new thread. The given Runnable or Message will than be scheduled in the Handler's message queue and processed when appropriate."


Handler updateHandler = new Handler() {
/** Gets called on every message that is received */
// @Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_LOCATION: {
beerRadarView.setCurrentLatitude(latitude);
beerRadarView.setCurrentLongitude(longitude);
break;
}
}
beerRadarView.invalidate();
super.handleMessage(msg);
}
};


STEP 3. implement android.location.LocationListener on the Activity


public class BeerRadar extends Activity implements LocationListener {


- Implement the necessary methods.

@Override
public void onLocationChanged(Location location) {
setCurrentGpsLocation(location);
}

@Override
public void onProviderDisabled(String provider) {
setCurrentGpsLocation(null);

}

@Override
public void onProviderEnabled(String provider) {
setCurrentGpsLocation(null);
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
setCurrentGpsLocation(null);
}

- Use onLocationChanged(Location location) to update the user's current location


private void setCurrentGpsLocation(Location location) {
if (location == null) {
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, this);
location = locationManager
.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
longitude = location.getLongitude();
latitude = location.getLatitude();
Message msg = new Message();
msg.what = UPDATE_LOCATION;
BeerRadar.this.updateHandler.sendMessage(msg);
}

Testing

Since it just an emulator, you can use the DDMS perspective to update the longitude and latitude.Longitude =50, Latitude = 50

Longitude =120, Latitude = 100

Source Code

Android Camera Capture Tutorial

Reference: http://www.anddev.org/the_pizza_timer_-_threading-drawing_on_canvas-t126.html

Email

java.padawan@androidph.com