Category Archives: Android

Get PhoneGap to Work on Mac OS X

PhoneGap is a free open source framework that allows us to create mobile apps using standard web technologies such as HTML, CSS, and JavaScript (http://phonegap.com).

If you follow the steps in the web page, Install PhoneGap (http://phonegap.com/install/), most likely you would get stuck in the second or third step. This is because the documentation is horrible and gives us a lot of confusions.

Earlier versions of PhoneGap were tightly integrated into Eclipse and Xcode. But it is obvious that PhoneGap stops catching up new updates of various platform. It seems that they went back to command line tools with poor quality documentations.

It makes me think that the only reason why Adobe purchased this company was just to kill this product.

Anyway, let us get started. To install 3.3 version of PhoneGap, Node.JS is required.

1. Install Node.JS

Go to http://nodejs.org and find the INSTALL button. Click it to install. This step is very straightforward and easy thanks to the node.js install program.

2. Install PhoneGap

Open “Terminal” and run the following.

$ sudo npm install -g phonegap

phonegap is a command line tool. You will be able to use “phone gap” command to create a project. But you will NOT be able to build or run the app in any platform.

3. Create a Project

$ phonegap create my-app
$ cd my-app

This will create a phone gap project but it doesn’t include any platform dependent modules.

4. Install Cordova

There are buzzes regarding to PhoneGap, Cordova, and their relationship. Here is a clarification from PhoneGap Blog. http://phonegap.com/2012/03/19/phonegap-cordova-and-what’s-in-a-name/

To make the long story short,

PhoneGap is a distribution of Apache Cordova. You can think of Apache Cordova as the engine that powers PhoneGap, similar to how WebKit is the engine that powers Chrome or Safari.

And,

If your goal is to build cross platform apps with HTML, JS and CSS then keep on using PhoneGap for everything you need. This isn’t to say the transition has been perfect. We messed up the last release by not correctly updating the documentation to reflect the name change which confused new users.

The conclusion is that we NEED cordova installation along with PhoneGap. Very weird.  Right? Anyway use the following command.

$ sudo npm install -g cordova

5. Add Platforms to a Project

The folder “platforms” inside “my-app” (the newly created project folder) has nothing from your creation. We have to add platforms (such as android and iOS). This is the moment where we have to use cordova. Let’s add the iOS platform that means cordova creates all necessary files to be a proper project for Xcode.

$ cordova platform add ios

Next let’s add the android platform.

$ cordova platform add android

You will see an error saying that ‘ant’ is not installed if you have installed it already in your system. Also I assume that you do not have MacPorts as well just like me. Let’s use “homebrew” to install “ant.”

$ ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go/install)"

After homebrew is successfully installed, use followings.

$ brew install ant

Now you are suppose to be able to install “android” platform. Try this again.

$ cordova platform add android

Now you can find two folders (ios and android) in the “platforms” folder.

6. Build Projects

Finally you are ready to build projects.

$ phonegap run android

This will compile, build, and install the project onto the device. If no Android device is connected to the system, it will start the Android emulator.

In  the same manner, use following to build and run the project for iOS.

$ phonegap run ios

You will see an error that says ios-sim was not found even though you have installed Xcode and by which your system has iOS Simulator already.

$ sudo npm install -g ios-sim

If you prefer using IDEs, you may import the android project from ADT and start the Xcode project file on Xcode.

OpenCV for Android

Why another tutorial for OpenCV for Android? I followed a tutorial from the OpenCV official website. It was disappointing. It turned out there are many problems during installation and building examples in my Mac OS X system.

My Android SDK version is 4.2.2 (API 17) with Android Development Tools (ADT) for its IDE. I believe this tutorial works as well with Android 4.3 (API 18) in Mac OS X.

Here is a list of steps to follow to get OpenCV for Android ready for you.

Download OpenCV for Android

  1. Download OpenCV for Android from http://opencv.org/downloads.html. You can find an Android version from the web. As I am writing this, the latest version is 2.4.6.
  2. Unzip the zip file and you will see a folder.
  3. Place the folder into a proper folder. In my case, I created a folder in my Documents and named it as work. And copy the OpenCV folder into the new folder.
  4. This is it for now until we build the libraries and samples.

Download Android NDK

You may find an NDK Plugins item from the menu Help – Install New Software. I tried this. But it shows the following error message.

Cannot complete the request. See the error log for details.
“Android Native Development Tools” will be ignored because it is already installed.

So I went to the Android NDK webpage and found a package. It seems we need to install this manually.

  1. Download a package from http://developer.android.com/tools/sdk/ndk/index.html. You can find two packages for each platform. Just select the first package which is a current and recommended toolchain for the NDK.
  2. Unzip it.
  3. Find a folder named “android-ndk-<version>.”
  4. Place this folder into the “work” folder. See the ‘Download OpenCV for Android’ section for more detail about the “work” folder.

Build OpenCV Library and Samples

Now we are kind of ready to build OpenCV Samples because we have to take a few more steps after importing Android projects from the OpenCV folder to your ADT’s workspace.

There is an NDK item in the Preferences of ADT under the Android section. It looks like we have to put NDK folder into the blank. But this is useless at least for the OpenCV samples. So you may ignore this NDK item in the Preferences.

  1. Select File – Import – Android – Existing Android Code into Workspace.
  2. You will see the ‘Import Project’ dialog.
  3. Choose ‘Root Directory’ by clicking the ‘Browse…’ button. The directory is the top folder of the OpenCV folder that you placed in the section ‘Download OpenCV for Android.’
  4. Now you can see five samples and three tutorials and one library. Select all the projects. If you want to use only one sample or tutorial, please don’t forget to select the project “sdk/java – OpenCV Library.” Then Click the ‘Finish’ button.
  5. If you want to maintain all original samples in the folder, you can check the “Copy projects into workspace” checkbox. This will copy all source code into your workspace.
  6. Some projects will show compile errors. According to OpenCV website tutorials, the errors will be shown only on a non-Windows operating systems. Anyway, I am using a Mac. So I see the errors. The remedy in the web http://docs.opencv.org/doc/tutorials/introduction/android_binary_package/O4A_SDK.html didn’t work for me. Here is what I did.
  7. First of all, NDKROOT is not defined in my system. Add NDKROOT with the value of the actual android-ndk folder that you placed in the section ‘Download Android NDK.’ This should be done all the complaining projects with errors.
    1. Go to Properties (@I) of each project. Note that this is NOT ‘Preferences.’ Select one project and right click. Find the Properties item in the bottom of the context menu.
    2. Find C/C++ Build – Environment.
    3. Add a variable. Name it NDKROOT and put a value with your android-ndk folder.
  8. Then go back the C/C++ Build item in the Properties. You can find ‘Build command:” Its corresponding text input box has either ${NDKROOT}/ndk-build.cmd or “${NDKROOT}/ndk-build.cmd” (Note that the quotation marks!). Remove ‘.cmd’ and the quotation marks if it has. I just want ${NDKROOT}/ndk-build in the text input box.

OpenCV Manager

If your Android phone does not have OpenCV Manager, you should download it from https://play.google.com/store/apps/details?id=org.opencv.engine&hl=en.

That’s it. Enjoy OpenCV for Android!!!

References:

Problems in updating Android SDK Tools with rev. 22

You may find an error after updating your Android SDK with Rev. 22. When you restart the ADT, you will see this error.

This Android SDK requires Android Developer Toolkit version 22.0.0 or above.
Current version is 21.x.x.
Please update ADT to the latest version.

The problem is you will not be able to update it from the “Check Updates” button. Clicking the button leads you to the “No updates were found” window along with the “Problem Occurred” window. If you ignore the problem, this error prevents us from running any of your existing projects.

This is because of the wrong URL of the Android Developer Tools Update Site. You cannot fix this problem by changing the Install/Update – Available Software Sites – Android Developer Tools Update Site since it adds automatically “content.xml” at the end of the URL.

Here is a list of steps that you need to take.

  1. Help – Install New Software in the ADT menu.
  2. Type https://dl-ssl.google.com/android/eclipse/site.xml in “Work with:” and Enter.
  3. You can see the “Developer Tools” item.
  4. Select it and click Next.
  5. Click Next one more.
  6. Click Finish accepting the terms of the license agreements.
  7. Click OK in the “Security Warning” window.
  8. Let the installer restart ADT after installing the tools.

That’s it.

GoogleMaps Demo

Here is a simple example of Android Maps v2 using MapFragment. We are assuming that the Google Play services SDK is installed. If you don’t have the SDK, please install before this. See details at here.

New Project

  • File > New > Android Application Project
    • Application Name: MapsDemo
    • Package Name: edu.kettering.mapsdemo
    • Minimum Required SDK: API 11: Android 3.0
      • Note: Fragment was introduced in API 11
    • Compile With: Google APIs (Google Inc.) (API 17)
      • If you have not installed Google APIs, Install it using the Android SKD Manager. Google APIs are the superset of Android APIs. This means “Google API level 17″ means “Android 4.2.2 (level 17) + Google APIs.”
  • Use default values for all the options.

Adding Library

Add Google Play services library.

  • Project > Properties.
  • Select Android in the left. Find the Library pane. Click “Add…” and select “google-play-services_lib”

Adding MapFragment

  • Open “activity_main.xml” and delete the text view.
  • Drag the “Fragment” in the “Layout” Palette and drop on the main layout.
    • “Choose Fragment Class” will be prompted.
    • Find “MapFragment.”
  • Align the fragment by adjusting the size. The root layout is RelativeLayout. So drag the border lines and drop at the end of the window left, top, right, and bottom.
  • Change the id to “@+id/map”
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <fragment
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.MapFragment"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

Using GoogleMap class

We can get a GoogleMap object using FragmentManager. A MapFragment can be acquired from findFragmentById. The map can get from getMap().

package edu.kettering.mapsdemo;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;

public class MainActivity extends Activity {
	static final LatLng ketteringLatLng = new LatLng(43.013651,-83.713498);
	private GoogleMap map;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		map = ((MapFragment)getFragmentManager().findFragmentById(R.id.map)).getMap();
		map.addMarker(new MarkerOptions().position(ketteringLatLng).title("Kettering"));
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(ketteringLatLng, 15));
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

}

