Wednesday, May 25, 2011

Basic calls for getting the location data from Android

The following is some basic calls to the GPS to retreive either the WIFI or cellular locations. The following code is NOT battery optimized, but serve as the starting point for location retreival

LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
updateLocation(location);  // define your own function here
}

public void onStatusChanged(String provider, int status, Bundle extras) {
Log.v(TAG, "onStatusChanged");
}

public void onProviderEnabled(String provider) {
Log.v(TAG, "onProviderEnabled");
}

public void onProviderDisabled(String provider) {
Log.v(TAG, "onProviderDisabled");
}
};

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

if (!lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
Constant.GPS_REQUEST_TIME, 0, locationListener);
} else {
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
Constant.GPS_REQUEST_TIME, 0, locationListener);
}


The location object will give you access to the longtitude and latitude.

Friday, May 13, 2011

Android - a rounded corner background panel

This example demonstrates how you can create an image by xml declaration. You will use the <shape> xml object and this file will need to be put in the image directories. The following example shows how to build a red oval panel and a white box panel.


drawable-mdpi/white_box.xml - white rounded corner box

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FFFFFF"/>
    <corners android:radius="5px"/>
    <padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
</shape>

drawable-mdpi/red_box.xml - red oval shaped box

<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#FF0000"/>
    <corners android:radius="20px"/>
    <padding android:left="5dp" android:top="0dp" android:right="5dp" android:bottom="0dp" />
</shape>

res/values/styles.xml


We will create a customStyle to use these panels.


<?xml version="1.0" encoding="utf-8"?>
<resources>
  <style name="customStyle">
        <item name="android:textColor">#ffffff</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textSize">12dip</item>
        <item name="android:gravity">center</item>
        <item name="android:background">@drawable/red_box</item>
  </style>
</resources>

Using the white box panel

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent">
<TextView android:id="@+id/tv" android:layout_width="wrap_content" style="@style/customStyle"
android:layout_height="wrap_content" android:layout_margin="50dip" android:text="demo text"/>
</RelativeLayout>

Friday, May 6, 2011

Android - How to add a header and a footer to listView

In the following example we add a previous button in the header, next button in the footer.


Activity Code:

ListView list = getListView();
View headerView = View.inflate(this, R.layout.header, null);
list.addHeaderView(headerView);
View footerView = View.inflate(this, R.layout.footer, null);
list.addFooterView(footerView);


Header XML Layout - header.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="5dip">

<Button android:id="@+id/previous"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />

</RelativeLayout>


Footer XML Layout - footer.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:padding="5dip">

<Button android:id="@+id/next"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />

</RelativeLayout>

Thursday, May 5, 2011

Android - How to Boot a Service When You Turn on the Phone

A typical usage for booting services when a phone starts is a message pull notification system. In the following, I have written a BoardcastReceiver to listen to the system boot event.


First, Create a BroadcastReciever:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class ActivateAtBootReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {

Intent i = new Intent();
i.setAction("com.pof.android.MessageService");
context.startService(i);

}
}

}

In AndroidManifest.xml,

Add the following permission inside the manifest tag:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />


Add the following to inside the application tag:


<service android:name=".MessageService"
android:process=":MessageService"
android:label="Message Notification">
<intent-filter>
<action android:name="com.pof.android.MessageService">
</action>
</intent-filter>
</service>

<receiver android:name=".ActivateAtBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED">
</action>
<category android:name="android.intent.category.HOME">
</category>
</intent-filter>
</receiver>

Note: MessageService is created by me. You will want to replace that with your own service.

Android - How to Hide a Button Dynamically

The following shows how to hide the view image button when a user's profile has no images.

Button viewImagesButton = (Button) findViewById(R.id.view_images_button);
if(images.size() <= 0) {
viewImagesButton.setVisibility(android.view.View.INVISIBLE);
}

Similarily you can show the button by:

viewImagesButton.setVisibility(android.view.View.VISIBLE);

Wednesday, May 4, 2011

Android Java - how to stream UTF-8 bytes using HttpURLConnection

Here's the code I have written for communication between the Android app and the backend server. You may want to encrypt your byte data.

Note that in the following code, I do not depend on the Content-Length to get the data. I use a 4096 byte buffer to read data until there are no data. I used to depend on the Content-Length to retrieve data from the data. However, when I was testing using Nexus S on Android SDK 2.3, the content length was null. After some investigation, I found that the server was sending gzip data and it wipes out the content length variable.


Here's the http connection code:

public static String getHttpWithResponse(String urlStr, byte[] data) {
 
  String responseFromServer = null;
  HttpURLConnection con = null;
 
  try {

   URL url = new URL(urlStr);
   con = (HttpURLConnection) url.openConnection();
   con.setReadTimeout(50000);
   con.setConnectTimeout(20000);

   con.setRequestProperty("Connection", "Keep-Alive");

   con.setInstanceFollowRedirects(true);
   con.setRequestProperty("Content-Type",
     "application/x-www-form-urlencoded");

   con.setRequestMethod("GET");

   con.setDoInput(true);
  
   if (data != null) {

    String lineEnd = "\r\n";
    String twoHyphens = "--";
    String boundary = "*****************************************";

    con.setRequestMethod("POST");
    con.setDoOutput(true);
    con.setRequestProperty("Content-Type",
      "multipart/form-data;boundary=" + boundary);
    con.setRequestProperty("Content-Disposition",
      "multipart/form-data");
    OutputStream o = con.getOutputStream();
    o.write(data, 0, data.length);
    o.close();
   } else {
    con.setInstanceFollowRedirects(true);
    con.setRequestProperty("Content-Type",
      "application/x-www-form-urlencoded");

    con.setRequestMethod("GET");
   }
  
   if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {
    throw new Exception("Response is not empty");
   }
  
   InputStream i = con.getInputStream();
  
   ByteArrayOutputStream bs = new ByteArrayOutputStream();
   byte[] bytesToRead = new byte[4096];

   int actuallyRead = 0;

   while (true) {
    int currentlyRead = i.read(bytesToRead, 0,
      bytesToRead.length);

    if (currentlyRead <= 0)
     break;

    bs.write(bytesToRead, 0, currentlyRead);
    actuallyRead += currentlyRead;

   }

   bytesToRead = null;
   i.close();

   if (actuallyRead < 0) {
    throw new Exception("Nothing Read");
   }

   bytesToRead = bs.toByteArray();

   responseFromServer = new String(bytesToRead, "UTF-8");
  
  } catch (Exception e) {
   responseFromServer = null;
  } finally {
   if (con != null) {
    con.disconnect();
   }
  }

  return responseFromServer;
 }

Tuesday, May 3, 2011

How to dynamically change TextView font color in Android

The following is an easy way to change the font color dynamically in activities.

Activity Code:

String msg = "Hello World!";
TextView tv = (TextView) findViewById(R.id.tv);
txt.setText(Html.fromHtml("<font color='green'>" + msg + "</font>"));

XML layout Code:

<TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content"/>

Monday, May 2, 2011

HttpConnection with AsyncTask and Loading Dialog

One of the common tasks in Android is to make an Http connection to retrieve some information for the app to use. In a lot of areas, internet speed is slow; and we should always assume a slow connection. If we do a synchronous Http connection (calling from the UI thread), we will block user activity if the http response does not return immediately. When users click on anywhere on the screen, there would be no response because the app is waiting for the Http connection to return on the UI thread. Users may assume the app is frozen. In addition, the ANR dialog box may show up.

The solution to this is to sponsor another a background thread to do the Http connection, without blocking the UI thread. We can achieve this by extending the AsyncTask. It also makes sense to put a cancel-able loading dialog box to show that the task is running. We want it to be cancel-able because we do not want to block the UI thread.

The following shows an example for download an image:

private class LoadImageTask extends
AsyncTask<String, Integer, Bitmap> {

private Activity mActivity;
private ProgressDialog mDialog;

public LoadImageTask(CustomActivity activity) {
mActivity = activity;
}

protected void onPreExecute() {
if (mActivity != null && mActivity.getIsActive()) {
mDialog = new ProgressDialog(mActivity);
mDialog.setMessage("Loading...");

mDialog.setCancelable(true);

OnCancelListener ocl = new OnCancelListener() {
public void onCancel(DialogInterface arg0) {
LoadImageTask.this.cancel(true);
}
};

mDialog.setOnCancelListener(ocl);
mDialog.show();
}
}

@Override
protected Bitmap doInBackground(String... m) {

    if(m != null && m.size() > 0) {
Bitmap image = HttpConnect.downloadImage(m[0]);
}
return image;
}

@Override
protected void onProgressUpdate(Integer... progress) {
}

@Override
protected void onPostExecute(Bitmap image) {

if (mActivity != null && mActivity.getIsActive() && mDialog != null) { mDialog.dismiss();
}

   if(im != null && image != null) {
     iv.setImageBitmap(image);  // iv is the ImageView object
   }

}



}

Notes:

CustomActivity extends Activity. It has a private variable called mActive is keep track of if the activity is active.  This is needed to keep track of if the activity is still on. When an AsyncTask returns, it is easy for a user to be ten activities ahead of the activity that started the AsyncTask.  In this case, when the dialog dismisses, you may get "java.lang.IllegalArgumentException: View not attached to window manager".

The following is the implementation of the CustomActivity. To use it, you will want your activities to extend it.


public class CustomActivity extends Activity {

protected boolean mActive = false;

public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
    mActive = true;
}

protected void onPause() {
        super.onPause();
        mActive = false;
    }
  
    public boolean getIsActive() {
     return mActive;
    }

    ...
}


To use the class, you will call the following in your Activity:

  new LoadImageTask(this).execute(YOUR_IMAGE_URL);