Show me the code! – By Davanum Srinivas

December 12, 2007

Android – Call/Dial from the adb shell command line

Filed under: Uncategorized — Davanum Srinivas @ 2:43 pm

Check if the “phone” service is up and running

C:\android\DialPhone>adb shell
# service check phone
service check phone
Service phone: found

Dial a specific contact in the contact list

# service call phone 1 s16 "davanum"
service call phone 1 s16 "davanum"
Result: Parcel(NULL)

1

Dial a specific phone #

# service call phone 2 s16 "+15084157509"
service call phone 2 s16 "+15084157509"
Result: Parcel(NULL)

1

Home work – try these and leave me a comment on what it affects in the emulator

# service call phone 8 i32 0
service call phone 8 i32 0
Result: Parcel(NULL)
# service call phone 8 i32 1
service call phone 8 i32 1
Result: Parcel(NULL)

Other options

# service
service
Usage: service [-h|-?]
       service check SERVICE
       service call SERVICE CODE [i32 INT | s16 STR] ...
Options:
   i32: Write the integer INT into the send parcel.
   s16: Write the UTF-16 string STR into the send parcel.

December 11, 2007

Android – How to poke around the sqlite3 databases

Filed under: Uncategorized — Davanum Srinivas @ 10:00 pm

Find and connect to a database

You can find busybox on Benno’s site. If you can find the location of the .db file, you don’t really need busybox. Though i highly recommend it since it has many useful utilities.

C:\android>adb shell
# export PATH=/data/busybox:$PATH
export PATH=/data/busybox:$PATH
# find data -name "*.db" -print
find data -name "*.db" -print
data/data/com.google.android.providers.contacts/databases/contacts.db
data/data/com.google.android.providers.googleapps/databases/accounts.db
data/data/com.google.android.providers.im/databases/im.db
data/data/com.google.android.providers.media/databases/media.db
data/data/com.google.android.providers.telephony/databases/mms.db
data/data/com.google.android.providers.telephony/databases/sms.db
data/data/com.google.android.providers.telephony/databases/telephony.db
data/data/com.google.android.providers.settings/databases/settings.db
data/data/com.google.android.maps/databases/maps.db
# sqlite3 data/data/com.google.android.providers.contacts/databases/contacts.db
sqlite3 data/data/com.google.android.providers.contacts/databases/contacts.db
SQLite version 3.5.0
Enter ".help" for instructions
sqlite>

Find the list of tables and their structure

sqlite> .tables
.tables
_deleted_people  contact_methods  peopleLookup
calls            people           phones
sqlite> .schema people
.schema people
CREATE TABLE people (_id INTEGER PRIMARY KEY,_sync_account TEXT,_sync_id TEXT,_sync_time TEXT,_sync_version TEXT,_sync_local_id INTEGER,_sync_dirty INTEGER,_sync_mark INTEGER,name TEXT NOT NULL,notes TEXT,photo TEXT,company TEXT,title TEXT,preferred_phone INTEGER,preferred_email INTEGER);
CREATE INDEX peopleSyncIdIndex ON people (_sync_id);
CREATE TRIGGER contact_cleanup DELETE ON people BEGIN DELETE FROM peopleLookup WHERE source = old._id;DELETE FROM phones WHERE person = old._id;DELETE FROM contact_methods WHERE person = old._id;UPDATE calls SET person = NULL WHERE person = old._id;END;
CREATE TRIGGER contact_to_deleted DELETE ON people WHEN old._sync_id is not null BEGIN INSERT INTO _deleted_people (_sync_id, _sync_account, _sync_version) VALUES (old._sync_id, old._sync_account, old._sync_version);END;
CREATE TRIGGER peopleLookup_insert AFTER INSERT ON people BEGIN SELECT _TOKENIZE('peopleLookup', new._id, new.name, ' ');END;
CREATE TRIGGER peopleLookup_update UPDATE OF name ON people BEGIN DELETE FROM peopleLookup WHERE source = new._id;SELECT _TOKENIZE('peopleLookup', new._id, new.name, ' ');END;
sqlite> .schema phones
.schema phones
CREATE TABLE phones (_id INTEGER PRIMARY KEY,person INTEGER,type INTEGER,number TEXT,number_key TEXT,label TEXT);
CREATE INDEX phonesIndex1 ON phones (person);
CREATE INDEX phonesIndex2 ON phones (number_key);
CREATE TRIGGER phones_delete DELETE ON phones BEGIN UPDATE people SET _sync_dirty=1 WHERE people._id=old.person;END;
CREATE TRIGGER phones_insert INSERT ON phones BEGIN UPDATE people SET _sync_dirty=1 WHERE people._id=new.person;END;
CREATE TRIGGER phones_update UPDATE ON phones BEGIN UPDATE people SET _sync_dirty=1 WHERE people._id=old.person;END;
CREATE TRIGGER preferred_phone_cleanup DELETE ON phones BEGIN UPDATE people SET preferred_phone = NULL WHERE preferred_phone = old._id; END;