Getting an API Key

Get an API Key with the package name, “edu.kettering.mapsdemo” and the SHA-1 fingerprint. See this article for more detail.

Editing AndroidManifest.xml

We need to add permission, uses-permission, use-feature, and meta-data. “edu.kettering.mapsdemo”s in red should be replaced with your app’s package name.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="edu.kettering.mapsdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

    <!-- Start Maps -->
    <permission
        android:name="edu.kettering.mapsdemo.permission.MAPS_RECEIVE"
        android:protectionLevel="signature"/>
    <uses-permission android:name="edu.kettering.mapsdemo.permission.MAPS_RECEIVE" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>
    <!-- End of Maps -->

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <!-- Start Maps -->
        <meta-data
      		android:name="com.google.android.maps.v2.API_KEY"
      		android:value="YOUR_OWN_KEY"/>
        <!-- End of Maps -->

        <activity
            android:name="edu.kettering.mapsdemo.MainActivity"
            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>

</manifest>

Getting Ready to Use Google Maps Android API v2

Why do we need another tutorial for Google Maps Android API v2? I have discovered little things are missed or inaccurate in tutorials here and there (even in Google’s developer guideline). I do not want to repeat every single detail that you can find other places. This can be a supplementary material for Google’s developer guide for Google Maps Android API v2.

