Saturday 31 October 2009

Windows 7 First Impressions



Well I have played with Windows 7 for a few days now. Its very interesting. Kind of a cross between Mac OS X and Window XP. Since I have only limited experience with Vista I can't really make a comparison there.



The start menu defaults to a kind of history of apps you have executed. Any app icon can be pinned to the menu permanently. The All Programs option is now sort of a click thru instead of pop ups. When you select an option from the menu, it pops up in place instead of growing to the right. A big improvement in usability if you ask me.



The taskbar just shows the icon for an application, much like the doc for OS X.  There will allow you to put a lot more apps on there. Also a big improvement.



So far so good. I think I can definitely upgrade my XP systems with this OS.

Friday 30 October 2009

Loading Android Resource in GridView

Refer to Android document R.drawable, there are a number of System Resources from 17301504 to 17301655.

This exercise modify the last GridView exercise to load the System Resources, instead of loading from /res/drawable.



mThumbIds[] is used to hold the resources id, to be passed to ImageAdapter. It is initialized in initResourceID()in ImageAdapter's constructor.

Each object is passed to to imageView using imageView.setImageResource(mThumbIds[position]) inside getView().

The main.xml is same as that in GridView exercise.

AndroidGridView2.java
package com.exercise.AndroidGridView2;

import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
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.Toast;

public class AndroidGridView2 extends Activity {

final int resourceStart = 17301504;
final int resourceEnd = 17301655;
final int ResourceLength = resourceEnd - resourceStart + 1;

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

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

gridview.setOnItemClickListener(gridviewOnItemClickListener);
}

private GridView.OnItemClickListener gridviewOnItemClickListener =
new GridView.OnItemClickListener(){

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
String strResource =
arg0.getAdapter().getItem(arg2).toString() +
" " +
Resources.getSystem().getResourceName(arg2+resourceStart);
Toast.makeText(AndroidGridView2.this,
strResource,
Toast.LENGTH_LONG).show();
}
};


public class ImageAdapter extends BaseAdapter {
private Context mContext;

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

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

public Object getItem(int position) {
return mThumbIds[position];
}

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

// create a new ImageView for each item referenced by the Adapter
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(50, 50));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}

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

private int[] mThumbIds = new int[ResourceLength];
private void initResourceID(){
for ( int i = 0; i < ResourceLength; i++)
{
mThumbIds[i] = i + resourceStart;
}
}
}
}


Download the files.

Thursday 29 October 2009

iPhone Challenger? Meet Motorola Droid



Google LogoA real challenger for the iPhone?  Maybe. Meet the Motorola Droid.



CNET Droid Review

Engadget Droid Review



Available from Verizon in early November for $199, this beauty sports a slide out keyboard and the Google Android 2.0 operating system. See the linked reviews for details on look and feel.



As an iPhone user this is one the first phones that tempts me to switch. Apple has a big lead at the moment in all things smart phone. But will it be able to keep it with a more open competitor? Things could get interesting.

New Ubuntu 9.10

Ubuntu 9.10 is HERE!

Ubuntu just released the new Ubuntu 9.10.

Refer the article to Install Android SDK release 3 on Eclipse 3.5 Galileo, in Ubuntu 9.10

Wednesday 28 October 2009

Android 2.0 Official Video


Highlights from the latest Android platform release

Hierarchy Viewer

The Hierarchy Viewer application allows you to debug and optimize your user interface. It provides a visual representation of the layout's View hierarchy (the Layout View) and a magnified inspector of the display (the Pixel Perfect View).

To get the Hierarchy Viewer started:

1. Connect your device or launch an emulator.
2. From a terminal, launch hierarchyviewer from your SDK /tools directory.
3. In the window that opens, you'll see a list of Devices. When a device is selected, a list of currently active Windows is displayed on the right. The "focused window" is the window currently in the foreground, and also the default window loaded if you do not select another.
4. Select the window that you'd like to inspect and click Load View Hierarchy. The Layout View will be loaded. You can then load the Pixel Perfect View by clicking the second icon at the bottom-left of the window.

If you've navigated to a different window on the device, press Refresh Windows to refresh the list of available windows on the right.


GridView

GridView shows items in two-dimensional scrolling grid. The items in the grid come from the ListAdapter associated with this view.

This exercise show how to implement a GridView to display drawable in /res.



