Saturday, March 12, 2011

MyApp: Calendar Widget for Android 2.2 (Froyo)

I had been looking for a decent home screen widget for the calendar(s) on my Samsung Galaxy S (currently on Froyo). Could not find one that is free, so wrote one.



Features:
* Includes events from all your calendars on the phone (personal, corporate etc., etc).
* Support for recurring events.
* Separate section for today's events and later events.
* Auto refresh in every 2 minutes (No, it doesn't wake up the phone, if sleeping -- so don't worry about battery).
* Occupies 3 (rows) x4 (columns) cells in home screen.
* Shows up to 6 events at a time.

Note:
* Apparently Google has deprecated the Calendar ContentProvider post Froyo. So this widget may not work post Froyo (including 2.2.1, I suppose). I only have a 2.2 phone and the emulators don't support Calendar (deprecated) -- so no idea.
* Tested only on Samsung Galaxy S on Froyo.

When I get a firmware update to Gingerbread to my SGS next month (hopefully!), I shall try to port this to Gingerbread as well, if possible.

Download the .apk installer here or point your phone to this QR code to download directly



Developed and built using Eclipse Indigo on Ubuntu.

Monday, February 21, 2011

Debugging Android apps remotely via Wifi

Android has tonnes of cool features and this is just one of them. Now I don't need to search for my USB cable to start developing/debugging the apps. Also, poor Samsung, my WinXP BSODs whenever I plug out my phone from adb debugging. This was irritating and that's when I came across this useful tweak.

Requirements:
+ You need a root'ed Android phone. (I suppose, to start/stop services..)
+ You need a terminal emulator software on the phone (preferrably). Android market gives it free.
+ You should already have a working debugging setup from your PC to the phone via USB (I mean all those SDK's, tools, ADT plugin installed).

Connect your phone via Wifi into a n/w that provides access to, from your PC.

On the phone: (either via a 'terminal emulator' OR via 'remote ssh' OR via 'adb shell on USB')

$ su
# setprop service.adb.tcp.port 6666
# stop adbd
# start adbd

All set, now the adbd knows it is suppose to listen on TCP instead of USB.

Disconnect the USB if connected.

On the PC: (example on Windows)
C:\>adb devices

if this shows your device connected, run
c:\>adb disconnect

Then,
c:\>adb connect mobile_ip_address:6666
Connected to device mobile_ip_address:6666
[not to mention, you can use any port other than 6666, but use the same in both places.]

c:\>adb devices
[this should list your device.]

All set. Use Eclipse and debug just like you would on a USB connected device.

Note: This change goes off once you restart your phone. You can automate this in a number of ways.. left to you.

Seeing my apps getting deployed wirelessly onto my phone and debugging and watching those variables remotely is really cool!! :)

Sunday, December 19, 2010

Custom device driver for Linksys USB Wifi on WDTV Live

There is lot of specifics involved, so before I go any further, let me clarify the title more. This post is about using the Cisco (Linksys) USB Wifi G Adaptor WUSB54GC on your WDTV Live for network connectivity. If you need to do this on your WDTV live, you would need to have sufficient understanding of Linux and shell scripting. Also, you need to have Wifi network and with appropriate network shares available via SMB or FTP and understand how you configure stuff.

WDTV Live has wireless network support, however it doesn't work on all USB Wifi network adaptors or chipsets. There is a defined list of USB Wifi adaptors that are compatible -- again this depends on which firmware you use. The stock firmware support (from western digital) might be different from the unofficial firmwares like wdlxtv. I use wdlxtv unofficial firmware but that doesn't support my linksys Wifi adaptor as well. I had bought this wifi dongle before WDTV live, so didn't want to buy another one. The result of trying to make this work on my WDTV is this post.

Apparently, the particular linksys adaptor that I'm talking about (take a look here if you want to know what I'm talking about) is based on Ralink Chipset 3070; in fact there are many other wifi dongles that are based on the same chipset. At least the unofficial firmware supports ralink 3070 chipset, however, it doesn't matter unless the device driver was built to support that particular device. ie., the device driver should have been compatible with the given vendor id and device id. This is where my device didn't work in spite of it being 3070 chipset.

I had to then setup a total cross-platform toolchain for WDTV Live by downloading the appropriate kernel source from Western Digital website. WD has provided beautiful support for building stuff for WDTV live. The source code of device driver for Ralink 3070 chipset is available from Ralink's website. I tampered the source a bit to accommodate this Linksys device too (Vendor Id: 1737, Device Id: 0077), cross-compiled the device driver for WDTV Live on my desktop Linux. Viola, my driver got loaded for that device.