We know what the Google Maps is already. Also the 2nd version was introduced many months ago. Google Maps API in Android was also improved and named v2. Many things have  changed since last year.

Let’s get started. I am assuming we are using Eclipse as an IDE.

Install Google Play services SDK

To use Google Maps in Android we should have Google Play services. I don’t know why Google uses the name “Play” for Maps services. Anyway…

  • Start the Android SDK Manager.
  • Find Google Play services in the Extras.
  • Install the package by clicking “Install …” button.

Get the library for Google Play services

After the package is installed. We have to make the library usable for other projects.

  • Import the library so that my project can use it.
    • Go to File > Import > Android > Existing Android Code … and click Next.
    • Browse and find “<your android sdk folder>/extras/google/google_play_services/libproject/google-play-services_lib” and click Finish.
  • We just installed Google Play services library.

Make the sample app, maps work.

There are few samples in the Google Play services SDK. Let us make “maps” work. We could use this as a working template that can be utilized for future projects.

  • Import the sample.
    • Go to File > Import > Android > Existing Android Code … and click Next.
    • Browse and find “<your android sdk folder>/extras/google/google_play_services/samples/maps”.
    • The default new project name of the sample is “MainActivity.” This is not a smart choice at all. Let us change it to something more meaningful such as “MapsSample” before you click the Finish button.
    • Also make sure that you check “Copy projects into workspace” so that the original code is intact.
  • Well.. you can see tons of errors. Look hopeless. But do not worry. We will fix them shortly.
    • Go to Project > Properties.
    • Find Android in the left. You will see the Library pane in your right. If you see the ‘X’ mark in red, select it and click Remove.
    • Click Add… and select the “google-play-services_lib” item. Click OK. Now you can see the check mark in green.
  • Most errors should be gone now. RetainMapActivity has some errors that are from using “FragmentActivity.”
    • Fragment was introduced in Android 3.0 (API level 11). ADT (Build: v21.1.0-569685) makes templates using minSDK 8. The example was written using Support Lib anyway. Let us use the support library.
  • Add a support lib v4 into the project.
    • Make the “libs” folder inside the project folder. If you followed my recommendation in naming the project, the folder name is “MapsSample” and the “libs” folder must be inside the folder.
    • Find “android-support-v4.jar” at “<your android sdk folder>/extras/android/support/v4/” and copy it into the “libs” folder.
    • Go to Project > Properties.
    • Find “Java Build Path” in the left. Select the “Libraries” tab and click “Add External JARs…” Find the “android-support-v4.jar” file. Note: Do not select the jar file inside the Android SDK folder.
    • This must remove all remaining errors.
  • Run the app. You may see the program crashes.
    • Go to Project > Properties.
    • Find “Java Build Path” in the left. Select the “Libraries” tab.  Find the “android-support-v4.jar” file and remove it. I know, I know.. This is a strange step. But it should fix the crash.
  • If you still see errors from GoogleMap related packages and classes, just choose “Fix project setup..” from the Quick Fix suggestion and select “Add archive ‘google-play-services.jar – ….” and click OK. This will add “google-play-services.jar” into the build path.