- Create a Android Application, AndroidGridView2 (It's my first time to use Android SDK r3 for Android 2.0). Target Android 2.0 (not necessary).

- Copy some picture files (or download from the file on the end of this text) into the folder /res/drawable-ldpi/ (for Android 2.0) or /res/drawable/ for former version, named drawing_01~14.png

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


- Modify AndroidGridView2.java
package com.exercise.AndroidGridView2;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
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.Toast;

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

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

gridview.setOnItemClickListener(gridviewOnItemClickListener);

}

private GridView.OnItemClickListener gridviewOnItemClickListener =
new GridView.OnItemClickListener(){

@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
Toast.makeText(AndroidGridView2.this,
arg0.getAdapter().getItem(arg2).toString(),
Toast.LENGTH_LONG).show();
}


};


public class ImageAdapter extends BaseAdapter {
private Context mContext;

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

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

public Object getItem(int position) {
return mThumbIds[position];
}

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

// create a new ImageView for each item referenced by the Adapter
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(85, 85));
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.drawing_01,
R.drawable.drawing_02,
R.drawable.drawing_03,
R.drawable.drawing_04,
R.drawable.drawing_05,
R.drawable.drawing_06,
R.drawable.drawing_07,
R.drawable.drawing_08,
R.drawable.drawing_09,
R.drawable.drawing_10,
R.drawable.drawing_11,
R.drawable.drawing_12,
R.drawable.drawing_13,
R.drawable.drawing_14
};
}

}


--------------------------------------------------------------------
Little bit advice:
- Set a break point inside getView(), you can know more on the operation of getView, such as when it will be called, and when go to initialize attributes, or load the previous convertView.

- Modify onItemClick() to check the value of arg0, arg1, arg2 & arg3.

In my code, the object ID of the clicked item of the GridView will be displayed on the Toast, in decimal. It's match with the ID of the item in the file R.java, in hexdecimal.



- Try the Tool, hierarchyviewer, it can give you a hierarchy viewer of the current view.
--------------------------------------------------------------------

Download the files.
--------------------------------------------------------------------
Another exercise
- Loading Android Resource in GridView.
- Custom GridView



Tuesday 27 October 2009

Create AVD for Android 2

The first step to use new Android SDK Rev.3 is to create a new AVD for Android 2.0

Click Window of Eclipse top menu, select Android SDK and AVD Manager.

Select Virtual Device on the left and click New.


You can see a new and improved Create new AVD dialog. Configure your new AVD, and Create AVD.


upgrade to Android SDK Tools, Revision 3

If you have already installed Android 1.6 SDK and Eclipse, it's very easy to upgrade to Android SDK Tools, Revision 3. The Android SDK and AVD Manager tool is included in Android 1.6 and later SDK packages.

If NOT yet, follow the procedure in Install Android SDK on Eclipse 3.5 Galileo.

Click Window on top menu of Eclipse, click Android SDK and AVD Manager.


Select Avaolable Package, wait a moments, the available Sources, Packages and Archieves will be shown on the right. Select the components you want.


Accept All


Wait download and install.


After downloaded and installed, you will be asked to restart Eclipse.


After Eclipse restart, you will ne asked to update ADT.


Click Help on top of Eclipse, click Install New Software...


Select the site
https://dl-ssl.google.com/android/eclipse/

It should be already in your list if you have installed Android SDK before. Wait a moment the available ADT will be appear.
Select available ADT and click Next


Next and Accept the terms, and click Finish. And wait installation of the the new ADT.

Restart Eclipse again.

That ALL.

Remember to create a new AVD for Android 2.0

Android 2.0, Release 1

Android 2.0 is a major platform release deployable to Android-powered handsets starting in November 2009. The release includes new features for users and developers, as well as changes in the Android framework API.

details>>

For a list of new user features and platform highlights, see the Android 2.0 Platform Highlights document.

Sunday 25 October 2009

Create SD Card in Android Emulator and copy files into, in Eclipse, Emulator and DDMS.

In my previous articles, I described how to Create an SD Card to existing AVD, and Copying Files to a Disk Image in ubuntu host system using text commands in Terminal.

Now, I have another approach using in Eclipse, Emulator and DDMS. May be it's more easy, at least no need to remember the text commands.

Create a new AVD in Eclipse:

- Start Eclipse, click Window on the top menu, and click Android SDK and AVD Manager.

- New a AVD


- Name your new AVD, select Target, type 1000M in SD Card and select skin. Click Create.
note: SD Card must be either a file path or a size such as 128K 0r 64M. type 1000M now to create a SD Card of 1G.

