Thursday, 31 December 2009

Merry Christmas and Happy New Year!

Just wanted to wish everyone a Merry Christmas and a Happy New Year. A little late on the Christmas, but better late than never. :)

TimePicker, a widget that to select the time by hour, minute and AM or PM.

A TimePicker is a widget that allows the user to select the time by hour, minute and AM or PM.

TimePicker

Further extends from the previouse exercise of DatePicker.

Modify main to have one more button to start TimePicker.
<?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/datepickerbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="DatePicker"
/>
<Button
android:id="@+id/timepickerbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TimePicker"
/>
</LinearLayout>


Modify the main class.
package com.exercise.AndroidDatePicker;

import java.util.Calendar;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TimePicker;
import android.widget.Toast;

public class AndroidDatePicker extends Activity {

private int myYear, myMonth, myDay, myHour, myMinute;
static final int ID_DATEPICKER = 0;
static final int ID_TIMEPICKER = 1;

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

Button datePickerButton = (Button)findViewById(R.id.datepickerbutton);
Button timePickerButton = (Button)findViewById(R.id.timepickerbutton);
datePickerButton.setOnClickListener(datePickerButtonOnClickListener);
timePickerButton.setOnClickListener(timePickerButtonOnClickListener);
}

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

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
final Calendar c = Calendar.getInstance();
myYear = c.get(Calendar.YEAR);
myMonth = c.get(Calendar.MONTH);
myDay = c.get(Calendar.DAY_OF_MONTH);
showDialog(ID_DATEPICKER);
}
};

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

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
final Calendar c = Calendar.getInstance();
myHour = c.get(Calendar.HOUR_OF_DAY);
myMinute = c.get(Calendar.MINUTE);
showDialog(ID_TIMEPICKER);
}
};

@Override
protected Dialog onCreateDialog(int id) {
// TODO Auto-generated method stub
switch(id){
case ID_DATEPICKER:
Toast.makeText(AndroidDatePicker.this,
"- onCreateDialog(ID_DATEPICKER) -",
Toast.LENGTH_LONG).show();
return new DatePickerDialog(this,
myDateSetListener,
myYear, myMonth, myDay);
case ID_TIMEPICKER:
Toast.makeText(AndroidDatePicker.this,
"- onCreateDialog(ID_TIMEPICKER) -",
Toast.LENGTH_LONG).show();
return new TimePickerDialog(this,
myTimeSetListener,
myHour, myMinute, false);
default:
return null;

}
}

private DatePickerDialog.OnDateSetListener myDateSetListener
= new DatePickerDialog.OnDateSetListener(){

@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
// TODO Auto-generated method stub
String date = "Year: " + String.valueOf(year) + "\n"
+ "Month: " + String.valueOf(monthOfYear+1) + "\n"
+ "Day: " + String.valueOf(dayOfMonth);
Toast.makeText(AndroidDatePicker.this, date,
Toast.LENGTH_LONG).show();
}
};

private TimePickerDialog.OnTimeSetListener myTimeSetListener
= new TimePickerDialog.OnTimeSetListener(){

@Override
public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
// TODO Auto-generated method stub
String time = "Hour: " + String.valueOf(hourOfDay) + "\n"
+ "Minute: " + String.valueOf(minute);
Toast.makeText(AndroidDatePicker.this, time,
Toast.LENGTH_LONG).show();
}
};

}


Download the files.



Avatar - The Review



Official Avatar Movie

Well now that I have seen Avatar in both 3d and 2d, it is time for me to drop my $0.02 on the subject.



First off, this is a great movie. A class David vs Goliath story that takes place in an amazingly detailed and interesting world. Everything just fits really well together, the plants, the animals, the floating rocks. James Cameron deserves a lot of praise for creating such a rich setting. I especially liked the night scenes with all the glow-in-the-dark plants.



In 3d, the movie works because the focus is on the story and not the 3d effects. Any 3d film I have seen in the past seems to be totally focused on cheesy 3d effects that happen every 10 or 15 minutes. This movie includes some great 3d effects, but it never forgets to tell the story first. In addition, the movie looks great in 3d. The colors and imagery are just as rich in the 2d and 3d versions. The 3d version doesn't feel washed out or blurry at all.



Much has been written about how 3d is gonna save movie theaters, blah blah blah. It still seems like a bit of a gimmick to me. It really doesn't add that much to the experience. Oh sure, it will prevent movie pirates from sending films out on the Net. But is it that much better than watching a movie at home on your own HD set?  I am not so sure about that.



In regards to the movie being preachy, I just don't buy that. There might be what, 1 or 2 statements in the entire movie that could be considered in that vein. But to me it is just a movie. The weak fighting back against the strong is as classic as it gets. Go see Avatar, you will be glad you did.



My rating. (5 out of 5)

DatePicker, a widget to select month, day and year.

