Thursday, December 24, 2009

A couple of short links - December 24, 2009

1. 16 Cool Cellphone Concepts Which Won't Be Implemented - cellphone concepts that look amazing, ranging from customizable back sides like the Nokia Unik, to cellphones that act as a bangle like the LG Helix.

2. The (Last and Next) Decade in Gadgets - breakthroughs in technology of the past decade and prediction of  future trends. The areas covered are Computers/OS, Cellphones, Web, cameras, Home Entertainment, Music, Movies, Video Games (here it mentions Microsoft's Natal project), and Print.

3. Top Ten Stories on Digg in 2009 - the title says it all.

4. Java Design Patterns book - written by IBM's James Cooper, an excellent free resource on Design Patterns implemented in Java. Bruce Eckel has a similar book, entitled Thinking in Patterns.

Tuesday, December 22, 2009

Cross-Platform Mobile Development


I don't have to tell you that there are multiple mobile platforms out there, such as Symbian, Blackberry, iPhone, Android, Windows Mobile, BREW, webOS, Limo, Bada, and others. As a mobile applications developer, one should try to cover as many platforms as possible. Developing on these platforms requires knowledge of Java, C++, .NET, Objective-C, clearly too many languages. As if this would not be enough, there is the fragmentation issue within each platform. So, what can one do to alleviate these problems? Fortunately, there are some tools that mitigate this issue by allowing to design/code once, and port to different platforms afterwards. I am just going to mention some of them, leaving up to you to go and further investigate if you deem necessary. A quick note: when HTML is mentioned, JavaScript and CSS are included by default.

Airplay allows you to code in Visual C++ and build for x86 and ARM compilers, thus building your application as an "OS-agnostic binary file that contains native CPU instructions". The platforms supported are iPhone, Android, Symbian, Windows Mobile, and BREW.

Alchemo is useful if you have a Java ME application that has to be ported to other platforms such as Android, BREW, iPhone, and Windows Mobile.

PhoneGap is an open source tool for web developers that want to take advantage of features such as location, accelerometer, etc, and still write applications using HTML. The platforms supported are iPhone, Android, Blackberry, Palm, and Windows Mobile.

With Pyxis Mobile, you can take advantage of their Application Studio and configure your application to run on Blackberry, Windows Mobile, and iPhone platforms. The platform offers support for connectivity to enterprise data sources.

Rhodes is an open source framework where you can build native applications (despite the use of HTML or Ruby for development) that run on the iPhone, Windows Mobile, Blackberry, Symbian, and Android.

Smartface Designer allows you to design you application using the tool and deploy it to Blackberry, Symbian, and J2ME. You can drag-and-drop existing components when using the designer tool, thus avoiding writing code.

Sybase Unwired Platform follows the same line of designing your applications once, and deploy it on several mobile platforms such as Windows Mobile, Windows 32 (Laptops/Tablets), iPhone, and Blackberry. The platforms is geared towards enterprise, offering integration with Sybase, Oracle, IBM, and Microsoft based databases, SAP, Remedy, etc enterprise applications, and Web Services.

Titanium Mobile uses Web Technologies (such as HTML, JavaScript, etc) to build native applications for the iPhone and Android platforms.

Voyager allows you to code in your favorite development language (Java, .NET languages, C++), and deploy it on various platforms such as Android, JavaFX, Windows Mobile, Java ME, Linux, Blackberry, Symbian, BREW, iPhone, with future support for Real-Time OSs. This is the platform that covers most of the existing mobile platforms, and allows for enterprise integration.

Other platforms that may be worth looking into are Dexterra ConcertSpring Wireless, and QuickConnect.

Below is a table summarizing the info supplied above (last updated on: July 17, 2010):



Airplay
Alchemo
PhoneGap
Pyxis Mobile App Studio
Rhodes
Smartface Designer
Sybase Unwired Platform
Titanium Mobile
Voyager
Platforms Supported
Android
X
X
X

X


X
X
Java FX








X
Windows Mobile
X
X
X
X
X

X

X
Linux








X
Blackberry


X
X
X
X
X

X
Symbian
X

X

X
X


X
BREW
X
X






X
iPhone
X
X
X
X
X

X
X
X
Java ME





X


X
Development Languages
C++
X







X
Java ME

X







Java








X
.NET








X
HTML


X

X


X

Own Visual Language



X

X
X


Other Properties
Open Source


X

X




Enterprise Integration



X


X

X
Free
X (only for iPhone)

X

X (if using GPL)






There will be a book that will come out on April of 2010 August 31, 2010 entitled "Pro Smartphone Cross-Platform Development: iPhone, Blackberry, Windows Mobile and Android Development and Distribution", by Sarah Allen and Vidal Graupera, and which should offer valuable resource for anyone involved with cross-platform development.

Please drop me a message if you know of any other cross-platform tool(s) worth mentioning.

Tuesday, December 15, 2009

Sliding Puzzle Full - First Review

I found the first review of my Android game, Sliding Puzzle Full, review made for an older version (1.0.0). I am glad James enjoyed the game. From the review:

"Sliding Puzzle Full v1.1.0 is a fun application that makes your photos take on a whole new life of their own. I liked the grid size option and the ability to choose a picture or create a new puzzle using my phones camera.


The controls are overall simple to use, the touchscreen sliding mechanism is very functional and the program offers simple fun. If you're a puzzler fan you'll love this game."

Some notes:
 - the game is available on Android Market, but it was built with version 1.6, hence some phones might not *see* it yet.
 - I will not update the slideme version of the game (which is 1.0.0). The newest version is 1.1.1 and can be found in the Android Market.

Sunday, November 22, 2009

Displaying Images from SD Card In Android - Part 2

In one of my previous posts, I wrote about how to fetch and display images from the SD card. The problem with the previous post is that one would have to wait until the first couple of images are available and shown on the screen. This implies that when the user wants to see the images, he will wait a couple of seconds until the first screen of images is available. The code that I'm going to post here works more like the Gallery application, meaning that one image at a time will be displayed on the screen. To achieve this effect, I used an AsyncTask, which fetches one image at a time in the background, and adds that image to the grid view during the progress update.

package blog.android.sdcard2;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.AdapterView.OnItemClickListener;

/**
 * Loads images from SD card. 
 * 
 * @author Mihai Fonoage
 *
 */
public class LoadImagesFromSDCardActivity extends Activity implements
OnItemClickListener {
    
    /**
     * Grid view holding the images.
     */
    private GridView sdcardImages;
    /**
     * Image adapter for the grid view.
     */
    private ImageAdapter imageAdapter;
    /**
     * Display used for getting the width of the screen. 
     */
    private Display display;

    /**
     * Creates the content view, sets up the grid, the adapter, and the click listener.
     * 
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        // Request progress bar
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.sdcard);

        display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

        setupViews();
        setProgressBarIndeterminateVisibility(true); 
        loadImages();
    }

    /**
     * Free up bitmap related resources.
     */
    protected void onDestroy() {
        super.onDestroy();
        final GridView grid = sdcardImages;
        final int count = grid.getChildCount();
        ImageView v = null;
        for (int i = 0; i < count; i++) {
            v = (ImageView) grid.getChildAt(i);
            ((BitmapDrawable) v.getDrawable()).setCallback(null);
        }
    }
    /**
     * Setup the grid view.
     */
    private void setupViews() {
        sdcardImages = (GridView) findViewById(R.id.sdcard);
        sdcardImages.setNumColumns(display.getWidth()/95);
        sdcardImages.setClipToPadding(false);
        sdcardImages.setOnItemClickListener(LoadImagesFromSDCardActivity.this);
        imageAdapter = new ImageAdapter(getApplicationContext()); 
        sdcardImages.setAdapter(imageAdapter);
    }
    /**
     * Load images.
     */
    private void loadImages() {
        final Object data = getLastNonConfigurationInstance();
        if (data == null) {
            new LoadImagesFromSDCard().execute();
        } else {
            final LoadedImage[] photos = (LoadedImage[]) data;
            if (photos.length == 0) {
                new LoadImagesFromSDCard().execute();
            }
            for (LoadedImage photo : photos) {
                addImage(photo);
            }
        }
    }
    /**
     * Add image(s) to the grid view adapter.
     * 
     * @param value Array of LoadedImages references
     */
    private void addImage(LoadedImage... value) {
        for (LoadedImage image : value) {
            imageAdapter.addPhoto(image);
            imageAdapter.notifyDataSetChanged();
        }
    }
    
    /**
     * Save bitmap images into a list and return that list. 
     * 
     * @see android.app.Activity#onRetainNonConfigurationInstance()
     */
    @Override
    public Object onRetainNonConfigurationInstance() {
        final GridView grid = sdcardImages;
        final int count = grid.getChildCount();
        final LoadedImage[] list = new LoadedImage[count];

        for (int i = 0; i < count; i++) {
            final ImageView v = (ImageView) grid.getChildAt(i);
            list[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
        }

        return list;
    }
    /**
     * Async task for loading the images from the SD card. 
     * 
     * @author Mihai Fonoage
     *
     */
    class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {
        
        /**
         * Load images from SD Card in the background, and display each image on the screen. 
         *  
         * @see android.os.AsyncTask#doInBackground(Params[])
         */
        @Override
        protected Object doInBackground(Object... params) {
            //setProgressBarIndeterminateVisibility(true); 
            Bitmap bitmap = null;
            Bitmap newBitmap = null;
            Uri uri = null;            

            // 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 cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                    projection, // Which columns to return
                    null,       // Return all rows
                    null,       
                    null); 
            int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID);
            int size = cursor.getCount();
            // If size is 0, there are no images on the SD Card.
            if (size == 0) {
                //No Images available, post some message to the user
            }
            int imageID = 0;
            for (int i = 0; i < size; i++) {
                cursor.moveToPosition(i);
                imageID = cursor.getInt(columnIndex);
                uri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageID);
                try {
                    bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
                    if (bitmap != null) {
                        newBitmap = Bitmap.createScaledBitmap(bitmap, 70, 70, true);
                        bitmap.recycle();
                        if (newBitmap != null) {
                            publishProgress(new LoadedImage(newBitmap));
                        }
                    }
                } catch (IOException e) {
                    //Error fetching image, try to recover
                }
            }
            cursor.close();
            return null;
        }
        /**
         * Add a new LoadedImage in the images grid.
         *
         * @param value The image.
         */
        @Override
        public void onProgressUpdate(LoadedImage... value) {
            addImage(value);
        }
        /**
         * Set the visibility of the progress bar to false.
         * 
         * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
         */
        @Override
        protected void onPostExecute(Object result) {
            setProgressBarIndeterminateVisibility(false);
        }
    }

    /**
     * Adapter for our image files. 
     * 
     * @author Mihai Fonoage
     *
     */
    class ImageAdapter extends BaseAdapter {

        private Context mContext; 
        private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();

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

        public void addPhoto(LoadedImage photo) { 
            photos.add(photo); 
        } 

        public int getCount() { 
            return photos.size(); 
        } 

        public Object getItem(int position) { 
            return photos.get(position); 
        } 

        public long getItemId(int position) { 
            return position; 
        } 

        public View getView(int position, View convertView, ViewGroup parent) { 
            final ImageView imageView; 
            if (convertView == null) { 
                imageView = new ImageView(mContext); 
            } else { 
                imageView = (ImageView) convertView; 
            } 
            imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
            imageView.setPadding(8, 8, 8, 8);
            imageView.setImageBitmap(photos.get(position).getBitmap());
            return imageView; 
        } 
    }

    /**
     * A LoadedImage contains the Bitmap loaded for the image.
     */
    private static class LoadedImage {
        Bitmap mBitmap;

        LoadedImage(Bitmap bitmap) {
            mBitmap = bitmap;
        }

        public Bitmap getBitmap() {
            return mBitmap;
        }
    }
    /**
     * When an image is clicked, load that image as a puzzle. 
     */
    public void onItemClick(AdapterView<?> parent, View v, int position, long id) {        
        int columnIndex = 0;
        String[] projection = {MediaStore.Images.Media.DATA};
        Cursor cursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                projection,
                null, 
                null, 
                null);
        if (cursor != null) {
            columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
            cursor.moveToPosition(position);
            String imagePath = cursor.getString(columnIndex); 

            FileInputStream is = null;
            BufferedInputStream bis = null;
            try {
                is = new FileInputStream(new File(imagePath));
                bis = new BufferedInputStream(is);
                Bitmap bitmap = BitmapFactory.decodeStream(bis);
                Bitmap useThisBitmap = Bitmap.createScaledBitmap(bitmap, parent.getWidth(), parent.getHeight(), true);
                bitmap.recycle();
                //Display bitmap (useThisBitmap)
            } 
            catch (Exception e) {
                //Try to recover
            }
            finally {
                try {
                    if (bis != null) {
                        bis.close();
                    }
                    if (is != null) {
                        is.close();
                    }
                    cursor.close();
                    projection = null;
                } catch (Exception e) {
                }
            }
        }
    }

}

