Tag Archives: android

Google Cloud Messaging (GCM)

GCM Intro
To begin using GCM, we must have a Google ID. Please refer to the following Getting Started guide link below.

Reference: http://developer.android.com/guide/google/gcm/gs.html

Android App and Registration ID

The Android app will need to initiate a registration intent to the C2DM server with the email address of our Google ID and the app ID to enable C2DM for the device. If successful, the C2DM server will return the app a Registration ID which should be passed to our application server to store to the database and use it for notifications later

We need to configure the following in AndroidManifest.xml to enable the Intents for registration and receiving registrations.

Codes:
<!-- Only this application can receive the messages and registration result -->
<permission android:name="com.yourapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.yourapp.permission.C2D_MESSAGE" />
<!-- This app has permission to register and receive message -->
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<application android:icon=”@drawable/icon” android:label=”@string/app_name” android:debuggable=”true”>
<!– Only C2DM servers can send messages for the app. If permission is not set – any other app can generate it –>
<receiver android:name=”.App$MyGcmReceiver” android:permission=”com.google.android.c2dm.permission.SEND”>
<!– Receive the actual message –>
<intent-filter>
<action android:name=”com.google.android.c2dm.intent.RECEIVE” />
<category android:name=”com.yourapp” />
</intent-filter>
<!– Receive the registration id –>
<intent-filter>
<action android:name=”com.google.android.c2dm.intent.REGISTRATION” />
<category android:name=”com.yourapp” />
</intent-filter>
</receiver>

</application>

The following codes in App.java will register the device for push notifications and receive a unique Registration ID such as:
“APA91bFkFObBt_BO8K4LL4rbf3wS6uTM32GoVaBG2G4GAE2QhF-sRFZGCGFGXYtuTj9fYDWk0Ec6wFgnsS8QyiLzJlZqe-mML2IufVwWka7_sFQbH2zAUhJ7cAVGud38QzUmnidbyATZ”

Codes:
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mc = new MyClass(this, super.appView);
super.appView.addJavascriptInterface(mc, “MyClass”);
Intent registrationIntent = new Intent(“com.google.android.c2dm.intent.REGISTER”);
registrationIntent.putExtra(“app”, PendingIntent.getBroadcast(this, 0, new Intent(), 0)); // boilerplate
registrationIntent.putExtra(“sender”, [value of Project ID]);
String deviceId = Settings.System.getString(getContentResolver(),Settings.System.ANDROID_ID);
mc.setDeviceid(deviceId);
startService(registrationIntent);
}

public static class MyGcmReceiver extends BroadcastReceiver {
private Context context;
@Override
public void onReceive(Context context, Intent intent) {
this.context = context;
if (intent.getAction().equals(“com.google.android.c2dm.intent.REGISTRATION”)) {
handleRegistration(context, intent);
} else if (intent.getAction().equals(“com.google.android.c2dm.intent.RECEIVE”)) {
handleMessage(context, intent);
}
}

private void handleRegistration(Context context, Intent intent) {
String registration = intent.getStringExtra(“registration_id”);
if (intent.getStringExtra(“error”) != null) {
// Registration failed, should try again later.
Log.d(“gcm”, “registration failed”);
String error = intent.getStringExtra(“error”);
if(error == “SERVICE_NOT_AVAILABLE”){
Log.d(“gcm”, “SERVICE_NOT_AVAILABLE”);
}else if(error == “ACCOUNT_MISSING”){
Log.d(“gcm”, “ACCOUNT_MISSING”);
}else if(error == “AUTHENTICATION_FAILED”){
Log.d(“gcm”, “AUTHENTICATION_FAILED”);
}else if(error == “TOO_MANY_REGISTRATIONS”){
Log.d(“gcm”, “TOO_MANY_REGISTRATIONS”);
}else if(error == “INVALID_SENDER”){
Log.d(“gcm”, “INVALID_SENDER”);
}else if(error == “PHONE_REGISTRATION_ERROR”){
Log.d(“gcm”, “PHONE_REGISTRATION_ERROR”);
}
} else if (intent.getStringExtra(“unregistered”) != null) {
// unregistration done, new messages from the authorized sender will be rejected
Log.d(“gcm”, “unregistered”);
} else if (registration != null) {
//Log.d(“gcm”, “registration: “+registration);
mc.setRegistrationid(registration);
}
}

private void handleMessage(Context context, Intent intent)
{
//Do whatever you want with the message
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
int icon = R.drawable.notificationicon; // icon from resources
CharSequence tickerText = “Alert from Your App”; // ticker-text
long when = System.currentTimeMillis(); // notification time
Context context = mContext.getApplicationContext(); // application Context
CharSequence contentTitle = “Your App Name”; // text
CharSequence contentText = intent.getStringExtra(“payload”); // expanded message

Intent notificationIntent = new Intent(context, App.class); // call App class, ie. launch this App
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

// the next two lines initialize the Notification, using the
// configurations above
Notification notification = new Notification(icon, tickerText, when);
notification.defaults |= Notification.DEFAULT_LIGHTS;
notification.ledARGB = 0xff00ff00;
notification.ledOnMS = 300;
notification.ledOffMS = 1000;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notification.setLatestEventInfo(context, contentTitle, contentText, contentIntent);
notificationManager.notify(10001, notification);
}
}