This device driver was built for the kernel 'Linux WDTVLIVE 2.6.22.19-19-4'. Make sure this matches your kernel version too (this should be in line with the WDTV Live official firmware version 1.02.21).

Also, if you have this USB dongle, you should see an entry like this when you do 'lsusb' on your WDTV Live:

Bus 001 Device 003: ID 1737:0077 Linksys

INSTRUCTIONS:
1. Download this zip file that contains (rt3070sta.ko, net.mounts, RT2870STA.dat, resolv.conf, wpa_supplicant.conf).
2. Create a folder named 'wireless' in the root of a pendrive and extract the contents to that folder.
3. Move/copy net.mounts from that folder to the root of your pendrive.
4. Edit the files net.mounts, RT2870STA.dat, resolv.conf, wpa_supplicant.conf and customize with your Wifi's SSID and WPA PSK as necessary. You can use wpa_passphrase on a desktop linux to create a encoded PSK and copy that to these files.
5. More customization is required in net.mounts script as per your network to mount the right share on startup. I use ftpfs, as that seems more robust than smbfs on WDTV live.
6. Plug-in this pen drive to your WDTV live along with your Linksys Wifi dongle and reboot, your WDTV Live should join your network and mount your network share seamlessly.

I've this setup successfully working for almost 3 months now. Every time I start my WDTV live it joins the wifi network in around 30-45 seconds and the network shares are instantly available. You also don't have to worry about network interruptions, WDTV Live takes care of remounting the shares if the ftpfs breaks for some reason (note the 'xmount' in the script).

Here is the net.mounts script that I use at home (with specifics removed):
#
# Original Author: Gerald Naveen A (http://geraldnaveen.blogspot.com)
#
# WDTV Live! net.mounts to use my custom built device driver for WUSB54GC
# on WDTV Live firmwares. This script uses the wpa_supplicant to configure
# WPA based authentication.
#
# Refer : http://geraldnaveen.blogspot.com/2010/12/custom-device-driver-for-linksys-usb.html
# for more info.
#
# COPY THIS FILE TO THE ROOT FOLDER OF YOUR USB DRIVE ON WDTV LIVE!
#
# License:
# You are free to modify/distribute/use for commercial/non-commercial/
# personal applications. Any modification to this code needn't be published.
# However, any publication of this code or the derivative of this code, should
# include the original author and this license text.
#
logger -t GERALD "$0 starting..."
if [ -f /tmp/gerald ]; then
# make sure this script doesn't run more than once. Sometimes, net.mounts
# gets called multiple times.
logger -t GERALD "$0 already ran."
else
touch /tmp/gerald
config_tool -c DISABLE_ETHERNET_ON_STANDBY=NO
config_tool -c USB_POWER_OFF=NO