Print some of the information from the tables

sqlite> .header on
.header on
sqlite> .mode column
.mode column
sqlite> select * from phones;
select * from phones;
_id         person      type        number        number_key    label
----------  ----------  ----------  ------------  ------------  ----------
1           1           1           +15085551212  21215558051+
2           1           0           +17815551212  21215551871+
3           1           2           +16175551212  21215557161+
sqlite> select * from people;
select * from people;
_id         _sync_account  _sync_id    _sync_time  _sync_version  _sync_local_id  _sync_dirty  _sync_mark  name              notes       photo       company     title       preferred_phone  preferred_email
----------  -------------  ----------  ----------  -------------  --------------  -----------  ----------  ----------------  ----------  ----------  ----------  ----------  ---------------  ---------------
1                                                                                 1                        Davanum Srinivas                                                  1

Other commands

sqlite> .help
.help
.bail ON|OFF           Stop after hitting an error.  Default OFF
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in an SQL text format
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.import FILE TABLE     Import data from FILE into TABLE
.indices TABLE         Show names of all indices on TABLE
.load FILE ?ENTRY?     Load an extension library
.mode MODE ?TABLE?     Set output mode where MODE is one of:
                         csv      Comma-separated values
                         column   Left-aligned columns.  (See .width)
                         html     HTML  code
                         insert   SQL insert statements for TABLE
                         line     One value per line
                         list     Values delimited by .separator string
                         tabs     Tab-separated values
                         tcl      TCL list elements
.nullvalue STRING      Print STRING in place of NULL values
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.schema ?TABLE?        Show the CREATE statements
.separator STRING      Change separator used by output mode and .import
.show                  Show the current values for various settings
.tables ?PATTERN?      List names of tables matching a LIKE pattern
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM NUM ...     Set column widths for "column" mode

Notes


More info in the original posts from Jason and Luisa.

December 9, 2007

Android – Invoke JNI based methods (Bridging C/C++ and Java)

Filed under: Uncategorized — Davanum Srinivas @ 11:23 pm

Here’s a screen shot

1

Step By Step Instructions below:


Step #1: Compile the project using ant. Some snippets are shown below.

NativeAdd.java class (with Native method)

package org.apache;

import android.util.Log;

public class NativeAdd {
    static {
        try {
            Log.i("JNI", "Trying to load libNativeAdd.so");
            System.loadLibrary("NativeAdd");
        }
        catch (UnsatisfiedLinkError ule) {
            Log.e("JNI", "WARNING: Could not load libNativeAdd.so");
        }
    }

    public static native long add(long a, long b);
}

Snippet that shows how this class/method is invoked from the main activity

    public void onClick(View view) {
        Log.i(LOG_TAG, "onClick");

        EditText a = (EditText) findViewById(R.id.a);
        EditText b = (EditText) findViewById(R.id.b);
        EditText c = (EditText) findViewById(R.id.c);
        Log.i(LOG_TAG, "calling native method");
        long sum = NativeAdd.add(Long.parseLong(a.getText().toString()),
                Long.parseLong(b.getText().toString()));
        Log.i(LOG_TAG, "back from native method");
        String text = Long.toString(sum);
        c.setText("Native add returns = " + text.subSequence(0, text.length()));
    }

Step #2: Generate header files using java by running the following command.

javah -classpath ../../android.jar;../bin/classes; org.apache.NativeAdd

Here’s how the header file (org_apache_CallNative.h) looks like

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class org_apache_NativeAdd */

#ifndef _Included_org_apache_NativeAdd
#define _Included_org_apache_NativeAdd
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     org_apache_NativeAdd
 * Method:    add
 * Signature: (JJ)J
 */