- Select and Start the new AVD.


After the Emulator started, you can close the AVD Manager.

Create folder in SDCard

I can't find any File Manager in Android Emulator. Fortunately, Android Emulator provide a DevTools with Terminal, so you can create a folder using Linux command inside Android Emulator.

- Click the ARROW on the screen to display the available application.

- Click DevTools.


- Scroll down to start Terminal Emulator


- It's the same a terminal in Linux, you can ls to view the current files and folders in your $home.


- Now change into sdcard and create a folder, say pictures.
$cd sdcard
$mkdir pictures

It's the only two line of command in this approach.


Copy files from your host system into Android SDCard

- With the Emulator is running. Now go back to Eclipse, switch to DDMS Perspective

Click the arrow on the menu bar and select DDMS

or

Click Window > Open Perspective > others > to select DDMS

- Select the device in the window on the left, eg. emulator-5554.
(Without select the device, you will fail in copying files into.)

- In DDMS, select File Explorer tag in the window on the right. Expend sdcard folder. Now, you can see the pictures folder in the tree. And also, you can see the Permissions of the folder.


- Select the pictures folder, and click Push a file onto the device .

- Browse to and select the files from your host, to push onto the Android Emulator.

- Further expend the folder pictures, you can see the new file here.


Related article: How to create sub-folder in SD Card of Android Emulator, using adb

Saturday 24 October 2009

Use mp3 as Notification sound

In the perviouse article, Status Bar Notifications, Notification is displayed on Status Bar.

To use the user's default sound, add "DEFAULT_SOUND" to the defaults field:

notification.defaults |= Notification.DEFAULT_SOUND;

But...actually, I can't make my Emulator to have any sound using this method. May be there are some setting missed in my Emulator.
(If you know why, please leave me a comment to let me know, thx.)

Alternatively, I use a mp3 file in sdcard as Notification sound.

- Follow my previous articles to Create an SD Card to existing AVD, and Copying Files to a Disk Image, to copy a mp3 file to the root of the sdcard, named sound.card.

- Add the code in the Activity.

notification.sound = Uri.parse("file:///sdcard/sound.mp3");

Friday 23 October 2009

Status Bar Notifications

A status bar notification adds an icon to the system's status bar (with an optional ticker-text message) and an expanded message in the "Notifications" window.



After Notification generated, a selected notification icon and Ticket will be displayed on the status bar.

User can reveal the Notifications window by pulling down the status bar (or selecting Notifications from the Home options menu), to view the Title and Content.

Notification can be generated inside Service or Activity. This exercise show the basic steps to generate a Notification in Activity.

The most important class is NotificationManager and Notification.

In the exercise:
android.R.drawable.btn_star_big_on is a drawable icon in Android system resource, you can assign any drawable icon.
when is when the notification should be generated, System.currentTimeMillis() = NOW.
NOTIFICATION_ID is a number which is unique in your application.
contentIntent is the expected intent to handle the notification. It's the own activity in this exercise.

The generated Notification can be cleared by:
NotificationManager.cancel(NOTIFICATION_ID);

Modify main.xml to have two Buttons to generate and clear the Notification
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/gen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Generate Notification"
/>
<Button
android:id="@+id/clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Clear Notification"
/>
</LinearLayout>


AndroidNotification.java
package com.exercise.AndroidNotification;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class AndroidNotification extends Activity {

NotificationManager myNotificationManager;
private static final int NOTIFICATION_ID = 1;

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

Button myGen = (Button)findViewById(R.id.gen);
myGen.setOnClickListener(myGenOnClickListener);
Button myClear = (Button)findViewById(R.id.clear);
myClear.setOnClickListener(myClearOnClickListener);


}

private void GeneratNotification(){

myNotificationManager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);

CharSequence NotificationTicket = "*** Notification";
CharSequence NotificationTitle = "Attention Please!";
CharSequence NotificationContent = "- Notification is coming -";
long when = System.currentTimeMillis();

Notification notification =
new Notification(android.R.drawable.btn_star_big_on,
NotificationTicket, when);

Context context = getApplicationContext();

Intent notificationIntent = new Intent(this,
AndroidNotification.class);
PendingIntent contentIntent =
PendingIntent.getActivity(this, 0, notificationIntent, 0);

notification.setLatestEventInfo(context, NotificationTitle,
NotificationContent, contentIntent);

myNotificationManager.notify(NOTIFICATION_ID, notification);

}