A DatePicker is a widget that allows the user to select a month, day and year.

DatePicker

Create a new Android Application, AndroidDatePicker.

Modify the layout, main.xml, to add a button to start the DatePicker.
<?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/datepickerbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="DatePicker"
/>
</LinearLayout>


Modify the main program, AndroidDatePicker.java.
package com.exercise.AndroidDatePicker;

import java.util.Calendar;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.Toast;

public class AndroidDatePicker extends Activity {

private int myYear, myMonth, myDay;
static final int ID_DATEPICKER = 0;

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

Button datePickerButton = (Button)findViewById(R.id.datepickerbutton);
datePickerButton.setOnClickListener(datePickerButtonOnClickListener);
}

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

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
final Calendar c = Calendar.getInstance();
myYear = c.get(Calendar.YEAR);
myMonth = c.get(Calendar.MONTH);
myDay = c.get(Calendar.DAY_OF_MONTH);
showDialog(ID_DATEPICKER);
}
};

@Override
protected Dialog onCreateDialog(int id) {
// TODO Auto-generated method stub
switch(id){
case ID_DATEPICKER:
Toast.makeText(AndroidDatePicker.this,
"- onCreateDialog -",
Toast.LENGTH_LONG).show();
return new DatePickerDialog(this,
myDateSetListener,
myYear, myMonth, myDay);
default:
return null;
}
}

private DatePickerDialog.OnDateSetListener myDateSetListener
= new DatePickerDialog.OnDateSetListener(){
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
// TODO Auto-generated method stub
String date = "Year: " + String.valueOf(year) + "\n"
+ "Month: " + String.valueOf(monthOfYear+1) + "\n"
+ "Day: " + String.valueOf(dayOfMonth);
Toast.makeText(AndroidDatePicker.this, date,
Toast.LENGTH_LONG).show();
}
};
}


A Toast will be displayed in onCreateDialog(), to show that DatePickerDialog() will be created once only (in the first time display the dialog).

Download the files.



Wednesday, 30 December 2009

Uses tabs to navigate between different views, TabWidget.

A TabWidget offers the ability to easily draw an interface that uses tabs to navigate between different views.







Start a new Android Application, named AndrodTab extends TabActivity.

To use TabWidget, a TabHost have to be used to contain the entire layout of the Activity. A TabHost requires two descendant elements: a TabWidget and a FrameLayout. In order to properly layout these elements, we've put them inside a vertical LinearLayout. Otherwise, the TabWidget and FrameLayout will overlay each other. The FrameLayout is where we keep the content that will change with each tab. Each child in the FrameLayout will be associated with a different tab.

Modify the main.xml

<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/tabview1">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="tabview1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Just put something here" />
/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/tabview2">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="tabview2" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="A" />
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="B" />
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C" />
/>
</LinearLayout>
<TextView
android:id="@+id/tabview3"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="tabview3" />
</FrameLayout>
</LinearLayout>
</TabHost>


Finally, modify the method onCreate() in AndroidTab.java

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

TabHost mTabHost = getTabHost();

mTabHost.addTab(mTabHost.newTabSpec("tab_test1")
.setIndicator("TAB 1")
.setContent(R.id.tabview1));
mTabHost.addTab(mTabHost.newTabSpec("tab_test2")
.setIndicator("TAB 2")
.setContent(R.id.tabview2));
mTabHost.addTab(mTabHost.newTabSpec("tab_test3")
.setIndicator("TAB 3")
.setContent(R.id.tabview3));

mTabHost.setCurrentTab(0);
}


getTabHost() returns a reference to the TabHost. Then call addTab() for each of the tabs to add them into the TabHost. setIndicator() set the text for the tab button, and setContent() define the View associate with the tab. At the end, call setCurrentTab() to define which tab should be opened by default.

Download the files.

Use system wallpaper as application's wallpaper

Just as Android provides other built-in resources, there are several themes that you swap in without having to write one yourself.

To set this theme for all the activites of your application, open the AndroidManifest.xml file and edit the <application> tag to include the android:theme attribute with the theme name.

If you want the theme applied to just one Activity in your application, then add the theme attribute to the <activity> tag, instead.

If you want to use system wallpaper as your own application's wall paper, add android:theme="@android:style/Theme.Wallpaper" in your AndroidManifest.xml.

Example:



<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exercise.AndroidSysTheme"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:theme="@android:style/Theme.Wallpaper"
>
<activity android:name=".AndroidSysTheme"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
<uses-sdk android:minSdkVersion="5" />

</manifest>


Sunday, 27 December 2009

Read Exif information in a JPEG file, ExifInterface

ExifInterface is a class for reading and writing Exif tags in a JPEG file. Here is a simple exercise to read Exif tags. To make it simple, it will read a fixed JPG file from SD Card. The name and path of the JPG file is hard coded.