The handleMessage() method will do the necessary to display a notification message on the device and when the notification is clicked, it will launch your app.

The MyClass class holds the Device ID and Registration ID and it is exposed to the HTML5 app using this line of code “super.appView.addJavascriptInterface(mc, “MyClass”)”. In the HTML5 app, we simply call “window.MyClass.getDeviceid()” and “window.MyClass.getRegistrationid()” to get the Device ID and Registration ID. So the HTML5 app will be able to call another web service to our app server with the account username, Device ID and Registration ID to be stored into our system. As the Registration ID might be changed periodically by the GCM server, we need to keep updating the Registration ID tied to each Device ID.

Reference: http://developer.android.com/guide/google/gcm/gs.html
Reference: http://developer.android.com/guide/google/gcm/gcm.html

Sending a Message from the App Server

Follow this GCM Demo guide from Google – http://developer.android.com/guide/google/gcm/demo.html and integrate the codes into your Java app server.

 

Tagged , ,

Android WebView Threads

Threads in the Android WebView continue running even though the app is switched out to another app. My test PhoneGap app will take 4% to 5% of the CPU when active and it will drain battery power if this 4%-5% CPU is running in the background all the time. Therefore it is vital to pause the threads if the app is put in the background which will result in the CPU % to be 0% in the Android Task Manager.

In your App.java class, add the following codes:
public class App extends DroidGap {

...

@Override
protected void onPause() {
super.onPause();
super.appView.pauseTimers();
}

@Override
protected void onResume() {
super.onResume();
super.appView.resumeTimers();
}

...

This will allow the webview (named as appView) to pause and resume its threads correctly.

References:
http://stackoverflow.com/questions/2040963/webview-threads-never-stop-webviewcorethread-cookiesyncmanager-http0-3
http://stackoverflow.com/questions/3431351/how-do-i-pause-flash-content-in-an-android-webview-when-my-activity-isnt-visibl

Tagged , ,

File Upload in WebView (Android)

The Android WebView by default doesn’t open file chooser for a HTML form input of type ‘file’. However, it is possible to make this work by overriding the hidden method openFileChooser, which needs to be overridden to pop up a file chooser and then return the result to WebView.

In com.phonegap.DroidGap class, add the openFileChooser method to GapClient:

Codes:
public class GapClient extends WebChromeClient {

...
// For Android < 3.0
public void openFileChooser(ValueCallback uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
DroidGap.this.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}

// For Android 3.0+
public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("*/*");
DroidGap.this.startActivityForResult(
Intent.createChooser(i, "File Browser"),
FILECHOOSER_RESULTCODE);
}

...

Add the following code to onActivityResult():
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = intent == null || resultCode != RESULT_OK ? null
: intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else {
super.onActivityResult(requestCode, resultCode, intent);
IPlugin callback = this.activityResultCallback;
if (callback != null) {
callback.onActivityResult(requestCode, resultCode, intent);
}
}
}