Button.OnClickListener myGenOnClickListener =
new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
GeneratNotification();
}

};

Button.OnClickListener myClearOnClickListener =
new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
myNotificationManager.cancel(NOTIFICATION_ID);
}

};
}


Download the files.

Thursday 22 October 2009

Windows 7 Netbook



Want to try out a Windows 7 Netbook. Check out this beauty from HP. Notice that the LCD screen actually has a matte finish!  I picked one up today. More comments to come as I get a chance to play with it.

Windows 7 Released Today

Microsoft is releasing Windows 7 today. From all accounts a most excellent operating system. Haven't had a chance to use it myself, I'm I am looking forward to trying it out. Grats Microsoft!

CheckBox

A checkbox is a specific type of two-states button that can be either checked or unchecked.



Modify the main.xml with two CheckBox and a Button.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<CheckBox
android:id="@+id/option1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option 1"
/>
<CheckBox
android:id="@+id/option2"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Option 2"
/>
<Button
android:id="@+id/OK"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="OK"
/>
</LinearLayout>


Modify the code to read and show the CheckBox's status.
package com.exercise.AndroidCheckBox;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.Toast;

public class AndroidCheckBox extends Activity {

CheckBox myOption1, myOption2;

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

myOption1 = (CheckBox)findViewById(R.id.option1);
myOption2 = (CheckBox)findViewById(R.id.option2);
Button myOK = (Button)findViewById(R.id.OK);
myOK.setOnClickListener(new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(AndroidCheckBox.this,
(CharSequence)(
"Option1 = " + myOption1.isChecked() +
" " +
"Option2 = " + myOption2.isChecked()),
Toast.LENGTH_LONG).show();
}

});

}
}


Download the files.

Tuesday 20 October 2009

Duct Tape Programmer

This is an old blog post, but new to me. Joel on Software wrote a post on Duct Tape Programming. A good read. I definitely agree that simpler is better.

ListActivity with @android:id/list and empty

Normally, ListActivity has a default layout that consists of a single, full-screen list in the center of the screen. However, if you desire, you can customize the screen layout by setting your own view layout with setContentView() in onCreate(). To do this, your own view MUST contain a ListView object with the id "@android:id/list".

In some case if the list is empty, the screen will become nothing. If you want to prompt the user if the list is empty, @android:id/list and @android:id/empty can be used.



Create a new AndroidEmptyListActivity extends ListActivity.
package com.exercise.AndroidEmptyListActivity;

import android.app.ListActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class AndroidEmptyListActivity extends ListActivity {

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

setContentView(R.layout.main);
SetupListView();
}

private void SetupListView()
{

String[] listItems = new String[] {
"Hello!",
"It's a Demo to use ListActivity,",
"with list/empty...",
"Is it Great?",
"android-er.blogspot.com"
};

/*
String[] listItems = new String[] {
};
*/

ArrayAdapter<String> listItemAdapter
= new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
listItems);

ListView lv = (ListView)this.findViewById(android.R.id.list);
lv.setAdapter(listItemAdapter);
}
}


Modify the main.xml to have a ListView with android:id="@android:id/list", and a TextView android:id="@android:id/empty".
<?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="@android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="List is Empty"/>
</LinearLayout>


If the List is not empty, the ListView will be shown, otherwise TextView will be shown.

Download the files.

Monday 19 October 2009

JIndenter First Option of NetBeans 6.7.1 Project Menu?

Duke Waving

I'm trying not to be angry as I write this post. But NetBeans developers why is JIndenter the first option when you right click the Project menu under the Files tab?



I was just finishing up a short project (i.e., example code) that will be seen by customers. I accidentally selected this option (my hand slipped), and the plugin proceeded to reformat all my code without a confirmation!  Not only did it add a comment spam to each of my source files, but since I had never configured the plugin, it messed up all my tabs.



Suggestions:

  1. Before globally rewriting someone's code, display a confirmation.

  2. Maybe an option like this should not be the first option on the menu? Just a thought.

Obviously I should have checked in a few times today and this would not be an issue. So it is really my fault. But really, this feature should be reconsidered. This happened on NetBeans 6.7.1 on OS X 10.5. Thanks for your consideration. :)

ListView with more items on each entry, using SimpleAdapter

In the previous articles of ListView, ArrayAdapter are used. Only one item can be display on each entry.

In this article, SimpleAdapter is used. Such that we have more than one items on each entry.