Refer to the article "Create SD Card in Android Emulator and copy files into, in Eclipse, Emulator and DDMS" to copy a JPG file with Exif into SD Card. And also, you have to update the program code with your own path and file name.

Create a Android Application, AndroidExif, with the following setting.

Project Name: AndroidExif
Build Target: Google APIs/Platform 2.0/API Level 5
Application name: AndroidExif
Package name: com.exercise.AndroidExif
Create Activity: AndroidExif
Min SDK Version: 5



Modify the layout file, main.xml, to assign id on the TextView.
<?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:id="@+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
/>
</LinearLayout>


Modify the code, AndroidExif.java, to implement our Activity to read Exif.
package com.exercise.AndroidExif;

import java.io.IOException;

import android.app.Activity;
import android.media.ExifInterface;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidExif extends Activity {

TextView myTextView;

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

myTextView = (TextView)findViewById(R.id.textview);

//change with the filename & location of your photo file
String filename = "/sdcard/DSC_3509.JPG";
try {
ExifInterface exif = new ExifInterface(filename);
ShowExif(exif);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(this, "Error!",
Toast.LENGTH_LONG).show();
}
}

private void ShowExif(ExifInterface exif)
{
String myAttribute="Exif information ---\n";
myAttribute += getTagString(ExifInterface.TAG_DATETIME, exif);
myAttribute += getTagString(ExifInterface.TAG_FLASH, exif);
myAttribute += getTagString(ExifInterface.TAG_GPS_LATITUDE, exif);
myAttribute += getTagString(ExifInterface.TAG_GPS_LATITUDE_REF, exif);
myAttribute += getTagString(ExifInterface.TAG_GPS_LONGITUDE, exif);
myAttribute += getTagString(ExifInterface.TAG_GPS_LONGITUDE_REF, exif);
myAttribute += getTagString(ExifInterface.TAG_IMAGE_LENGTH, exif);
myAttribute += getTagString(ExifInterface.TAG_IMAGE_WIDTH, exif);
myAttribute += getTagString(ExifInterface.TAG_MAKE, exif);
myAttribute += getTagString(ExifInterface.TAG_MODEL, exif);
myAttribute += getTagString(ExifInterface.TAG_ORIENTATION, exif);
myAttribute += getTagString(ExifInterface.TAG_WHITE_BALANCE, exif);
myTextView.setText(myAttribute);
}

private String getTagString(String tag, ExifInterface exif)
{
return(tag + " : " + exif.getAttribute(tag) + "\n");
}
}


Download the files.



Wednesday, 23 December 2009

Android Scripting Environment, ase_r15.apk, just released.

The Android Scripting Environment (ASE) brings scripting languages to Android by allowing you to edit and execute scripts and interactive interpreters directly on the Android device. These scripts have access to many of the APIs available to full-fledged Android applications, but with a greatly simplified interface.

android-scripting Project Home>>

My old article, android-scripting: Android Scripting Environment, described how to install Android Scripting Environment on Android Emulator.

AndroidMapper files list

It's a bug here:

Using onTouchEvent(MotionEvent, MapView) to handle user touching as in the example, it's work fine on Android Emulator, but not working on true phone.

Please refer to another article "Problem of onTouchEvent(MotionEvent, MapView) on MapView" for details.

Android Er@2010-06-22
------------------------------------------------

The the passed articles, I have a series to describe the steps in implementing AndroidMapper, a Android application to get and update location using MapView. Here is a list of the related article:





- Exercise AndroidMapper, Android Application using MapView
- Display a marker on MapView, using Overlays
- Get center location of a map, using MapView.getMapCenter()
- Move the marker on MapView
- Move the marker on MapView with Zoom Control Seekbar
- Implement a Help Dialog, using onCreateOptionsMenu(), onOptionsItemSelected() and AlertDialog.Builder
- Add options menu on MapView
- AndroidMapper: return result in Bundle

Related articles:
- Change GPS location of Android Emulator
- Obtaining a Maps API Key


Download the files.

AndroidMapper: return result in Bundle.

It's (may be) the last article in the series of AndroidMapper. Here, methods exitOk() and exitCancel() in AndroidMapView were implemented to return or ignore the updated location back to the main activity, AndroidMapper. As you know, a application return nothing after processed is useless!



In exitOk() method in AndroidMapView, the updated location will be packed inside Bundle, and finish() with RESULT_OK.
private void exitOk()
{
Bundle bundle = new Bundle();
bundle.putInt("Longitude", updatedGeoPoint.getLongitudeE6());
bundle.putInt("Latitude", updatedGeoPoint.getLatitudeE6());
Intent intent = new Intent();
intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish();
}


In exitCancel() method in AndroidMapView, simple finish() with RESULT_CANCELED.
 private void exitCancel()
{
Intent intent = new Intent();
setResult(RESULT_CANCELED, intent);
finish();
}