The webview will be able to pop up a file chooser and then return the chosen file.

Reference: http://stackoverflow.com/questions/5907369/file-upload-in-webview

Tagged , , ,

URL Redirection within the same WebView (Android)

The default behaviour of a PhoneGap when a user taps on a web link is to open the default browser app and run the web link. However, it might make better sense for the web link to open in the same PhoneGap webview instance if the web link was intended to load a remote HTML5 app that should run in the PhoneGap enviroment on the device.

In com.phonegap.DroidGap class, add in the following lines of code to process URLs starting with “androidurl://”. The problem was that “http://&#8221; URLs somehow do not trigger the “shouldOverrideUrlLoading()” event, so the workaround was to use “androidurl://” within the HTML5 app to trigger the event on the native app and then revert the URL to “http://&#8221; and process it accordingly.

Codes:
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {

...

// All else
else {
// IMPT! Fix for shouldOverrideUrlLoading() to be triggered from window.location javascript
if (url.startsWith("androidurl://")) {
url = url.replaceAll("androidurl://", "http://");
}

...
}

Tagged , ,

Getting Started with PhoneGap (Android)

The main steps are already detailed on the official PhoneGap site – http://phonegap.com/start.

Below are my tried and tested steps for your reference to get the Android environment up and a test App running on a Windows 7 machine, then you can add in the steps to create a PhoneGap app from the official guide above.

Getting Started with Running an Android App in Eclipse

1) Download and extract Eclipse Classic 3.7.1, 174 MB from http://www.eclipse.org/downloads.

2) Download the Android SDK installer (installer_r16-windows.exe) at http://developer.android.com/sdk/index.html. Make sure that you change the installation directory to “c:\android-sdk” for example, it must be a PATH with NO spaces! When the installation is done, run SDK Manager and it will download and install some files which will take some time, make sure that Android SDK Tools/Platforms are installed.

3) In SDK Manager window, go to Virtual Devices and add a new device called Android2.3.3 and select Target to be “Android 2.3.3 – API Level 10”, click on “Create AVD”. (You can add more devices with different versions as you please)

4) Run Eclipse and start to install the Android Development Tools (ADT) plugin for use with Eclipse, the repository url is “https://dl-ssl.google.com/android/eclipse/&#8221;. Follow the steps here to configure the ADT: http://developer.android.com/sdk/eclipse-adt.html#installing.

5) Start a new Android Project by clicking File > New > Android Project.

  • In the New Android Project dialog, select Create project from existing source.
  • Click Browse and navigate to where the SDK is installed and select Spinner, e.g. C:\android-sdk\samples\android-13\Spinner
  • Android 1.5 will be selected as the Build Target and click on Finish.
  • Go to Project -> Clean to build the project.

6) Right-click on SpinnerActivity project and Run As -> Android Application. It will run with the Android 2.3.3 AVD created before.
7) The Android emulator will run and note that it takes a while to load.

Deploy and Run on An Actual Device

1) Depending on the model of your phone, e.g. Samsung or HTC etc, you will need to download the driver for the phone here: http://developer.android.com/sdk/oem-usb.html. In my case, I use the Samsung Galaxy S and I have to install the Samsung Kies software.

2) Follow the instructions to configure your Android phone for Development at: http://developer.android.com/guide/developing/device.html.

3) Then connect your phone to test if it can be detected by the Android SDK using a command prompt and go to “C:\android-sdk\platform-tools” and run “adb devices” to see a list of devices connected.

The output from the command will be something like this:
List of devices attached
35311BC91FD700EC device

4) In Eclipse, right-click on SpinnerActivity project and Run As -> Run Configurations.

5) Click on the Target tab and change the Deployment Target Selection Mode from Auto to Manual and click on Run. This will allow you to choose the device instead of the AVD.

6) Select the device and click OK.

7) The app will be running on your Android phone.

Adding Another Device

1) Follow the same steps as above and depending on your model of your phone, download the correct USB drivers.

Tagged , , ,