# NOTE: change below line to cp from your USB wireless device
cp /tmp/mnt/64BA-4243/wireless/* /tmp/
cp /tmp/resolv.conf /etc/resolv.conf

WIFI_DEV=ra0
insmod /tmp/rt3070sta.ko
ifconfig $WIFI_DEV up
sleep 5 ; # let the radio come up before scanning
#discover the current channel number
CHANNEL_NUMBER=`iwlist $WIFI_DEV scan | grep YOUR_WIFI_SSID -A 2 | grep Channel | cut -d\( -f 2 | cut -d\ -f 2 | cut -d\) -f 1`
logger -t GERALD "Configuring $WIFI_DEV to Channel $CHANNEL_NUMBER"
iwconfig $WIFI_DEV channel $CHANNEL_NUMBER

wpa_supplicant -i$WIFI_DEV -c/tmp/wpa_supplicant.conf -B
# DHCP client has some issue and is inconsistent.
#udhcpc -i $WIFI_DEV
logger -t GERALD "Configuring IP Address for $WIFI_DEV"
ifconfig $WIFI_DEV STATIC_IP_ADDRESS_OF_WDTV_LIVE netmask 255.255.255.0
route add default gw YOUR_NW_DEFAULT_GW_IP

sleep 15 ; # let the n/w settle before mount
logger -t GERALD "Pinging NAS via LAN..."
ping -c 1 -W 2 NAS_IP_ADDR_HSPEED | grep -q "bytes from"
if [ $? -eq 0 ] ; then
logger -t GERALD "Ping successful for NAS via LAN. Now xmounting..."
xmount "ftp://NAS_IP_ADDR_HSPEED" NAS_1Gbps ftpfs "-o user=movies:password"
logger -t GERALD "xmount done for NAS."
else
# could not reach NAS via high speed n/w.
# try to reach via Wifi.
logger -t GERALD "Pinging NAS via Wifi..."
ping -c 1 -W 2 NAS_IP_ADDR | grep -q "bytes from"
if [ $? -eq 0 ] ; then
logger -t GERALD "Ping successful for NAS via Wifi. Now xmounting..."
xmount "ftp://NAS_IP_ADDR" NAS_Wifi ftpfs "-o user=movies:password"
logger -t GERALD "xmount done for NAS."
fi
fi
fi ; # if already ran
logger -t GERALD "$0 complete."

In case you find issues, check /tmp/messages.txt file on your WDTV Live for the relevant log messages to troubleshoot.

Monday, December 06, 2010

Video cable for Samsung Galaxy S

Disclaimer: Although this should just apply for any other SGS, this has been tested only on SGS Vibrant, India model.

If you didn't know how this cable looks, here it is. Note the 4 terminal stereo plug (stereo plug normally has only 3 terminals).

Although one can build a cable from scratch, it is usually cheaper or same to get a read-made cable like this, instead of trying to build our own cable with just the plugs. Ok, so what is there to be done if I get the ready-made cable -- the reason is that, all cables aren't compatible with SGS video output. This post anyway has all the info to build your own cable, if you are interested.

The common confusion that prevails in the forums is that some cables work and some don't; so how do we buy the right cable. The answer is that, you don't have to worry as long as the terminals are exactly these (ie., 4 pin stereo on one end, RCA on the other end). I had bought one such cable from ebay India for just Rs. 100. You can search for "Camcorder RCA cable" and you might end up on one such item.

If you get a camcorder cable, it most likely won't work. If it doesn't work in the normal configuration (ie., Yellow to Video, Red/White to Audio), try connecting the White to Video, and Yellow/Red to Audio. This should just work.

Why?


This is how a 4-terminal stereo plug looks. Lets name the terminals as T1..T4 as in figure. The camcorder cables send T4 to Video, but the SGS sends out video on T2. On Camcorder cables, T2 is the Left/Mono Audio (White) -- thus swapping that for Video works.

T1 is the Ground. As long as the ground is not changed, you should be able to convert any cable to be compatible with SGS video out. T1 usually gets in contact with the case/body of the device, so I would think it would be always ground on any such cables for consumer devices. I also don't see any reason for a cable to insert the video terminal between the two audio terminals. Going by these rules, it is most likely that, it is either T4 (T2, T3 are audio) or T2 (T3, T4 are audio) which is the video terminal.

If you have a multimeter, just connect this cable to your phone, turn on Video out on settings and measure the DC voltage (0-20V range) on each of the RCA plugs (make sure the phone isn't playing any audio). Only the video plug will sense a voltage of ~1.5V at this point. This should identify the video plug safely without having to try out various plugs on the TV directly.

To summarize, so you can build one if required. The SGS video output pin out is:

T1: Ground
T2: Video
T3: Left/Mono
T4: Right

Monday, November 29, 2010

Getting over problems

Courtesy: Pravs World

If you can't get through the mountain, Go around it. If you can't go around it, Go over it.

If you can't go over it, sit down and ask yourself, if getting to the other side is all that important? If it is, set about digging a tunnel.

For every problem, there are many solutions. Whatever the problems in life are, you have to find ways to get over it.

Saturday, November 13, 2010

MyApp: Spb Wallet to KeePass convertor

Spb Wallet is one of the best wallet applications that I've used across various mobile platforms. However, it isn't available on all mobile platforms and it isn't free either. Once I moved on to Android, I hit a road-block because Spb Wallet isn't available for Android (yet). I'm sure you would understand how painful it is to not have a wallet app, once you are used to.

KeePass is the alternate. KeePass is an open-source software for password management. To be fair, KeePass isn't as great as Spb Wallet, but does its job. Being open-source, it is available on almost all platforms including desktops.

The pain here is the conversion. I have tonnes of data on Spb Wallet that manually entering them on KeePass is a no-go. Unfortunately, and mostly intentionally, Spb Wallet doesn't export to any well known format for import into KeePass. There weren't any handy tools to convert either. Thankfully Spb Wallet had a text export and KeePass had many import options. But it isn't directly compatible, because Spb Wallet doesn't have any proper structure to the exported TXT file and the grammar is quite ambiguous. KeePass has a well defined XML structure for import (found it by doing a sample XML export from KeePass). I wrote this python script to convert the Spb Wallet TXT export file into the XML format that KeePass can understand. In reality, Spb Wallet has more "specific" fields than KeePass, so there isn't always a direct mapping. Any non-mappable field (Account Number for example) will be appended in the notes section of KeePass so no information is lost.

This script is a simple parser that understands and converts the Spb Wallet TXT export file. It maintains the internal state of parsing and learns dynamically about what the current token is -- some times even looking ahead into the file to resolve ambiguities.

This script is probably not so robust on errors. But this did the job for my Wallet export which is reasonably big.

If you find any bug on this script that you want me to fix, report here. I *MAY* fix it for you.

Here is the source:
#
# Code to Convert Spb Wallet TXT export file to KeePass XML import file.
#
# Original Author: Gerald Naveen A (http://geraldnaveen.blogspot.com)
#
# License:
# You are free to modify/distribute/use for commercial/non-commercial/
# personal applications. Any modification to this code needn't be published.
# However, any publication of this code or the derivative of this code, should
# include the original author and this license text.
#
import sys

def mywrite(f, str):
f.write("{0}\n".format(str));
def main():
print "\nSpb Wallet to KeePass Convertor v 1.0 by Gerald Naveen\n";
print "Report bugs at http://geraldnaveen.blogspot.com/2010/11/myapp-spb-wallet-to-keepass-convertor.html\n";
if len(sys.argv) < 3:
print "Usage: spb_wallet_to_keepass.py <spb_txt_export_file> <keepass_xml_import_file>";
print "\nWhere,\nspb_txt_export_file: path to the TXT file exported from Spb Wallet.";
print "keepass_txt_import_file: path to the output XML file, that shall be imported into KeePass.";
return;
try:
ifile = open (sys.argv[1], 'r');
except:
print "Could not open input file", sys.argv[1];
return;

try:
ofile = open (sys.argv[2], 'w');
except:
print "Could not open output file", sys.argv[2];
return;

FOLDER_NAME_TOKEN=1;
ENTRY_NAME_TOKEN=FOLDER_NAME_TOKEN+1;
BEFORE_NOTES_TOKEN=ENTRY_NAME_TOKEN+1;
NOTES_TOKEN=BEFORE_NOTES_TOKEN+1;
INVALID_VALUE='invalid';

next_token=ENTRY_NAME_TOKEN;
folder_name = INVALID_VALUE;
entry_name = INVALID_VALUE;
user_name = INVALID_VALUE;
password = INVALID_VALUE;
url = INVALID_VALUE;
notes = INVALID_VALUE;
valid_entry = False;

mywrite(ofile, '<?xml version="1.0" encoding="utf-8" standalone="yes"?>');
mywrite(ofile, '<pwlist>');
try:
for line in ifile:
line = line.strip('\r\n');
if len(line) == 0:
# empty line
if valid_entry == False:
# entry name found after folder name
folder_name = entry_name;
entry_name = INVALID_VALUE;
else:
# found the last line of the entry..dump
mywrite(ofile, '<pwentry>');
if folder_name != INVALID_VALUE:
mywrite(ofile, '<group>{0}</group>'.format(folder_name));
mywrite(ofile, '<title>{0}</title>'.format(entry_name));
if user_name != INVALID_VALUE:
mywrite(ofile, '<username>{0}</username>'.format(user_name));
if password != INVALID_VALUE:
mywrite(ofile, '<password>{0}</password>'.format(password));
if url != INVALID_VALUE:
mywrite(ofile, '<url>{0}</url>'.format(url));
if notes != INVALID_VALUE:
notes=notes.replace('\n', '&#xD;&#xA;');
mywrite(ofile, '<notes>{0}</notes>'.format(notes));
mywrite(ofile, '</pwentry>');
user_name = INVALID_VALUE;
password = INVALID_VALUE;
url = INVALID_VALUE;
notes = INVALID_VALUE;
valid_entry = False;
next_token=ENTRY_NAME_TOKEN;
else:
if next_token == ENTRY_NAME_TOKEN:
entry_name = line;
next_token = BEFORE_NOTES_TOKEN;
else:
valid_entry = True;
if next_token == BEFORE_NOTES_TOKEN:
if line.startswith('User Name:'):
user_name = line[len('User Name:'):].strip(' ');
elif line.startswith('Password:'):
password = line[len('Password:'):].strip(' ');
elif line.startswith('Web Site:'):
url = line[len('Web Site:'):].strip(' ');
elif line.startswith('Notes:'):
if notes == INVALID_VALUE:
notes = line[len('Notes:'):].strip(' ');
else:
notes += '\n' + line[len('Notes:'):].strip(' ');
next_token = NOTES_TOKEN;
else:
# any unknown params should go as notes.
if notes == INVALID_VALUE:
notes = line;
else:
notes += '\n' + line;
elif next_token == NOTES_TOKEN:
# any thing from the notes section.
notes += '\n' + line;
except:
print "Unknown error occured while processing the input file.";
mywrite(ofile, '</pwlist>');
ifile.close();
ofile.close();
print "Success. Now import {0} in KeePass as KeePass XML".format(sys.argv[2]);
if __name__ == "__main__":
main()
Download spb_wallet_to_keepass.py

Update [26-Nov-2010]:

If the script ran successfully, but the output XML file didn't work on import, it could most likely be a CRLF issue. Try this in such cases:

Lets assume your file is test.txt.

1. Open test.txt in Notepad
2. "Save as" another file, say test2.txt (Note: select Utf8 as Encoding).
3. Use test2.txt as input

Monday, October 11, 2010

Asus RT-N16 -- my new home Wifi router

This is a long pending post. I had bought this router for my home a month back and never got to write about it.

Picture first:


I had been looking for a Wifi router at my home before I could narrow down on this. Here were my requirements:
  1. Good range: The current wifi router that came with my Internet connection can barely cover 25% of my house. I needed something that can cover every corner of my duplex flat. Otherwise the exercise is of little use.
  2. Thirdparty-firmware: I was mainly eying at dd-wrt for the firmware. It *has* to be dd-wrt compatible so I can play around with the firmware and add services to it. dd-wrt has multiple versions of the firmware based on the flash size available. Most routers these days have <=4MB flash (some of them only 2MB). Those will need to run mini or micro versions of dd-wrt which have limited capabilities and extensions. I was looking at a router with at least 8MB flash, so the mega version of dd-wrt fits.
  3. 802.11n: I wanted 802.11n draft support. I was actually more inclined towards simultaneous dual-band vs just dual-band. Also, I was fine with 2.4GHz 'n' if 5.8GHz 'n' isn't available.
  4. USB mass storage: I wanted the router to also be my NAS. So I can just attach a USB HDD to the router and can access it anywhere else in the house. Currently my EEEBox does this for me, but that's one more device to run.
Selecting a good range was the difficult task because there is no benchmark that fits all for this. When some one says *good* it is relative and not absolute. That doesn't help me in anyways to select mine. As it was an important factor me, I had to overshoot my expectation and get a really good one.

Eventually, I narrowed down on 2 routers:
  1. Cisco Linksys WRT610N
  2. Asus RT-N16
Both seem to claim very good range. WRT610N has 3 internal antennas, whereas the RT-N16 has 3 external antennas. Anytime, the external ones are more powerful. The Cisco one looks really cool though. No, but range was more important. Both are of similar price range (~10K INR). Both have USB support. Both are supported by dd-wrt. The biggest differentiating factor between these two that helped me decide, is the hardware. RT-N16 has an awesome processor at 533MHz, 128MB of RAM and more importantly 32MB of flash. The Cisco one has just 8MB flash. 32MB flash is too good that I don't have to worry about space while enhancing the router for other tasks. The powerful processor should also come in handy to support multiple operations in parallel. There are more comparisons on the specifications, but that's too much to be here.

Went ahead and bought RT-N16. Even though I didn't intend to use the official firmware, the Asus official firmware for RT-N16 is pretty good (except for the NAS part). In fact the official firmware supports 1TB disks and NTFS file system. Even the dd-wrt doesn't have proper support for NTFS (yes, ntfs-3g driver is available, but is still error-prone on large partitions, that I had to move to ext3 for my NAS). Any time a journal'ed file system like ext3 is better for NAS. Anyways, in 2 days, I upgraded the firmware to the unofficial dd-wrt. It's very handy, specially as it exposes the whole Linux underneath. The extra flash memory can be mounted via JFFS2 file system and that's the place to make wonders.

This router has served all my purposes. It has terrific range that my earlier mobile (ASUS P320) which is the weakest Wifi client I have, connects from the farthest point within the house. Before I could wink, I already have 8 wifi devices at home -- enjoying the n/w all around my house. It's an useful luxury!