In onActivityResult() in AndroidMapper, resultCode will be checked if it's RESULT_OK. If yes, the data inside Bundle will be used to update mylongitude and mylatitude.
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
if(resultCode==RESULT_OK)
{
Bundle extras = data.getExtras();
String mylongitudeText = String.valueOf((float)extras.getInt("Longitude")/1000000);
String mylatitudeText = String.valueOf((float)extras.getInt("Latitude")/1000000);
mylongitude.setText(mylongitudeText);
mylatitude.setText(mylatitudeText);
validLocation();
}
}


Download the files.

Monday, 21 December 2009

Add options menu on MapView

Further works on the exercise, AndroidMapper. A options menu will be added in AndroidMapView using XML; with options of OK, Cancel, Reload and Help.



Reload: is used to reload the initiate location.
Help: display the help message.
OK and Cancel: not yet implemented. They will be used to return to the last Activity, AndroidMapper, with or ignore the updated location.

To know more about how to add options menu using XML, refer to the article Implement option menu using XML.

Download the files.

Sunday, 20 December 2009

Implement option menu using XML

In my old articles, "Exercise: Menu and Dialog" and "Implement a Help Dialog, using onCreateOptionsMenu(), onOptionsItemSelected() and AlertDialog.Builder", option menu was added using programmatic coding. In this article, another method to implement option menu, using XML, will be introduced.



First of all, create a new Android application AndroidOptionMenu.

Create a folder, "menu", under /res

Create a menu.xml under /res/menu

<?xml version="1.0" encoding="UTF-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_help"
android:title="Help" />
<item android:id="@+id/menu_OK"
android:title="OK" />
<item android:id="@+id/menu_Cancel"
android:title="Cancel" />
</menu>


Modify AndroidOptionMenu.java to implement the methods onCreateOptionsMenu(Menu menu) and onOptionsItemSelected(MenuItem item):
package com.exercise.AndroidOptionMenu;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

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

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater myMenuInflater = getMenuInflater();
myMenuInflater.inflate(R.menu.menu, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch(item.getItemId()){
case(R.id.menu_OK):
Toast.makeText(this, "OK", Toast.LENGTH_LONG).show();
break;
case(R.id.menu_help):
Toast.makeText(this, "Help", Toast.LENGTH_LONG).show();
break;
case(R.id.menu_Cancel):
Toast.makeText(this, "Cancel", Toast.LENGTH_LONG).show();
break;
}
return true;
}
}

Saturday, 19 December 2009

Stack Overflow for Android beginner, a collaboratively edited question and answer site for Android programmers.

Google announced working with Stack Overflow, a collaboratively edited question and answer site for programmers, to improve developer support, especially for developers new to Android. In essence, the Android tag on Stack Overflow will become an official Android app development Q&A medium. Google encourage to post beginner-level technical questions there. It's also important to point out that Google don't plan to change the android-developers group, so intermediate and expert users should still feel free to post there.

Thursday, 17 December 2009

Android device dashboard of Platform Versions

Google just announced the new device dashboard of Platform Versions. It provides information about deployed Android-powered devices that is helpful to developers as they build and update their apps. The dashboard provides the relative distribution of Android platform versions on devices running Android Market.


Data collected during two weeks ending on 12/14/2009


Tuesday, 15 December 2009

More Thoughts on Google OS

Google LogoAfter listening to the This week in Google from a few weeks back, I have a better understanding of just what the new Google OS is. It is interesting that Google is targeting the Netbook platform for this OS. With the requirement to essentially have an Internet connection to do anything, this would seem like quite a limitation to me. There are still a lot of places I go with a laptop or netbook where there is no Wifi.



To me a better target might be a really inexpensive desktop replacement. Almost everyone I know now has wifi unless they live in very remote places. The combination of Google Office and OS would meet the needs of about 90% of the folks. Word processing, web surfing, video streaming, social networks, etc.. would all be covered. The only thing I see missing is a replacement for Quicken.



Just think how nice it would be to set up your parents or grandparents on a PC that  is always up to date, gets no viruses and is really easy to use. With a low price that would be a very compelling story.

Monday, 14 December 2009

Implement a Help Dialog, using onCreateOptionsMenu(), onOptionsItemSelected() and AlertDialog.Builder.



Follow the code from previouse exercise, Move the marker on MapView with Zoom Control Seekbar.
In this exercise, a OptionsMenu with a single OptionsItem of help will be added.

The option items is added to the option menu in the method onCreateOptionsMenu(), in which initialize the contents of the Activity's standard options menu.

Because it's only one item in the menu, Menu.add(CharSequence title) is used to add item to the menu. Otherwise, Menu.add(int groupId, int itemId, int order, CharSequence title) or Menu.add(int groupId, int itemId, int order, int titleRes) have to be used.