It's a ListActivity list the ASCII symbols from 32 to 126, in which there are two items, the code and the symbol, will be display on each entry.

The ASCII symbols are converted using EncodingUtils.getAsciiString().

We have a List to hold the code:symbol pair for each entry, and use SimpleAdapter to adapte to the ListView.

All we need is two files only
/res/row.xml, which is the layout of each entry on the ListView
<?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="wrap_content"
android:orientation="horizontal">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text = " " />
<TextView android:id="@+id/code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text = " : " />
<TextView android:id="@+id/symbol"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>


AndroidAscii.java
package com.exercise.AndroidAscii;

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

import org.apache.http.util.EncodingUtils;

import android.app.ListActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;

public class AndroidAscii extends ListActivity
{
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);

List<Map<String, CharSequence>> asciiPair =
new ArrayList<Map<String, CharSequence>>();

Map<String, CharSequence> asciidata;

for ( int i = 32; i <= 126; i++ )
{
asciidata = new HashMap<String, CharSequence>();

String strCode = String.valueOf(i);

byte[] data = {(byte) i};
CharSequence strSymbol = EncodingUtils.getAsciiString(data);

asciidata.put("Code", strCode );
asciidata.put("Symbol", strSymbol );
asciiPair.add(asciidata);
}

SimpleAdapter AsciiAdapter = new SimpleAdapter(
this,
asciiPair,
R.layout.row,
new String[] { "Code", "Symbol" },
new int[] { R.id.code, R.id.symbol } );

setListAdapter(AsciiAdapter);
}

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);

String myAscii = l.getItemAtPosition(position).toString();
Toast.makeText(this, myAscii, Toast.LENGTH_LONG).show();
}
}


Download the files.

Saturday 17 October 2009

Friday 16 October 2009

Growing Your Site

Pro Blogger has a link to a talk given by Digg's Kevin Rose in London. The talk is on how to grow your site to a million unique visitors.



Here were my take aways:

  • Keep things simple. More features are not always a good thing. Often they are looking for what they can remove from their site.

  • Build and release. Don't think you understand your users. Release new features, see how it works out with the users. Then iterate.



He also had some ideas about building community via Podcasting and parties and such. Not everyone is a Podcaster methinks. Also, it seems like the launch party thing is really only gonna work in Silicon Valley. Probably not a great help if you live in Northern Wyoming, for example.



In regard to other things Digg, I was listening to a 3 week old This Week in Tech pod cast. Kevin stated that with 40 million unique visitors a month Digg has reached the limits of MySQL. They are planning to move their back end to the open source Cassandra database.



I think that is a nice little factoid to know. Once you hit 20 or 30 million uniques, you may have to start weighing your database options.A nice problem to have. :)

ListView and ListActivity, with Layout Animation

In the article Layout Animation, list_layout_controller is included in layout, main.xml. In the last article ListView and ListActivity, ListView is implemented as ListActivity, without XML file for layout. How can apply the LayoutAnimationController.

In this article, the same visual effect of Layout Animation will be applied on ListView and ListActivity.



- Follow the last exercise, ListView and ListActivity.

- Same as Layout Animation, create the folder /res/anim and add the files list_layout_controller.xml and scale.xml.

list_layout_controller.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="50%"
android:animation="@anim/scale" />


scale.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="0.1"
android:toXScale="1"
android:fromYScale="0.1"
android:toYScale="1.0"
android:duration="2000"
android:pivotX="10%"
android:pivotY="10%"
android:startOffset="100" />
</set>


- Modify AndroidListActivity.java to add two lines of code in onCreate()
 @Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, COUNTRIES));
getListView().setTextFilterEnabled(true);

LayoutAnimationController controller
= AnimationUtils.loadLayoutAnimation(
this, R.anim.list_layout_controller);
getListView().setLayoutAnimation(controller);

}

That's!


ListView and ListActivity

A ListView is a View that shows items in a vertically scrolling list. The items are acquired from a ListAdapter.

In this exercise, I will show how to implement a ListView using ListActivity. The main activity (AndroidListView) call the ListActivity(AndroidListActivity) by startActivityForResult. AndroidListActivity have a ListView only, the selected item will be passed back to AndroidListView by means of Bundle.



_ Create a new Android Application named AndroidListView

- Modify main.xml to have a Button to start the ListActivity with ListView, and have a TextView to show the result from ListActivity.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/selectCountryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Select Country"
/>
<TextView
android:id="@+id/myCountry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>