Emulator

As of writing this Android emulator does not support Google Play services. We should use an actual Android device.

API Key

An API Key is required to use Google Play services.  To get a key we need an SHA-1 fingerprint and the application registration with the Google Maps Android API v2.

SHA-1 fingerprint

  • Locate debug.keysotre file in the “.android” in the current user folder.
    • /Users/<user name>/.android/ or ~/.android/
  • List the SJA-1 fingerprint.
    • keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android
  • Find the “SHA1:” line in the output.
    • Alias name: androiddebugkeyCreation date: Apr 4, 2013
      Entry type: PrivateKeyEntry
      Certificate chain length: 1
      Certificate[1]:
      Owner: CN=Android Debug, O=Android, C=US
      Issuer: CN=Android Debug, O=Android, C=US
      Serial number: 515de791
      Valid from: Thu Apr 04 16:50:25 EDT 2013 until: Sat Mar 28 16:50:25 EDT 2043
      Certificate fingerprints:
       MD5: 11:22:33:44:A8:0E:99:00:33:94:44:33:55:7E:66:FD
       SHA1: FF:CC:BB:16:99:E3:AA:FF:55:44:42:77:AE:55:46:99:43:55:88:C2
       Signature algorithm name: SHA1withRSA
       Version: 3
    • Copy the hexa-decimal string after “SHA1:” (Note that the string above is not actual fingerprint)

Registration

  • Go to https://code.google.com/apis/console/
  • Select “Services” and find “Google Maps Android API v2″ in the middle of the list.
    • Turn the switch on.
  • Select API Access.
    • Click “Create new Android key…”
    • Paste the copied SHA-1 fingerprint and put ‘;’ followed by the package name (com.example.mapdemo).
  • Find “API key:” in the “Key for Android apps (with certificates)”

Put the Key into AndroidManifest.xml

Locate AndroidManifest.xml and open it. Find “YOUR_OWN_KEY” and replace it with the API key.

<application
  android:icon="@drawable/ic_launcher"
  android:label="@string/demo_title"
  android:hardwareAccelerated="true">
  <meta-data
    android:name="com.google.android.maps.v2.API_KEY"
    android:value="YOUR_OWN_KEY"/>