JNIEXPORT jlong JNICALL Java_org_apache_NativeAdd_add
  (JNIEnv *, jclass, jlong, jlong);

#ifdef __cplusplus
}
#endif
#endif

Step #3: Code a tiny C file (org_apache_NativeAdd.c) as shown below:

#include "org_apache_NativeAdd.h"

JNIEXPORT jlong JNICALL Java_org_apache_NativeAdd_add
  (JNIEnv *env, jclass c, jlong a, jlong b)
{
    return a + b;
}

Step #4: Please read the following article before you try the next step.
Shared library “Hello World!” for Android

Step #5: Compile and link the org_apache_NativeAdd.c/org_apache_NativeAdd.h into a shared library.

arm-none-linux-gnueabi-gcc  -I/usr/lib/jvm/java-1.5.0-sun/include -I/usr/lib/jvm/java-1.5.0-sun/include/linux  -fpic -c org_apache_NativeAdd.c
arm-none-linux-gnueabi-ld -T armelf_linux_eabi.xsc -shared -o libNativeAdd.so org_apache_NativeAdd.o

NOTE: details on armelf_linux_eabi.xsc are in the Step #4.

Step #6: copy the library to the emulator. Copy the Application as well.
adb push native/libNativeAdd.so /system/lib
adb install bin/CallNative.apk

Step #7: Run the app to try some values. That's it!

Download the sources and Application - CallNative.zip

Android – Reverse GeoCode – find information about a specified lat/long using MapPoint Web Service

Filed under: Uncategorized — Davanum Srinivas @ 2:55 pm

Here’s a sample on how to use Microsoft’s MapPoint SOAP API to get information on a specified latitude/longitude

1

Just enter your Mappoint userid, password, latitude, longitude and press lookup button.

Notes


Here’s the application:

package org.apache.geocode;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthPolicy;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.PostMethod;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;
import java.util.List;

/**
 * Uses the MapPoint Web Service to get details (of nearby locations/addresses) on a specified lat/long
 */
public class ReverseGeoCoder extends Activity implements View.OnClickListener {

    // UI elements
    private Button mLookup;
    private EditText mUserId;
    private EditText mPassword;
    private EditText mLatitude;
    private EditText mLongitude;

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

        // Gather the troops
        mUserId = (EditText) findViewById(R.id.username);
        mPassword = (EditText) findViewById(R.id.password);
        mLatitude = (EditText) findViewById(R.id.latitude);
        mLongitude = (EditText) findViewById(R.id.longitude);
        mLookup = (Button) findViewById(R.id.lookup);