- Modify AndroidListView.java
package com.exercise.AndroidListView;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class AndroidListView extends Activity {

TextView MyCountry;

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

MyCountry = (TextView)findViewById(R.id.myCountry);
Button SelectCountryButton = (Button)findViewById(R.id.selectCountryButton);
SelectCountryButton.setOnClickListener(SelectCountryButtonOnClickListener);
}

private Button.OnClickListener SelectCountryButtonOnClickListener =
new Button.OnClickListener()
{
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setClass(AndroidListView.this, AndroidListActivity.class);

startActivityForResult(intent, 0);
}
};

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==0)
{
switch (resultCode)
{ case RESULT_OK:
MyCountry.setText(data.getStringExtra("country"));
break;
case RESULT_CANCELED:
break;

}

}
}


}


- Create AndroidListActivity extends ListActivity

Right click com.exercise.AndroidListView, the package In the Project Windows on the left. Select New > Class >

Click Browser beside Superclass, select ListActivity. (May be you have to clear the content inside "Choose a type" box to make it appear in the "Matching items" list.

Type AndroidListActivity in the Name



Click Finish

- Modify AndroidListActivity
package com.exercise.AndroidListView;

import android.app.ListActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class AndroidListActivity extends ListActivity {

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

setListAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, COUNTRIES));
getListView().setTextFilterEnabled(true);
}

static final String[] COUNTRIES = new String[] {
"Afghanistan", "Albania", "Algeria", "American Samoa",
"Andorra", "Angola", "Anguilla", "Antarctica",
"Antigua and Barbuda", "Argentina", "Armenia", "Aruba",
"Australia", "Austria", "Azerbaijan", "Bahrain",
"Bangladesh", "Barbados", "Belarus", "Belgium", "Belize",
"Benin", "Bermuda", "Bhutan", "Bolivia",
"Bosnia and Herzegovina", "Botswana", "Bouvet Island",
"Brazil", "British Indian Ocean Territory",
"British Virgin Islands", "Brunei", "Bulgaria",
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia",
"Cameroon", "Canada", "Cape Verde", "Cayman Islands",
"Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia",
"Comoros", "Congo", "Cook Islands", "Costa Rica", "Croatia",
"Cuba", "Cyprus", "Czech Republic",
"Democratic Republic of the Congo", "Denmark", "Djibouti",
"Dominica", "Dominican Republic", "East Timor", "Ecuador",
"Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands",
"Fiji", "Finland", "Former Yugoslav Republic of Macedonia",
"France", "French Guiana", "French Polynesia",
"French Southern Territories", "Gabon", "Georgia", "Germany",
"Ghana", "Gibraltar", "Greece", "Greenland", "Grenada",
"Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau",
"Guyana", "Haiti", "Heard Island and McDonald Islands",
"Honduras", "Hong Kong", "Hungary", "Iceland", "India",
"Indonesia", "Iran", "Iraq", "Ireland", "Israel", "Italy",
"Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya", "Kiribati",
"Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon", "Lesotho",
"Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
"Macau", "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali",
"Malta", "Marshall Islands", "Martinique", "Mauritania",
"Mauritius", "Mayotte", "Mexico", "Micronesia", "Moldova",
"Monaco", "Mongolia", "Montserrat", "Morocco", "Mozambique",
"Myanmar", "Namibia", "Nauru", "Nepal", "Netherlands",
"Netherlands Antilles", "New Caledonia", "New Zealand",
"Nicaragua", "Niger", "Nigeria", "Niue", "Norfolk Island",
"North Korea", "Northern Marianas", "Norway", "Oman", "Pakistan",
"Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru",
"Philippines", "Pitcairn Islands", "Poland", "Portugal",
"Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda",
"Sqo Tome and Principe", "Saint Helena", "Saint Kitts and Nevis",
"Saint Lucia", "Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines", "Samoa", "San Marino",
"Saudi Arabia", "Senegal", "Seychelles", "Sierra Leone",
"Singapore", "Slovakia", "Slovenia", "Solomon Islands", "Somalia",
"South Africa", "South Georgia and the South Sandwich Islands",
"South Korea", "Spain", "Sri Lanka", "Sudan", "Suriname",
"Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland",
"Syria", "Taiwan", "Tajikistan", "Tanzania", "Thailand",
"The Bahamas", "The Gambia", "Togo", "Tokelau", "Tonga",
"Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
"Turks and Caicos Islands", "Tuvalu", "Virgin Islands", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"United States", "United States Minor Outlying Islands",
"Uruguay", "Uzbekistan", "Vanuatu", "Vatican City", "Venezuela",
"Vietnam", "Wallis and Futuna", "Western Sahara", "Yemen",
"Yugoslavia", "Zambia", "Zimbabwe"
};