Connect your Android device and execute the sample. You will see “Google Maps API Demos” app.

Broadcast Receiver Demo

In this demo a broadcast receiver will be implemented to show how to receive a broadcast.

Here is the action list from API 17. You can find the text file inside your ADT directory (/sdk/platforms/android-<version>/data/broadcast_actions.txt).


android.app.action.ACTION_PASSWORD_CHANGED
android.app.action.ACTION_PASSWORD_EXPIRING
android.app.action.ACTION_PASSWORD_FAILED
android.app.action.ACTION_PASSWORD_SUCCEEDED
android.app.action.DEVICE_ADMIN_DISABLED
android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED
android.app.action.DEVICE_ADMIN_ENABLED
android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED
android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED
android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED
android.bluetooth.adapter.action.DISCOVERY_FINISHED
android.bluetooth.adapter.action.DISCOVERY_STARTED
android.bluetooth.adapter.action.LOCAL_NAME_CHANGED
android.bluetooth.adapter.action.SCAN_MODE_CHANGED
android.bluetooth.adapter.action.STATE_CHANGED
android.bluetooth.device.action.ACL_CONNECTED
android.bluetooth.device.action.ACL_DISCONNECTED
android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED
android.bluetooth.device.action.BOND_STATE_CHANGED
android.bluetooth.device.action.CLASS_CHANGED
android.bluetooth.device.action.FOUND
android.bluetooth.device.action.NAME_CHANGED
android.bluetooth.device.action.UUID
android.bluetooth.devicepicker.action.DEVICE_SELECTED
android.bluetooth.devicepicker.action.LAUNCH
android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT
android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED
android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED
android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED
android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED
android.hardware.action.NEW_PICTURE
android.hardware.action.NEW_VIDEO
android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS
android.intent.action.ACTION_POWER_CONNECTED
android.intent.action.ACTION_POWER_DISCONNECTED
android.intent.action.ACTION_SHUTDOWN
android.intent.action.AIRPLANE_MODE
android.intent.action.BATTERY_CHANGED
android.intent.action.BATTERY_LOW
android.intent.action.BATTERY_OKAY
android.intent.action.BOOT_COMPLETED
android.intent.action.CAMERA_BUTTON
android.intent.action.CONFIGURATION_CHANGED
android.intent.action.DATE_CHANGED
android.intent.action.DEVICE_STORAGE_LOW
android.intent.action.DEVICE_STORAGE_OK
android.intent.action.DOCK_EVENT
android.intent.action.DREAMING_STARTED
android.intent.action.DREAMING_STOPPED
android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE
android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE
android.intent.action.FETCH_VOICEMAIL
android.intent.action.GTALK_CONNECTED
android.intent.action.GTALK_DISCONNECTED
android.intent.action.HEADSET_PLUG
android.intent.action.INPUT_METHOD_CHANGED
android.intent.action.LOCALE_CHANGED
android.intent.action.MANAGE_PACKAGE_STORAGE
android.intent.action.MEDIA_BAD_REMOVAL
android.intent.action.MEDIA_BUTTON
android.intent.action.MEDIA_CHECKING
android.intent.action.MEDIA_EJECT
android.intent.action.MEDIA_MOUNTED
android.intent.action.MEDIA_NOFS
android.intent.action.MEDIA_REMOVED
android.intent.action.MEDIA_SCANNER_FINISHED
android.intent.action.MEDIA_SCANNER_SCAN_FILE
android.intent.action.MEDIA_SCANNER_STARTED
android.intent.action.MEDIA_SHARED
android.intent.action.MEDIA_UNMOUNTABLE
android.intent.action.MEDIA_UNMOUNTED
android.intent.action.MY_PACKAGE_REPLACED
android.intent.action.NEW_OUTGOING_CALL
android.intent.action.NEW_VOICEMAIL
android.intent.action.PACKAGE_ADDED
android.intent.action.PACKAGE_CHANGED
android.intent.action.PACKAGE_DATA_CLEARED
android.intent.action.PACKAGE_FIRST_LAUNCH
android.intent.action.PACKAGE_FULLY_REMOVED
android.intent.action.PACKAGE_INSTALL
android.intent.action.PACKAGE_NEEDS_VERIFICATION
android.intent.action.PACKAGE_REMOVED
android.intent.action.PACKAGE_REPLACED
android.intent.action.PACKAGE_RESTARTED
android.intent.action.PACKAGE_VERIFIED
android.intent.action.PHONE_STATE
android.intent.action.PROVIDER_CHANGED
android.intent.action.PROXY_CHANGE
android.intent.action.REBOOT
android.intent.action.SCREEN_OFF
android.intent.action.SCREEN_ON
android.intent.action.TIMEZONE_CHANGED
android.intent.action.TIME_SET
android.intent.action.TIME_TICK
android.intent.action.UID_REMOVED
android.intent.action.USER_PRESENT
android.intent.action.WALLPAPER_CHANGED
android.media.ACTION_SCO_AUDIO_STATE_UPDATED
android.media.AUDIO_BECOMING_NOISY
android.media.RINGER_MODE_CHANGED
android.media.SCO_AUDIO_STATE_CHANGED
android.media.VIBRATE_SETTING_CHANGED
android.media.action.CLOSE_AUDIO_EFFECT_CONTROL_SESSION
android.media.action.OPEN_AUDIO_EFFECT_CONTROL_SESSION
android.net.conn.BACKGROUND_DATA_SETTING_CHANGED
android.net.nsd.STATE_CHANGED
android.net.wifi.NETWORK_IDS_CHANGED
android.net.wifi.RSSI_CHANGED
android.net.wifi.SCAN_RESULTS
android.net.wifi.STATE_CHANGE
android.net.wifi.WIFI_STATE_CHANGED
android.net.wifi.p2p.CONNECTION_STATE_CHANGE
android.net.wifi.p2p.DISCOVERY_STATE_CHANGE
android.net.wifi.p2p.PEERS_CHANGED
android.net.wifi.p2p.STATE_CHANGED
android.net.wifi.p2p.THIS_DEVICE_CHANGED
android.net.wifi.supplicant.CONNECTION_CHANGE
android.net.wifi.supplicant.STATE_CHANGE
android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED
android.speech.tts.engine.TTS_DATA_INSTALLED