        // Set up a couple of button listeners
        mLookup.setOnClickListener(this);
    }

    // SOAP Request for the FindServiceSOAP.GetLocationInfo web service
    String soapRequestXML_1 = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
            "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\" " +
            "               xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
            "               xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n" +
            "  <soap:Body>\n" +
            "    <GetLocationInfo xmlns=\"http://s.mappoint.net/mappoint-30/\">\n" +
            "      <location>";
    String soapRequestXML_2 = "      </location>\n" +
            "      <dataSourceName>MapPoint.NA</dataSourceName>\n" +
            "      <options/>\n" +
            "    </GetLocationInfo>\n" +
            "  </soap:Body>\n" +
            "</soap:Envelope>";

    /**
     * When the user clicks on the lookup button we use the MapPoint SOAP API to fetch the
     * information of the specified latitude longitude.
     *
     * @param view
     */
    public void onClick(View view) {

        String user = mUserId.getText().toString();
        String password = mPassword.getText().toString();
        String latitude = mLatitude.getText().toString();
        String longitude = mLongitude.getText().toString();

        // Use our customized Digest Auth scheme as per the following article
        // http://go.mappoint.net/MappointMPAC/default.aspx?main=article.aspx&id=J10002
        AuthPolicy.registerAuthScheme(AuthPolicy.DIGEST, DigestMapPointScheme.class);

        String url = "http://findv3.staging.mappoint.net/Find-30/FindService.asmx";
        HttpClient client = new HttpClient();
        client.setConnectionTimeout(8000);

        Credentials credentials = new UsernamePasswordCredentials(user, password);
        client.getState().setCredentials(AuthScope.ANY, credentials);

        PostMethod postMethod = new PostMethod(url);

        // Construct a SOAP request by hand
        StringBuffer request = new StringBuffer();
        request.append(soapRequestXML_1);
        request.append("<Latitude>").append(latitude).append("</Latitude>");
        request.append("<Longitude>").append(longitude).append("</Longitude>");
        request.append(soapRequestXML_2);
        postMethod.setRequestBody(request.toString());
        postMethod.setRequestHeader("Content-Type",
                "text/xml; charset=utf-8");
        postMethod.setRequestHeader("SOAPAction",
                "\"http://s.mappoint.net/mappoint-30/GetLocationInfo\"");

        int statusCode = 0;
        try {
            statusCode = client.executeMethod(postMethod);
        } catch (IOException e) {
            Log.d("ReverseGeoCoder", e.toString(), e);
        }

        Log.i("ReverseGeoCoder", "statusCode>>>" + statusCode);
        Log.i("ReverseGeoCoder", "statusLine>>>" + postMethod.getStatusLine());

        // Parse the SOAP Response
        MyContentHandler myContentHandler = new MyContentHandler();
        try {
            SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
            parser.parse(postMethod.getResponseBodyAsStream(), myContentHandler);
        } catch (Exception e) {
            Log.d("ReverseGeoCoder", e.toString(), e);
        }

        // Display the response details.
        List list = myContentHandler.getLocations();
        String[] items = new String[list.size()];
        for (int i = 0; i < list.size(); i++) {
            MyContentHandler.MyLocation location = (MyContentHandler.MyLocation) list.get(i);
            Log.i("ReverseGeoCoder", "Location : " + i);
            Log.i("ReverseGeoCoder", "Latitude : " + location.latitude);
            Log.i("ReverseGeoCoder", "Longitude : " + location.longitude);
            Log.i("ReverseGeoCoder", "Description : " + location.description);
            items[i] = location.description + " (geo:" + location.latitude + "," + location.longitude + ")";
        }
        // Show the data in the list view
        ListView listView = (ListView) findViewById(R.id.data);
        listView.setAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1,
                items));
        postMethod.releaseConnection();
    }
}

Download the sources and Application – ReverseGeoCode.zip

December 5, 2007

Android – Use ATOM/GData API to access the Google Calendar

Filed under: Uncategorized — Davanum Srinivas @ 11:15 pm

Here’s a sample on how to use access Google Calendar from Android


1

Just enter your userid (with @gmail.com), password, select any date and press lookup button.

Here’s the meat of the application:

import com.google.android.gdata.client.AndroidXmlParserFactory;
import com.google.android.gdata.client.JakartaGDataClient;
import com.google.wireless.gdata.calendar.client.CalendarClient;
import com.google.wireless.gdata.calendar.data.EventEntry;
import com.google.wireless.gdata.calendar.data.When;
import com.google.wireless.gdata.calendar.parser.xml.XmlCalendarGDataParserFactory;
import com.google.wireless.gdata.client.QueryParams;
import com.google.wireless.gdata.data.Feed;
import com.google.wireless.gdata.parser.GDataParser;
    /**
     * When the user clicks on the lookup button we use the ATOM/GData API to fetch the
     * details of events for a specific date
     * @param view
     */
    public void onClick(View view) {
        XmlCalendarGDataParserFactory factory = new XmlCalendarGDataParserFactory(new AndroidXmlParserFactory());
        JakartaGDataClient dataClient = new JakartaGDataClient();

        // Generate the URL for a private ATOM feed in google calendar 
        String url = "http://www.google.com/calendar/feeds/" +
                dataClient.encodeUri(mUserId.getText().toString()) + "/private/full";
        QueryParams params = dataClient.createQueryParams();
        String pad1 = (mMonth + 1) < 10 ? "0" : "";
        String pad2 = mDay < 10 ? "0" : "";
        params.setParamValue("start-min", mYear + "-" + pad1 + (mMonth + 1) + "-" + pad2 + mDay + "T00:00:00");
        params.setParamValue("start-max", mYear + "-" + pad1 + (mMonth + 1) + "-" + pad2 + mDay + "T23:59:59");
        url = params.generateQueryUrl(url);
        Log.i("GoogleContacts", "URL :" + url);

        CalendarClient client = new CalendarClient(dataClient, factory, url);
        try {
            Log.i("GoogleContacts", "BaseFeedUrl:" + client.getBaseFeedUrl());

            String user = mUserId.getText().toString();
            String password = mPassword.getText().toString();
            Log.i("GoogleContacts", "userid:" + user);
            Log.i("GoogleContacts", "password:" + password);

            // Get the google token
            String authToken = client.getAuthToken(user, password);
            Log.i("GoogleContacts", "Token:" + authToken);

            // Use the token and access the actual feed.
            java.io.InputStream is = dataClient.getFeedAsStream(url, authToken);
            GDataParser parser = factory.createParser(is);
            Feed feed = parser.init();

            int totalResults = feed.getTotalResults();
            Log.i("GoogleContacts", "Results:" + totalResults);

            // wade thru the entries and pick interesting information
            EventEntry entry = null;
            String[] items = new String[totalResults];
            for (int i = 0; i < totalResults; i++) {
                entry = (EventEntry) parser.readNextEntry(entry);
                Log.i("GoogleContacts", "Entry ID:" + entry.getId());
                Log.i("GoogleContacts", "Entry Title:" + entry.getTitle());
                Log.i("GoogleContacts", "Update Date:" + entry.getUpdateDate());
                When when = entry.getFirstWhen();
                Date time1 = zulu.parse(when.getStartTime());
                Date time2 = zulu.parse(when.getEndTime());
                Log.i("GoogleContacts", "Date/Time: FROM " + zulu2.format(time1) + " TO " + zulu2.format(time2));
                items[i] = zulu2.format(time1) + " - " + zulu2.format(time2) + " : " + entry.getTitle();
            }

            // Display the information in the list view
            ListView listView = (ListView) findViewById(R.id.data);
            listView.setAdapter(new ArrayAdapter(this,
                    android.R.layout.simple_list_item_1,
                    items));
        } catch (Exception e) {
            Log.e("GoogleContacts", e.toString(), e);
        }
    }