@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
super.onListItemClick(l, v, position, id);

Intent intent = new Intent();
Bundle bundle = new Bundle();

bundle.putString("country", l.getItemAtPosition(position).toString());
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish();
}

}


Notice that we don't need to load a layout (at least, not in this case, because we're using the whole screen for our list). Instead, we just call setListAdapter() (which automatically adds a ListView to the ListActivity), and provide it with an ArrayAdapter that binds a simple_list_item_1 layout item to each entry in the COUNTRIES array (added next). The next line of code adds a text filter to the ListView, so that when the user begins typing, the list will filter the entire view to display only the items that match the entry.

- Modify AndroidMainfest.xml to include the AndroidListActivity.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exercise.AndroidListView"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".AndroidListView"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AndroidListActivity"></activity>
</application>
<uses-sdk android:minSdkVersion="3" />

</manifest>


Actually, it's same as Google example Hello, ListView, with some extra; such as how the inter-action between main activity and the ListActivity.

Download the files.

Thursday 15 October 2009

Layout Animation

Layout Animation can be used to add visual effects on any controls derived from ViewGroup, such as ListView. ListView is a view that shows items in a vertically scrolling list. The items come from the ListAdapter associated with this view.

In this article, I will have a example to show how to implement a simple Layout Animation.



Create a Android Application named AndroidLayoutAnimation.

- Create a new folder named /anim under /res

- Create two xml file under /res/anim to handle the animation
list_layout_controller.xml
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
android:delay="50%"
android:animation="@anim/scale" />


scale.xml
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/accelerate_interpolator">
<scale
android:fromXScale="0.1"
android:toXScale="1"
android:fromYScale="0.1"
android:toYScale="1.0"
android:duration="2000"
android:pivotX="10%"
android:pivotY="10%"
android:startOffset="100" />
</set>


- Modify main.xml to have a ListView and Button.
<?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/myListView"
android:persistentDrawingCache="animation|scrolling"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layoutAnimation="@anim/list_layout_controller" />
/>
<Button
android:id="@+id/myRestartButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Restart"
/>
</LinearLayout>


- Modify AndroidLayoutAnimation.java to setContentView() using listlayout.xml, and SetupListView(). In order to show the effect, a button is used to restart the animation.
package com.exercise.AndroidLayoutAnimation;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;

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

loadScreen();
}

private Button.OnClickListener MyRestartButtonOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
loadScreen();
}
};

private void loadScreen(){
setContentView(R.layout.main);
SetupListView();

Button MyRestartButton = (Button)findViewById(R.id.myRestartButton);
MyRestartButton.setOnClickListener(MyRestartButtonOnClickListener);
}

private void SetupListView()
{
String[] listItems = new String[] {
"Hello!",
"It's a Demo to use Layout Animation",
"Is it Great?",
"android-er.blogspot.com"
};

ArrayAdapter<String> listItemAdapter
= new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
listItems);

ListView lv = (ListView)this.findViewById(R.id.myListView);
lv.setAdapter(listItemAdapter);
}
}


Download the files.

Tuesday 13 October 2009

Animation background, using animation-list and AnimationDrawable

In the article Simulate Animation, using Runnable Thread, I described how to simulate animation using Runnable Thread. For sure, it's not a good practice. In this article I show how to implement animation using animation-list and AnimationDrawable.


The animation can be started and stopped by the two button, Start and Stop.

- Save the four graphics, used to form the animation of a rotating arrow, in the /res/drawable/ folder.



- Create a file named arrow_animation.xml in /res/drawable
<animation-list
xmlns:android="http://schemas.android.com/apk/res/android"
>
<item android:drawable="@drawable/arrow_01" android:duration="100" />
<item android:drawable="@drawable/arrow_02" android:duration="100" />
<item android:drawable="@drawable/arrow_03" android:duration="100" />
<item android:drawable="@drawable/arrow_04" android:duration="100" />
</animation-list>


- Modify main.xml to add two Button to Start and Stop animation
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
<Button
android:id="@+id/myStartButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"
/>
<Button
android:id="@+id/myStopButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Stop"
/>
<ImageView
android:id="@+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
</LinearLayout>