android.intent.action.PHONE_STATE will be used in this demo.

First we need to add READ_PHONE_STATE permission. Open “AndroidManifest.xml” and add the permission.


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

Add a receiver tag with a proper name in the “AndroidManifest.xml” file.

        <receiver android:name="PhoneEventReceiver">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE">
                    
                </action>
            </intent-filter>
        </receiver>

Create a new class named “PhoneEventReceiver” inheriting the “BroadcastReceiver” class.

package edu.kettering.broadcastreceiverdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class PhoneEventReceiver extends BroadcastReceiver {
	private static final String mDebugTag = "MY_DEBUG" ;
	@Override
	public void onReceive(Context context, Intent intent) {
		// TODO Auto-generated method stub
		Bundle extras = intent.getExtras();
		if(extras == null) 
			return;
		String state = extras.getString(TelephonyManager.EXTRA_STATE);
		Log.d(mDebugTag, state);
		
		if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
			String phoneNumber = extras
					.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
			Log.d(mDebugTag, phoneNumber);
		}
	}
}

PopupMenu Demo

The PopupMenu is anchored to a View. If a PopupMenu is shown in response to a click event to a button, the Popup will be displayed under the button if there is room.
The PopupMenu needs at least SDK 11. You need to open “AndroidManifest.xml” and change the minimum SDK version (“8″) to “11” since the ADT makes an app template with “8” for the minSdkVersion.

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