Download the sources and Application – GoogleCalendar.zip

December 4, 2007

Command line Java on DalvikVM

Filed under: Uncategorized — Davanum Srinivas @ 11:21 am

Found this very useful when i was trying out the JNI under android (Short Story – Could not get it to work!).

Step #1: Start with a simple java class

package org.apache;

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

Compile the java class

C:\android\CmdLine>javac -d . -g Helloworld.java

Step #2: Package the generated classes into a temporary jar

C:\android\CmdLine>jar -cvf Temp.jar *
added manifest
adding: Hello.java(in = 0) (out= 0)(stored 0%)
adding: HelloWorld.java(in = 149) (out= 122)(deflated 18%)
adding: org/(in = 0) (out= 0)(stored 0%)
adding: org/apache/(in = 0) (out= 0)(stored 0%)
adding: org/apache/HelloWorld.class(in = 556) (out= 344)(deflated 38%)

Step #3: Use the “dx” tool to generate a classes.dex from our temporary jar.

C:\android\CmdLine>dx --dex --output=c:/android/CmdLine/classes.dex c:/android/CmdLine/Temp.jar

Step #4: Use the “aapt” tool to create a new jar suitable for use with dalvikvm

C:\android\CmdLine>aapt add CmdLine.jar classes.dex
 'classes.dex'...

and push it into a known location on the emulator

C:\android\CmdLine>adb push CmdLine.jar /data
30 KB/s (0 bytes in 481.000s)

Step #5: kick the tires of dalvikvm

C:\android\CmdLine>adb shell
# /system/bin/dalvikvm -Xbootclasspath:/system/framework/core.jar -version
/system/bin/dalvikvm -Xbootclasspath:/system/framework/core.jar -version
DalvikVM version 0.2.0
Copyright (C) 2007 Google, Inc.
Blah blah blah LICENSE blah blah.
Dalvik VM init failed (check log file)

Step #6: Run our code

# /system/bin/dalvikvm -Xbootclasspath:/system/framework/core.jar -classpath /data/CmdLine.jar org.apache.HelloWorld
/system/bin/dalvikvm -Xbootclasspath:/system/framework/core.jar -classpath /data/CmdLine.jar org.apache.HelloWorld
Hello World!

Android Quick Tip – 1 liner to make the VM wait for debugger to connect

Filed under: android — Davanum Srinivas @ 6:46 am

Add this line in your code and when you run the application hits this method, it will wait till you connect with a debugger.


android.os.Debug.waitForDebugger();