onOptionsItemSelected() will be called whenever an item in options menu is selected. Also, because it's only one item in the menu, so there are no need to check which item have been selected, otherwise, you have to check the itemId.

Add the following three method in the class AndroidMapView:
 public boolean onCreateOptionsMenu(Menu menu)
{
menu.add("Help");
return super.onCreateOptionsMenu(menu);
}

private void openOptionsHelpDialog()
{
new AlertDialog.Builder(this)
.setTitle(R.string.help_title).setMessage(R.string.help_message)
.setPositiveButton(R.string.help_ok,
new DialogInterface.OnClickListener() {

@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub

}
}
)
.show();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
super.onOptionsItemSelected(item);
openOptionsHelpDialog();
return true;
}


Implement a file helpmessage.xml in the folder /res/values/, it's used to hold the text used inside the Help Dialog.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="help_title">Help!</string>
<string name="help_message">\n
Pan on the Map: touch the screen and move.\n
Update location: touch on the screen.\n
Zoom: slice on the SeekBar on bottom.\n
</string>
<string name="help_ok">OK</string>
</resources>


Download the files.

Friday, 11 December 2009

Storing Data in Safari for iPhone

Recently I have been thinking of different ways you could store simple text messages and the like on an iPhone. There are a number of note applications out there and you can sync the iPhone note application with some e-mail clients.



Anyway, I ran across this inventive solution, store an entire application in a URL. Its all explained on the Life with Lunchhooks blog. Now that is really cool. Simply put that in a link. Then click the link and save it to your Bookmark list. Whamo-bamo, you have some offline data stored on your iPhone. The only limitation is the data is read only.



But fear not, there are a number of ways you can store applications that can both read and write offline. Apple has this offline application programming guide on their web site. Really neat stuff. I gotta find some time to play with this. :)

Optimize your layouts using layoutopt

layoutopt is a command-line tool that helps you optimize the layouts and layout hierarchies of your applications. You can run it against your layout files or resource directories to quickly check for inefficiencies or other types of problems that could be affecting the performance of your application.

Wednesday, 9 December 2009

Move the marker on MapView with Zoom Control Seekbar

In the last exercise, Move the marker on MapView, user touch on screen to pan the Map and update marker. It's very confuse with the BuiltInZoomControls function. So I separate the zoom function outside the MapView in this exercise. It's a SeekBar under the MapView, user can change the zoom level by sliding on the SeekBar.



It involve modification on mymapview.xml and AndroidMapView.java.

mymapview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>
<CheckBox
android:id="@+id/satellite"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" Satellite "
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="@+id/longitude"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Longitude:"
/>
<TextView
android:id="@+id/latitude"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Latitude:"
/>
</LinearLayout>
</LinearLayout>
<com.google.android.maps.MapView
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:clickable="true"
android:apiKey="0pkoAyp5YM52XRlxoRPJeJbP0Tp69yYlrRO7lJg"
/>
<SeekBar
android:id="@+id/zoombar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="20"
android:progress="0"/>
</LinearLayout>


AndroidMapView.java
package com.AndroidMapper;

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

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;

public class AndroidMapView extends MapActivity {

private TextView myLongitude, myLatitude;
private CheckBox mySatellite;

private MapView myMapView;
private MapController myMapController;

private SeekBar myZoomBar;

private void SetSatellite()
{
myMapView.setSatellite(mySatellite.isChecked());
};

@Override
protected void onCreate(Bundle icicle) {
// TODO Auto-generated method stub
super.onCreate(icicle);
setContentView(R.layout.mymapview);

Bundle bundle = this.getIntent().getExtras();
int Mode = bundle.getInt("Mode");

myMapView = (MapView)findViewById(R.id.mapview);
myMapController = myMapView.getController();
myMapView.setBuiltInZoomControls(false);

myLongitude = (TextView)findViewById(R.id.longitude);
myLatitude = (TextView)findViewById(R.id.latitude);
mySatellite = (CheckBox)findViewById(R.id.satellite);
mySatellite.setOnClickListener(mySatelliteOnClickListener);

myZoomBar = (SeekBar)findViewById(R.id.zoombar);
SetZoomLevel();
myZoomBar.setOnSeekBarChangeListener(myZoomBarOnSeekBarChangeListener);

SetSatellite();

if(Mode == 0)
{
GeoPoint initGeoPoint = myMapView.getMapCenter();
CenterLocation(initGeoPoint);
}
else if(Mode == 1)
{
int intLatitude = bundle.getInt("Latitude");
int intLongitude = bundle.getInt("Longitude");
GeoPoint initGeoPoint = new GeoPoint(intLatitude, intLongitude);
CenterLocation(initGeoPoint);
}
}

private SeekBar.OnSeekBarChangeListener myZoomBarOnSeekBarChangeListener =
new SeekBar.OnSeekBarChangeListener(){

public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
SetZoomLevel();
}

public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub

}