Just like any other Android API Guides, the section of the PopupMenu (http://developer.android.com/guide/topics/ui/menus.html#PopupMenu) is good for just lying-down-readers for fun but not good enough for someones who really need to use it.

Here I would like to provide a complete tutorial. But notice that this is not something that you can copy and paste it to build. You have to understand what you are doing.

First let us add a button to show a Popup menu. Please notice that the android:onClick attribute has the “showPopup” method name.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".PopupMenuDemoActivity" >

    <Button
        android:id="@+id/buttonShowMePopup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:onClick="showPopup"
        android:text="Show Me Popup" />

</RelativeLayout>

It is always good idea to use resources to maintain strings.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">PopupMenuDemo</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="archive">Archive</string>
    <string name="delete">Delete</string>
    <string name="popup_menu_archive_selected">Archive is selected.</string>
    <string name="popup_menu_delete_selected">Delete is selected.</string>
</resources>

A new menu XML file must be added. Add a new Android XML with “Menu” Resource Type. Use “actions.xml” as its name. Please insert two items for this demo.

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/archive" android:title="@string/archive"></item>
    <item android:id="@+id/delete" android:title="@string/delete"></item>
</menu>

Then we are pretty much ready to write code. For the sake of simplicity I decided to use the main activity (named PopupMenuDemoActivity) as a target of the “PopupMenu.OnMenuItemClickListener” interface. This means the activity should implement the interface. When the user selects an item, Android system will call the onMenuItemClick() callback.

package edu.kettering.popupmenudemo;

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

public class PopupMenuDemoActivity extends Activity 
		implements PopupMenu.OnMenuItemClickListener {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_popup_menu_demo);
	}

	public void showPopup(View v)
	{
		PopupMenu popupMenu = new PopupMenu(this, v);
		popupMenu.getMenuInflater().inflate(R.menu.actions, popupMenu.getMenu());
		popupMenu.setOnMenuItemClickListener(this);
		popupMenu.show();
	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.popup_menu_demo, menu);
		return true;
	}

	@Override
	public boolean onMenuItemClick(MenuItem item) {
		String msg;
		switch(item.getItemId()) {
		case R.id.archive:
			msg = getResources().getString(R.string.popup_menu_archive_selected);
			Toast.makeText(getApplicationContext(), 
                   msg, Toast.LENGTH_LONG).show(); 
			return true;
		case R.id.delete:
			msg = getResources().getString(R.string.popup_menu_delete_selected);
			Toast.makeText(getApplicationContext(), 
                    msg, Toast.LENGTH_LONG).show(); 
			return true;
		}
		return false;
	}

}

 

ProgressBar Demo

According to Android API Guides, we should avoid ProgressDialog. Instead, using ProgressBar in the layout is recommended.

Her is my layout xml. You may use other than RelativeLayout if you do not like it.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:paddingBottom="@dimen/activity_vertical_margin"
	android:paddingLeft="@dimen/activity_horizontal_margin"
	android:paddingRight="@dimen/activity_horizontal_margin"
	android:paddingTop="@dimen/activity_vertical_margin"
	tools:context=".ProgressBarDemo2Activity" >

<ProgressBar
	android:id="@+id/progressBar"
	style="?android:attr/progressBarStyleHorizontal"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_alignParentLeft="true"
	android:layout_alignParentRight="true"
	android:layout_alignParentTop="true" />
</RelativeLayout>

After putting a ProgressBar into my layout, I create a Runnable object and a Thread. Call the thread with the runnable object that has my own overridden run() method.

package edu.kettering.progressbardemo2;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.ProgressBar;

public class ProgressBarDemo2Activity extends Activity {
	private ProgressBar mProgressBar;
	private int mProgress = 0;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_progress_bar_demo2);

		mProgressBar = (ProgressBar)findViewById(R.id.progressBar);

		Runnable runWork = new Runnable() {
			@Override
			public void run() {
				while(mProgress < 100) {
					mProgress = doWork();
					mProgressBar.setProgress(mProgress);
				}
			}
		};
		Thread thread = new Thread(runWork);
		thread.start();
	}

	private int doWork() {
		// sleep 50 millisecond
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		mProgress++;

		return mProgress;
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.progress_bar_demo2, menu);
		return true;
	}
}

ProgressDialog Demo

This demo shows how to use ProgressDialog in the horizontal style. You can add your own task while the progress bar is displaying. This will also show how to update the progress bar as you are doing your task. For the sake of simplicity of source code I do not create onClickListener. Instead I use the “andoroid.onClick” property of a button.

In the layout xml, add a button and change onClick property with “startProgress” that we will be implementing in a moment.

<Button
    android:id="@+id/btnStartProgress"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="startProgress"
    android:text="@string/start_progress" />

Here is the full source code.

package edu.kettering.progressbardemo;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.view.View;