Which port you ask? run the “ddms” utility and see for yourself which port your application is waiting on.

December 1, 2007

Android – Much Better Geo Location from just CellID/LAC

Filed under: Uncategorized — Davanum Srinivas @ 12:37 am

Digging a bit deeper!


The previous post that used the ZoneTag and Yahoo GeoCode API’s. Not this one!…Here are 3 screen shots for the following CellID/LAC combinations : (20442/6015, 53361/9800, 32981/6206)

1


2


3


As you may note, This sample has mush finer resolution/location.

Here’s some relevant code snippets:


public void browseTo(int cellid, int lac) throws Exception {
    String url = "http://www.google.com/glm/mmap";
    HttpURL httpURL = new HttpURL(url);
    HostConfiguration host = new HostConfiguration();
    host.setHost(httpURL.getHost(), httpURL.getPort());
    HttpConnection connection = connectionManager.getConnection(host);
    connection.open();

    PostMethod postMethod = new PostMethod(url);
    postMethod.setRequestEntity(new MyRequestEntity(cellid, lac));
    postMethod.execute(new HttpState(), connection);
    InputStream response = postMethod.getResponseBodyAsStream();
    DataInputStream dis = new DataInputStream(response);
    dis.readShort();
    dis.readByte();
    int code = dis.readInt();
    if (code == 0) {
        double lat = (double) dis.readInt() / 1000000D;
        double lng = (double) dis.readInt() / 1000000D;
        dis.readInt();
        dis.readInt();
        dis.readUTF();
        Log.i("LocateMe", "Lat, Long: " + lat + "," + lng);

        /**
         * Start Google Maps and zoom into the lat/long
         */
        ContentURI uri = ContentURI.create("geo:" + lat
                + "," + lng);
        Intent intent = new Intent("android.intent.action.VIEW", uri);
        startActivity(intent);

    } else {
        NotificationManager nm = (NotificationManager)
                getSystemService(NOTIFICATION_SERVICE);
        nm.notifyWithText(100,
                "Could not find lat/long information",
                NotificationManager.LENGTH_SHORT, null);
    }
    connection.close();
}

private static class MyRequestEntity implements RequestEntity {
    int cellId, lac;

    public MyRequestEntity(int cellId, int lac) {
        this.cellId = cellId;
        this.lac = lac;
    }

    public boolean isRepeatable() {
        return true;
    }

    public void writeRequest(OutputStream outputStream) throws 
		IOException {
        DataOutputStream os = new DataOutputStream(outputStream);
        os.writeShort(21);
        os.writeLong(0);
        os.writeUTF("fr");
        os.writeUTF("Sony_Ericsson-K750");
        os.writeUTF("1.3.1");
        os.writeUTF("Web");
        os.writeByte(27);

        os.writeInt(0);
        os.writeInt(0);
        os.writeInt(3);
        os.writeUTF("");
        os.writeInt(cellId);  // CELL-ID
        os.writeInt(lac);     // LAC
        os.writeInt(0);
        os.writeInt(0);
        os.writeInt(0);
        os.writeInt(0);
        os.flush();
    }

    public long getContentLength() {
        return -1;
    }

    public String getContentType() {
        return "application/binary";
    }
}

Download the sources and Application – LocateMe-3.0.zip

November 30, 2007

Android – Poor man’s “My Location” (Show current cell location)

Filed under: Uncategorized — Davanum Srinivas @ 12:28 pm

Many Thanks to Nicholas and bloid for comments on my previous blog post. Here’s an update with some commentary

Step #1: Get the Cell-Id, LAC, MCC and MNC from the cell phone


Set up a PhoneIntentReceiver as shown below:

// Set up the handler for receiving events for service state etc.
mPhoneStateReceiver = new PhoneStateIntentReceiver(this, new ServiceStateHandler());
mPhoneStateReceiver.notifyServiceState(MY_NOTIFICATION_ID);
mPhoneStateReceiver.notifyPhoneCallState(MY_NOTIFICATION_ID);
mPhoneStateReceiver.notifySignalStrength(MY_NOTIFICATION_ID);
mPhoneStateReceiver.registerIntent();