public void onStopTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub

}
};

private void SetZoomLevel()
{
int myZoomLevel = myZoomBar.getProgress()+1;
myMapController.setZoom(myZoomLevel);
Toast.makeText(this,
"Zoom Level : " + String.valueOf(myZoomLevel),
Toast.LENGTH_LONG).show();
};

private void placeMarker(int markerLatitude, int markerLongitude)
{
Drawable marker=getResources().getDrawable(
android.R.drawable.ic_menu_myplaces);
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
myMapView.getOverlays().add(new InterestingLocations(marker,
markerLatitude, markerLongitude));
}

@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}

private void CenterLocation(GeoPoint centerGeoPoint)
{
myMapController.animateTo(centerGeoPoint);

myLongitude.setText("Longitude: "+
String.valueOf((float)centerGeoPoint.getLongitudeE6()/1000000));
myLatitude.setText("Latitude: "+
String.valueOf((float)centerGeoPoint.getLatitudeE6()/1000000));
placeMarker(centerGeoPoint.getLatitudeE6(),
centerGeoPoint.getLongitudeE6());
};

private CheckBox.OnClickListener mySatelliteOnClickListener =
new CheckBox.OnClickListener(){

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

class InterestingLocations extends ItemizedOverlay<OverlayItem>{

private List<OverlayItem> locations =
new ArrayList<OverlayItem>();
private Drawable marker;
private OverlayItem myOverlayItem;

boolean MoveMap;

public InterestingLocations(Drawable defaultMarker,
int LatitudeE6, int LongitudeE6) {
super(defaultMarker);
// TODO Auto-generated constructor stub
this.marker=defaultMarker;
// create locations of interest
GeoPoint myPlace = new GeoPoint(LatitudeE6,LongitudeE6);
myOverlayItem = new OverlayItem(myPlace, "My Place", "My Place");
locations.add(myOverlayItem);

populate();
}

@Override
protected OverlayItem createItem(int i) {
// TODO Auto-generated method stub
return locations.get(i);
}

@Override
public int size() {
// TODO Auto-generated method stub
return locations.size();
}

@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
// TODO Auto-generated method stub
super.draw(canvas, mapView, shadow);

boundCenterBottom(marker);
}

@Override
public boolean onTouchEvent(MotionEvent arg0, MapView arg1) {
// TODO Auto-generated method stub
//super.onTouchEvent(arg0, arg1);



int Action = arg0.getAction();
if (Action == MotionEvent.ACTION_UP){

if(!MoveMap)
{
Projection proj = myMapView.getProjection();
GeoPoint loc = proj.fromPixels((int)arg0.getX(), (int)arg0.getY());

//remove the last marker
myMapView.getOverlays().remove(0);

CenterLocation(loc);
}

}
else if (Action == MotionEvent.ACTION_DOWN){

MoveMap = false;

}
else if (Action == MotionEvent.ACTION_MOVE){
MoveMap = true;
}

return super.onTouchEvent(arg0, arg1);
//return false;
}
}
}


Download the files.

Tuesday, 8 December 2009

Word Wrap in Linux/Unix/OS X



Have you ever needed to word wrap a text document at a specific width?  Well I did and I found out there is a Unix utility that called fold that does this. Check out an explanation on the nixCraft blog.

Move the marker on MapView

It's a bug here:

Using onTouchEvent(MotionEvent, MapView) to handle user touching as in the example, it's work fine on Android Emulator, but not working on true phone.

Please refer to another article "Problem of onTouchEvent(MotionEvent, MapView) on MapView" for details.

Thanks thibault let me know the case:)

Android Er@2010-06-22
------------------------------------------------

In the last exercises, Display a marker on MapView, using Overlays and Get center location of a map, using MapView.getMapCenter(), a marker is displayed on the center of the MapView. In this exercise, I want to add the capability of moving the marker on the Map. Unfortunately, I can't find any solution (from books and internet) to move (drag) the marker on MapView, similar to web version. Finally, I made a alternative.

If user Touch on screen without moving, it will be treated as update marker, the marker will be place on the new location, and it will be centered on the MapView. If user Touch on screen and Move, it will be treated as pan the MapView, the marker will not be changed.



The main logic is in the method onTouchEvent(MotionEvent arg0, MapView arg1), ACTION_UP, ACTION_DOWN and ACTION_MOVE cases.

((int)arg0.getX(), (int)arg0.getY()) is the on-screen pixel coordinates, it can be translated to latitude/longitude(GePoint) using Projection.fromPixels.

AndroidMapView.java

package com.AndroidMapper;

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

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;
import com.google.android.maps.Projection;