- Modify the Java code as:
package com.exercise.AndroidAnimationDrawable;

import android.app.Activity;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class AndroidAnimationDrawable extends Activity {

AnimationDrawable AniFrame;

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

Button MyStartButton = (Button)findViewById(R.id.myStartButton);
MyStartButton.setOnClickListener(MyStartButtonOnClickListener);
Button MyStopButton = (Button)findViewById(R.id.myStopButton);
MyStopButton.setOnClickListener(MyStopButtonOnClickListener);

ImageView MyImageView = (ImageView)findViewById(R.id.myImageView);
MyImageView.setBackgroundResource(R.drawable.arrow_animation);
AniFrame = (AnimationDrawable) MyImageView.getBackground();
}

Button.OnClickListener MyStartButtonOnClickListener =
new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AniFrame.start();
}
};

Button.OnClickListener MyStopButtonOnClickListener =
new Button.OnClickListener(){
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
AniFrame.stop();
}
};

}


----------------------------------------
In the book of Professional Android Application Development (Wrox Programmer to Programmer) by Reto Meier, it's stated that:

At the time of going to print, there is a bug in the AnimationDrawable class. Currently, AnimationDrawable resources are not properly loaded until some time after an Activity�s onCreate method has completed. Current work-arounds use timers to force a delay before loading a frame-by-frame resource.



I have the same problem; if AniFrame.start() is called inside onCreate() immediately, the animation cannot start. That's why I add the buttons to start and stop the operation.
----------------------------------------

Android Twitter Client, post updated status to Twitter from Android.

It's a Twitter Client post updated status using Twitter API.



There are three EditText in the application, for user name, password (hiden by password dots) and updated status. Submit to Twitter by pressing of the Submit button. If successful, a Toast with "OK" will be shown, otherwise "ERROR" will be shown.



- Before start; base64 is needed in the application, refer to the article Use base64 in Android to download and include it in the Build Path of the project.

- Modify AndroidManifest.xml, Allows Android applications to open network sockets: "android.permission.INTERNET".

- Modify main.xml to have the input EditText for user name, password and updated status.
<?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"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="User Name and Password"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="User Name:"
/>
<EditText
android:id="@+id/username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Password:"
/>
<EditText
android:id="@+id/password"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="What are you doing?"
/>
<EditText
android:id="@+id/whatareyoudoing"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/mySubmit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Submit"
/>
</LinearLayout>

main.xml can be downloaded here.

- Modify the java as:
package com.exercise.AndroidTwitterClient;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;

import org.apache.commons.codec.binary.Base64;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class AndroidTwitterClient extends Activity {

EditText MyUsername;
EditText MyPassword;
EditText MyStatus;

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

Button MySubmitButton = (Button)findViewById(R.id.mySubmit);
MySubmitButton.setOnClickListener(MySubmitButtonOnClickListener);
MyUsername = (EditText)findViewById(R.id.username);
MyPassword = (EditText)findViewById(R.id.password);
MyStatus = (EditText)findViewById(R.id.whatareyoudoing);
}

private Button.OnClickListener MySubmitButtonOnClickListener
= new Button.OnClickListener(){

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

if (SendTwitter(MyUsername.getText().toString(),
MyPassword.getText().toString(),
MyStatus.getText().toString())){
Toast.makeText(AndroidTwitterClient.this,
"OK", Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(AndroidTwitterClient.this,
"ERROR", Toast.LENGTH_SHORT).show();
}

}

};

public static boolean SendTwitter(String twitteruser,
String twitterpass, String msg) {

boolean result = true;

try {

//String twitteruser = "android_er";
//String twitterpass = "pass_word";
URLConnection connection = null;
String credentials = twitteruser + ":" + twitterpass;

String encodecredentials =
new String(Base64.encodeBase64(credentials.getBytes()));

URL url = new URL("http://twitter.com/statuses/update.xml");
String encodedData = URLEncoder.encode(msg, "UTF-8");

connection = url.openConnection();

connection.setRequestProperty( "Authorization",
"Basic " + encodecredentials);
connection.setDoOutput(true);

OutputStreamWriter out = new
OutputStreamWriter(connection.getOutputStream());
out.write("status=" + encodedData);
out.close();

BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream()));

while (in.readLine() != null) {
}
in.close();
} catch (Exception e) {
result = false;
}

return result;

}
}

AndroidTwitterClient.java can be downloaded here.