And the handler snippet is below. Note that he MNC/MCC information is returned by the getOperatorNumeric method.

    /**
     * Notification Handler for Phone events
     */
    private class ServiceStateHandler extends Handler {
        private boolean updateOnce = false;

        public void handleMessage(Message msg) {
            Log.i("LocateMe", "In handleMessage : " + msg.what);
            switch (msg.what) {
                case MY_NOTIFICATION_ID:
                    ServiceState state = mPhoneStateReceiver.getServiceState();
                    notification_cid = state.getCid();
                    notification_lac = state.getLac();
                    notification_signal = mPhoneStateReceiver.getSignalStrength();
                    notifiction_operator_numeric = "" + state.getOperatorNumeric();

                    if (updateOnce == false) {
                        updateOnce = true;
                        updateTextFields();
                    }
                    break;
            }
        }
    }

Here’s a screen shot with the default values that you get from the emulator:
001

Since the default information from emulator is unusable, let’s find something we can use say from this site -
“groovy mother…”
Here’s a screen shot:
002

Step #2: Convert the Cell information into an Address


Now the question is how do we get an address information from the cellid/lac/mnc/mcc. The GNUCITIZEN site has a nice tip on how to use the ZoneTag API from Yahoo. So we construct a nice URL with the information entered on screen.
http://zonetag.research.yahooapis.com/services/rest/V1/cellLookup.php?apptoken=ZoneTagDemoToken&cellid=20442&lac=6015&mnc=410&mcc=310

Just Click on the URL above to see the XML that it returns.

Step #3: Convert Address into a Lat/Long


Just use the Yahoo GeoCode API. We parse the XML from the previous step, extract the city, state, zip and construct a new URL.
http://local.yahooapis.com/MapsService/V1/geocode?appid=YahooDemo&city=Boston&state=Massachusetts&zip=02215

Again just Click on the URL above to see the XML that it returns.

Step #4: Launch the Maps

ContentURI uri = ContentURI.create("geo:" + myContentHandler2.getLatitude()
     + "," + myContentHandler2.getLongitude());
Intent intent = new Intent("android.intent.action.VIEW", uri);
startActivity(intent);

003

Piece of cake :)

Download the sources and Application – LocateMe-2.0.zip

UPDATE: December 1, 2007 8 AM EST – Latest version is here

November 29, 2007

Android – Access Cell Phone details (Cell-Id, LAC, Signal Strength)

Filed under: Uncategorized — Davanum Srinivas @ 9:57 am

Here is the screen shot

001

Next Step(s)!


Need to figure out how to use cell-id/lac information to get a lat/long for the phone location! Please drop me a note or add comments here if you have an idea on how to do it. Thanks.

Source

package org.apache;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.PhoneStateIntentReceiver;
import android.telephony.ServiceState;
import android.util.Log;
import android.widget.EditText;

public class LocateMe extends Activity {
    private PhoneStateIntentReceiver mPhoneStateReceiver;
    private EditText mEditLac;
    private EditText mEditCid;
    private EditText mEditSignal;

    private static final int MY_NOTIFICATION_ID = 0x100;

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);

        mPhoneStateReceiver = new PhoneStateIntentReceiver(this, new ServiceStateHandler());
        mPhoneStateReceiver.notifyServiceState(MY_NOTIFICATION_ID);
        mPhoneStateReceiver.notifyPhoneCallState(MY_NOTIFICATION_ID);
        mPhoneStateReceiver.notifySignalStrength(MY_NOTIFICATION_ID);
        mPhoneStateReceiver.registerIntent();

        mEditLac = (EditText) findViewById(R.id.lac);
        mEditCid = (EditText) findViewById(R.id.cell_id);
        mEditSignal = (EditText) findViewById(R.id.signal_strength);
    }

    private class ServiceStateHandler extends Handler {
        public void handleMessage(Message msg) {
            Log.i("LocateMe", "In handleMessage : " + msg.what);
            switch (msg.what) {
                case MY_NOTIFICATION_ID:
                    ServiceState state = mPhoneStateReceiver.getServiceState();
                    int cid = state.getCid();
                    int lac = state.getLac();
                    int signal = mPhoneStateReceiver.getSignalStrength();
                    mEditCid.setText("" + cid);
                    mEditLac.setText("" + lac);
                    mEditSignal.setText("" + signal);
                    break;
            }
        }
    }
}

Download the sources and Application – LocateMe.zip

UPDATE: Nov 30, 12:37 EST – Version 2.0 is here

« Newer PostsOlder Posts »

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 131 other followers