public class AndroidMapView extends MapActivity {

private TextView myLongitude, myLatitude;
private CheckBox mySatellite;

private MapView myMapView;
private MapController myMapController;

private void SetSatellite()
{
myMapView.setSatellite(mySatellite.isChecked());
};

@Override
protected void onCreate(Bundle icicle) {
// TODO Auto-generated method stub
super.onCreate(icicle);
setContentView(R.layout.mymapview);

Bundle bundle = this.getIntent().getExtras();
int Mode = bundle.getInt("Mode");

myMapView = (MapView)findViewById(R.id.mapview);
myMapController = myMapView.getController();
myMapView.setBuiltInZoomControls(true);

myLongitude = (TextView)findViewById(R.id.longitude);
myLatitude = (TextView)findViewById(R.id.latitude);
mySatellite = (CheckBox)findViewById(R.id.satellite);
mySatellite.setOnClickListener(mySatelliteOnClickListener);

SetSatellite();

if(Mode == 0)
{
GeoPoint initGeoPoint = myMapView.getMapCenter();
CenterLocation(initGeoPoint);
}
else if(Mode == 1)
{
int intLatitude = bundle.getInt("Latitude");
int intLongitude = bundle.getInt("Longitude");
GeoPoint initGeoPoint = new GeoPoint(intLatitude, intLongitude);
CenterLocation(initGeoPoint);
}
}

private void placeMarker(int markerLatitude, int markerLongitude)
{
Drawable marker=getResources().getDrawable(
android.R.drawable.ic_menu_myplaces);
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
myMapView.getOverlays().add(new InterestingLocations(marker,
markerLatitude, markerLongitude));
}

@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}

private void CenterLocation(GeoPoint centerGeoPoint)
{
myMapController.animateTo(centerGeoPoint);

myLongitude.setText("Longitude: "+
String.valueOf((float)centerGeoPoint.getLongitudeE6()/1000000));
myLatitude.setText("Latitude: "+
String.valueOf((float)centerGeoPoint.getLatitudeE6()/1000000));
placeMarker(centerGeoPoint.getLatitudeE6(),
centerGeoPoint.getLongitudeE6());
};

private CheckBox.OnClickListener mySatelliteOnClickListener =
new CheckBox.OnClickListener(){

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

class InterestingLocations extends ItemizedOverlay<OverlayItem>{

private List<OverlayItem> locations =
new ArrayList<OverlayItem>();
private Drawable marker;
private OverlayItem myOverlayItem;

boolean MoveMap;

public InterestingLocations(Drawable defaultMarker,
int LatitudeE6, int LongitudeE6) {
super(defaultMarker);
// TODO Auto-generated constructor stub
this.marker=defaultMarker;
// create locations of interest
GeoPoint myPlace = new GeoPoint(LatitudeE6,LongitudeE6);
myOverlayItem = new OverlayItem(myPlace, "My Place", "My Place");
locations.add(myOverlayItem);

populate();
}

@Override
protected OverlayItem createItem(int i) {
// TODO Auto-generated method stub
return locations.get(i);
}

@Override
public int size() {
// TODO Auto-generated method stub
return locations.size();
}

@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
// TODO Auto-generated method stub
super.draw(canvas, mapView, shadow);

boundCenterBottom(marker);
}

@Override
public boolean onTouchEvent(MotionEvent arg0, MapView arg1) {
// TODO Auto-generated method stub
//super.onTouchEvent(arg0, arg1);



int Action = arg0.getAction();
if (Action == MotionEvent.ACTION_UP){

if(!MoveMap)
{
Projection proj = myMapView.getProjection();
GeoPoint loc = proj.fromPixels((int)arg0.getX(), (int)arg0.getY());

//remove the last marker
myMapView.getOverlays().remove(0);

CenterLocation(loc);
}

}
else if (Action == MotionEvent.ACTION_DOWN){

MoveMap = false;

}
else if (Action == MotionEvent.ACTION_MOVE){
MoveMap = true;
}

return super.onTouchEvent(arg0, arg1);
//return false;
}
}
}


Download the files.

Sunday, 6 December 2009

MOTODEV Studio for Android

MOTODEV Studio for Android is a Complete Development Package: One installer ensures an integrated development environment with Eclipse 3.5 and Android Development Tools (ADT) plus automatic download and configuration of the latest Android SDK.

MOTODEV Studio for Android Version 1.0.2 now support Android 1.1, Android 1.5, Android 1.6, Android 2.0 (Motorola Droid and Milestone)

MOTODEV Web Site

Friday, 4 December 2009

Android 2.0.1, Release 1 is available

Android 2.0.1 is a minor platform release deployable to Android-powered handsets starting in December 2009. This release includes minor API changes, bug fixes and framework behavioral changes.

Details>>>

If you have installed Android 1.6 or later SDK packages with Eclipse, you can update your Android SDK using the Android SDK and AVD Manager.

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