public class ProgressBarDemo extends Activity {
	final static int maxValue = 500;
	private ProgressDialog progressBar;
	private int progress;
	private int progressSomthing;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_progress_bar_demo);
	}

	public void startProgress(View v) {
		progressBar = new ProgressDialog(v.getContext());
		progressBar.setCancelable(true);
		progressBar.setMessage("Wait...");
		progressBar.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progressBar.setProgress(0);
		progressBar.setMax(maxValue);
		progressBar.show();

		progress = 0;
		progressSomthing = 0;

		// make a runnable to be used in the thread
		Runnable runProgress = new Runnable() {
			public void run() {
				while (progress < maxValue) {

					progress = doSomething();

					progressBar.setProgress(progress);
				}

				if (progress >= maxValue) {
					progressBar.dismiss();
				}
			}
		};

		Thread thread = new Thread(runProgress);
		thread.start();
	}

	// this should return a progress value
	public int doSomething() {
		// sleep 50 millisecond
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		progressSomthing++;

		return progressSomthing;
 	}
 }

DatePickerDialog Demo

This demo shows you how to use DatePickerDialog. You will see a warning “showDialog is deprecated” when you use “showDialog” method. First of all you could use, as of today, “showDialog” even though the message warns you. This link (http://www.technotalkative.com/android-datepickerdialog-example/) is an example that you can still use. However we do not know when it will stop working anyway since the method had been deprecated anyway.

Previously we used to use

  • showDialog(int id);
  • onCreateDialog(int id);

“showDialog” will be called in a handler of a button or something. And we need to prepare the onCreateDialog callback something like below.

@Override
protected Dialog onCreateDialog(int id) {
    switch (id) {
    case DATE_DIALOG_ID:
        return new DatePickerDialog(this, mDateSetListener, mYear, mMonth, mDay);
    }
    return null;
}

Android’s API Guides say “you should use a DialogFragment as a container for your dialog.” I do not want to create a DialogFragment to use DatePickerDialog. Many recently tutorials explain how to use DialogFragment to use even simple dialogs. Here is a good news. We could use DatePickerDialog without knowing DialogFragment.

The idea of the new approach is that we create an instance of the “DatePickerDialog” class and set properties before we show the dialog by the “show” method. I think that this is a more integrated way to manage dialogs.

mDatePickerDialog = new DatePickerDialog(v.getContext(), mDateSetListener,
                                         mYear, mMonth, mDay);
mDatePickerDialog.setCancelable(true);
mDatePickerDialog.setTitle("Pick a date");
mDatePickerDialog.show();
//showDialog(DATE_DIALOG_ID);

Full source code is as below.

package edu.kettering.datepickerdemoactivity;

import java.util.Calendar;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.TextView;

public class DatePickerDemoActivity extends Activity {
  private TextView mDateDisplay;
  private Button mPickDate;
  private int mYear;
  private int mMonth;
  private int mDay;
  private DatePickerDialog mDatePickerDialog;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_date_picker_demo);

    // get the current date
    final Calendar c = Calendar.getInstance();
    mYear = c.get(Calendar.YEAR);
    mMonth = c.get(Calendar.MONTH);
    mDay = c.get(Calendar.DAY_OF_MONTH);

    // get references of views
    mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
    mPickDate = (Button) findViewById(R.id.pickDate);

    // add a click listener to the button
    mPickDate.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
        mDatePickerDialog = new DatePickerDialog(v.getContext(),
                         mDateSetListener, mYear, mMonth, mDay);
        mDatePickerDialog.setCancelable(true);
        mDatePickerDialog.setTitle("Pick a date");
        mDatePickerDialog.show();
        //showDialog(DATE_DIALOG_ID);
      }
    });
    // display the current date in the TextView
    updateDisplay();
  }

  // update the date in the TextView
  private void updateDisplay() {
    mDateDisplay.setText(new StringBuilder()
             .append(mMonth + 1).append("-")
             .append(mDay).append("-")
             .append(mYear).append(" "));
  }

  // define a variable mDataSetListener
  private DatePickerDialog.OnDateSetListener mDateSetListener =
    new DatePickerDialog.OnDateSetListener() {
      public void onDateSet(DatePicker view, int year,
                            int monthOfYear, int dayOfMonth) {
        mYear = year;
        mMonth = monthOfYear;
        mDay = dayOfMonth;
        updateDisplay();
     }
   };
}
Follow

Get every new post delivered to your Inbox.

Join 147 other followers