The sdcard.xml file:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <GridView  
        android:id="@+id/sdcard"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:verticalSpacing="10dp"
        android:horizontalSpacing="10dp" 
        android:stretchMode="columnWidth"
        android:gravity="center" />
        
</FrameLayout>    

That's it. Let me know if you have any questions.

Enjoy!

Wednesday, November 18, 2009

Gartner's Top 10 Mobile Apps for 2012

Gartner has released its top 10 applications for 2012.

"Consumer mobile applications and services are no longer the prerogative of mobile carriers,” said Sandy Shen, research director at Gartner. “The increasing consumer interest in smartphones, the participation of Internet players in the mobile space, and the emergence of application stores and cross-industry services are reducing the dominance of mobile carriers. Each player will influence how the application is delivered and experienced by consumers, who ultimately vote with their attention and spending power".

LBS applications are at number 2. The LBS user base is predicted to grow to 526 million by 2012 (from 96 million in 2009). NFC is at number 7. I read here that some new version of iPhone is supposed to include NFC support (actually support for proximity), and I do believe it's about time for the mobile industry to get behind this technology.

You can read the entire report at Gartner.

Friday, November 13, 2009

Engineering Achievements and Challenges

The National Academy of Engineering (NAE) put together two lists: one that ranks the 20 greatest engineering achievements of the 20th century, and one with engineering's challenges to be accomplished:


SOURCE: IEEE Spectrum