Select Available Packages on the left and select the components you want to update, and click Install Selected.


Click to select Accept All and click Install Accepted.


Wait for Installing, then restart Eclipse.


After Eclipse restarted, click Window -> Preferences, and click Android on the left. You will be asked to update ADT to the latest version.


To update ADT, click Help -> Check for Updates in Eclipse.


The list of Updates will be come out soon. Select All and click Next, and then Finish.


Accept the Security Warning by clicking OK.


And finally accept Restart Eclipse again.

That's All.

Tuesday, 1 December 2009

Static Factory Methods vs Constructors

Duke WavingI was looking for a good explanation for the advantages of using static factory methods instead of constructors. The Redemption in a Blog site has a very good explanation. It just goes to should that old blog posts don't fade away, they just get better with time. :)

Get center location of a map, using MapView.getMapCenter().

In the last exercise, Display a marker on MapView, using Overlays, a marker will be displayed in Mode 1 (with starting location) only. In this exercise, current center location of the Map in Mode 0 (without starting location) will be retrieved using the method MapView.getMapCenter(). Such that the appearance of both mode will be kept consistance.



To achieve it, AndroidMapView have to be modified, to add function in onCreate() to handle Mode 0.
package com.AndroidMapper;

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

import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapController;
import com.google.android.maps.MapView;
import com.google.android.maps.OverlayItem;

public class AndroidMapView extends MapActivity {

private TextView myLongitude, myLatitude;
private CheckBox mySatellite;

private MapView myMapView;
private MapController myMapController;

private void SetSatellite()
{
myMapView.setSatellite(mySatellite.isChecked());
};

@Override
protected void onCreate(Bundle icicle) {
// TODO Auto-generated method stub
super.onCreate(icicle);
setContentView(R.layout.mymapview);

Bundle bundle = this.getIntent().getExtras();
int Mode = bundle.getInt("Mode");

myMapView = (MapView)findViewById(R.id.mapview);
myMapController = myMapView.getController();
myMapView.setBuiltInZoomControls(true);

myLongitude = (TextView)findViewById(R.id.longitude);
myLatitude = (TextView)findViewById(R.id.latitude);
mySatellite = (CheckBox)findViewById(R.id.satellite);
mySatellite.setOnClickListener(mySatelliteOnClickListener);

SetSatellite();

if(Mode == 0)
{
GeoPoint initGeoPoint = myMapView.getMapCenter();
CenterLocation(initGeoPoint);
}
else if(Mode == 1)
{
int intLatitude = bundle.getInt("Latitude");
int intLongitude = bundle.getInt("Longitude");
GeoPoint initGeoPoint = new GeoPoint(intLatitude, intLongitude);
CenterLocation(initGeoPoint);
}

}

private void placeMarker(int markerLatitude, int markerLongitude)
{
Drawable marker=getResources().getDrawable(
android.R.drawable.ic_menu_myplaces);
marker.setBounds(0, 0, marker.getIntrinsicWidth(),
marker.getIntrinsicHeight());
myMapView.getOverlays().add(new InterestingLocations(marker,
markerLatitude, markerLongitude));
}

@Override
protected boolean isRouteDisplayed() {
// TODO Auto-generated method stub
return false;
}

private void CenterLocation(GeoPoint centerGeoPoint)
{
myMapController.animateTo(centerGeoPoint);

myLongitude.setText("Longitude: "+
String.valueOf((float)centerGeoPoint.getLongitudeE6()/1000000));
myLatitude.setText("Latitude: "+
String.valueOf((float)centerGeoPoint.getLatitudeE6()/1000000));
placeMarker(centerGeoPoint.getLatitudeE6(),
centerGeoPoint.getLongitudeE6());
};

private CheckBox.OnClickListener mySatelliteOnClickListener =
new CheckBox.OnClickListener(){

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

class InterestingLocations extends ItemizedOverlay<OverlayItem>{

private List<OverlayItem> locations =
new ArrayList<OverlayItem>();
private Drawable marker;

public InterestingLocations(Drawable defaultMarker,
int LatitudeE6, int LongitudeE6) {
super(defaultMarker);
// TODO Auto-generated constructor stub
this.marker=defaultMarker;
// create locations of interest
GeoPoint myPlace = new GeoPoint(LatitudeE6,LongitudeE6);
locations.add(new OverlayItem(myPlace ,
"My Place", "My Place"));
populate();
}

@Override
protected OverlayItem createItem(int i) {
// TODO Auto-generated method stub
return locations.get(i);
}

@Override
public int size() {
// TODO Auto-generated method stub
return locations.size();
}

@Override
public void draw(Canvas canvas, MapView mapView,
boolean shadow) {
// TODO Auto-generated method stub
super.draw(canvas, mapView, shadow);

boundCenterBottom(marker);
}
}
}


Other files are same as previous exercise.

Download the files.