Thursday, October 15, 2009

Performance Comparison of Major Web Browsers

Six Revisions has published a performance comparison of the major five web browsers (Firefox, Chrome, IE, Opera, and Safari) in terms of JavaScript speed, CPU usage under stress, DOM selection, CSS rendering speed, page load time, and browser cache performance. Bottom line, Google Chrome 3.0 wins in four out of the six performance indicators, coming third and second in the DOM Selection Speed (won by Opera) and Page Load Time (won by Firefox) tests.

Monday, September 28, 2009

Displaying images from SD card in Android

Below you will find a Android example of how to access and display images that are stored on your SD card.

I wrote part 2 for this article, where images are loaded in the background using an asynchronous task. It is an improvement over this article, but I strongly suggest trying this one first to fully appreciate the differences between the two approaches.

The main idea is to make use of the MediaStore class, which is a Media provider that contains data for all available media on both internal and external storage devices (such as an SD card). An adapter is used as a bridge between the data and the view.

The activity is shown below:

package blog.android.sdcard;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemClickListener;

/**
 * Displays images from an SD card.
 */
public class SDCardImagesActivity extends Activity {

    /**
     * Cursor used to access the results from querying for images on the SD card.
     */
    private Cursor cursor;
    /*
     * Column index for the Thumbnails Image IDs.
     */
    private int columnIndex;

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

        // Set up an array of the Thumbnail Image ID column we want
        String[] projection = {MediaStore.Images.Thumbnails._ID};
        // Create the cursor pointing to the SDCard
        cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                projection, // Which columns to return
                null,       // Return all rows
                null,
                MediaStore.Images.Thumbnails.IMAGE_ID);
        // Get the column index of the Thumbnails Image ID
        columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID);

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

        // Set up a click listener
        sdcardImages.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView parent, View v, int position, long id) {
                // Get the data location of the image
                String[] projection = {MediaStore.Images.Media.DATA};
                cursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        projection, // Which columns to return
                        null,       // Return all rows
                        null,
                        null);
                columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                cursor.moveToPosition(position);
                // Get image filename
                String imagePath = cursor.getString(columnIndex);
                // Use this path to do further processing, i.e. full screen display
            }
        });
    }

    /**
     * Adapter for our image files.
     */
    private class ImageAdapter extends BaseAdapter {

        private Context context;

        public ImageAdapter(Context localContext) {
            context = localContext;
        }

        public int getCount() {
            return cursor.getCount();
        }
        public Object getItem(int position) {
            return position;
        }
        public long getItemId(int position) {
            return position;
        }
        public View getView(int position, View convertView, ViewGroup parent) {
            ImageView picturesView;
            if (convertView == null) {
                picturesView = new ImageView(context);
                // Move cursor to current position
                cursor.moveToPosition(position);
                // Get the current value for the requested column
                int imageID = cursor.getInt(columnIndex);
                // Set the content of the image based on the provided URI
                picturesView.setImageURI(Uri.withAppendedPath(
                        MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageID));
                picturesView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                picturesView.setPadding(8, 8, 8, 8);
                picturesView.setLayoutParams(new GridView.LayoutParams(100, 100));
            }
            else {
                picturesView = (ImageView)convertView;
            }
            return picturesView;
        }
    }
}
The layout of the main activity is shown below:

<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android" 
    android:id="@+id/sdcard"
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:padding="10dp"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="10dp"
    android:numColumns="auto_fit"
    android:columnWidth="90dp"
    android:stretchMode="columnWidth"
    android:gravity="center"
/>

In order for this to work, you need to emulate an SD card.

Enjoy!

UPDATE (October 19, 2009): In order to be bale to view thumbnails images from the SD Card, Android needs to create them first, hence you should start the Gallery application that comes preinstalled, and open the sdcard folder which will automatically create thumbnails for the images stored on your sdcard. This is a current shortcoming of the SDK that will be fixed in future releases (http://groups.google.com/group/android-developers/browse_thread/thread/3f01b284e2537312/fa9487d19db4907e).

UPDATE (October 07, 2009): For some reason, if you use
MediaStore.Images.Thumbnails.IMAGE_ID
like in the previous version of the above code, the images are not always displayed on the screen. Changing to
MediaStore.Images.Thumbnails._ID
seems to solve the problem. I will look more into why and get back to you.
Furthermore, some images have the wrong path attached to them.  I changed the creation of the cursor object from
cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
projection, // Which columns to return
null,       // Return all rows
null,
null);
to
cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
projection, // Which columns to return
null,       // Return all rows
null,
MediaStore.Images.Thumbnails.IMAGE_ID);

Wednesday, September 16, 2009

Dilbert comic strip - Sep 11, 2009

Dilbert.com


You got to have a sense of humor to enjoy this, especially in this hard economic times.

Thursday, August 27, 2009

Global smartphone market shares

From the data findings by analyst company Canalys, while Nokia and Microsoft shares are decreasing, RIM and Apple are growing:


The above stats are based on the number of units shipped during Q2 of 2009. The iPhone 3G was not yet out when the data was gathered. Android-based smartphones where not available in the second quarter of 2008. By this time next year, Android will be much closer to Microsoft's share. Nokia just came out with its Maemo-based smartphone, the N900. Moving more towards Maemo rather than hanging on to Symbian might actually work out pretty well for Nokia in the end.

Satisfiability Problems Online Game

There's an article in the IEEE Spectrum regarding Crowdsourcing the Complexities of Electronic Design Automation, which talks about a game called FunSAT that could benefit chip makers in improving their designs. Humans can help at this stage because these kind of problems are difficult for computers to solve. The game simulates satisfiability problems (SATs), and presents the user with a set of buttons and bubbles, where the buttons can have a value of true, false, or unassigned, and the bubbles, which represent clauses, can be satisfied, unsatisfied, or undetermined. The goal is to turn all bubbles green. For a bubble to turn green, at least one of the literals in the corresponding clause must be true. If all of the literals are false, the bubble will turn red; otherwise, it will be gray.

Why are satisfiability problems important? Because they aid in the design verification stage of chip design. "For example, they are used to find out what input combinations result in exposing a potential bug in the design. If no such combination exists, then the designers don’t have to worry about that particular bug."

"Modern satisfiability solvers tend to prioritize and limit their searches to a few paths that look promising. By using this method, however, they don’t always find the best configuration. What humans lack in brute-force speed, they tend to make up for with pattern recognition and intuition—the ability to know a good move without being able to explain why it’s good. And, most important, they like to play games."

Friday, August 21, 2009

Invoke a Web Service from JSP

This tutorial is a small example on how to call a SOAP-based Web Service from a browser-based client (using HTML and JSP). As an application server, we will use GlassFish. When you download GlassFish, make sure you use the preview version for this tutorial.

The example takes a number as its input and computes the corresponding Fibonacci number. We will start with the Web Service:
package blog.webservice;

import javax.jws.WebMethod;
import javax.jws.WebService;

@WebService
public class FibonacciNumber {

@WebMethod
public int fibNum(int n) {
int first = 1, fib = 1;
for (int i = 3; i <= n; i++) {
int temp = first + fib;         
first = fib;         
fib = temp;         
}     
return fib;     
}
}

After compilation of the above code, you take the resulting class file and copy it to the blog/WEB-INF/classes/blog/webservice directory. Next step is to package the WEB-INF directory into a WAR file for deployment by executing the following command:
% jar cvf fib.war WEB-INF

The WAR is then copied to GLASSFISH_HOME/domains/domain1/autodeploy, where GLASSFISH_HOME points to the GlassFish install directory. If the deployment succeeds, a second file named fib.war_deployed appears in a few seconds in the autodeploy directory.
The good part about GlassFish is that you do not need to manually (using wsgen) generate the JAX-B artifacts that the service requires, because the current Metro web services stack release ships with GlassFish, automatically generating these artifacts. The WSDL file is also automatically generated and will be used on the client side as described in one of the sections below.

To generate the client-side artifacts (client.FibonacciNumber, client.FibonacciNumberService, etc), we will use wsimport on the wsdl file generated aytomatically by GlassFish:

wsimport -keep -p client http://localhost:8080/fib/FibonacciNumberService?wsdl

Next step is writing the client-side code, namely the HTML page, two JSP pages, and one xml configuration file.

The HTML page is simple and straightforward:
<html>
<body>
<form method = 'POST' action = 'fib.jsp'>
Input number for Fibonacci: <input type = 'text' name = 'inputNumber'><br/><hr/>
<input type = 'submit' value = ' Get Fibonacci '/>
</form>
</body>
</html>

The actual JSP that will invoke the FibonacciNumber web service is below:
<%@ page errorPage = 'errors.jsp' %>
<%@ page import = "client.FibonacciNumber" %>
<%@ page import = "client.FibonacciNumberService" %>
<html>
<body>
<%! private int fibNo, temp; %>
<%
String inputString = request.getParameter("inputNumber");
if (inputString != null) {
temp = Integer.parseInt(inputString.trim());
}

FibonacciNumberService service =  new FibonacciNumberService();
FibonacciNumber port = service.getFibonacciNumberPort();
fibNo = port.fibNum(temp);

%>
<p>fib(<%= temp %>) = <%= fibNo %></p>
<a href = 'index.html'>Try another number</a>
</body>
</html>

Of course, clients should not see such messages displayed in the browser. Consider this error page more for the developer.

The web.xml deployment document is:
<?xml version = '1.0' encoding = 'UTF-8'?>
<web-app xmlns = 'http://java.sun.com/xml/ns/javaee'
xmlns:xsi = 'http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation = 'http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd'
version = '2.5'>
<error-page>
<exception-type>java.lang.NumberFormatException</exception-type>
<location>/errors.jsp</location>
</error-page>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>

Any input to the HTML that is not a number will be caught and an error message will be displayed through the errors.jsp page.

Once we have all the files, we package them into a WAR:
jar cvf fibClient.war *.html *.jsp WEB-INF

and deploy it the same way as we did with fib.war.

For testing, open the http://localhost:8080/fibClient/ URL in your favorite browser.